Merge branch 'dev'
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Pages;
|
||||||
|
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class EditProfile extends \Filament\Auth\Pages\EditProfile
|
||||||
|
{
|
||||||
|
public function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
$this->getNameFormComponent(),
|
||||||
|
$this->getEmailFormComponent(),
|
||||||
|
$this->getPasswordFormComponent(),
|
||||||
|
$this->getPasswordConfirmationFormComponent(),
|
||||||
|
$this->getCurrentPasswordFormComponent(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ class ParticipantsRelationManager extends RelationManager
|
|||||||
if (($data['status'] ?? 'hadir') === 'hadir') {
|
if (($data['status'] ?? 'hadir') === 'hadir') {
|
||||||
$activity = $this->getOwnerRecord();
|
$activity = $this->getOwnerRecord();
|
||||||
MemberPoint::firstOrCreate(
|
MemberPoint::firstOrCreate(
|
||||||
['user_id' => $data['recordId'], 'source_type' => 'activity', 'source_id' => $activity->id],
|
['user_id' => $data['recordId'], 'source_type' => \App\Models\Activity::class, 'source_id' => $activity->id],
|
||||||
['points' => 10, 'reason' => "Hadir di kegiatan: {$activity->title}"]
|
['points' => 10, 'reason' => "Hadir di kegiatan: {$activity->title}"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ class ParticipantsRelationManager extends RelationManager
|
|||||||
->after(function (EditAction $action, $record, array $data) {
|
->after(function (EditAction $action, $record, array $data) {
|
||||||
$activity = $this->getOwnerRecord();
|
$activity = $this->getOwnerRecord();
|
||||||
$existing = MemberPoint::where('user_id', $record->id)
|
$existing = MemberPoint::where('user_id', $record->id)
|
||||||
->where('source_type', 'activity')
|
->where('source_type', \App\Models\Activity::class)
|
||||||
->where('source_id', $activity->id)
|
->where('source_id', $activity->id)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class ParticipantsRelationManager extends RelationManager
|
|||||||
'user_id' => $record->id,
|
'user_id' => $record->id,
|
||||||
'points' => 10,
|
'points' => 10,
|
||||||
'reason' => "Hadir di kegiatan: {$activity->title}",
|
'reason' => "Hadir di kegiatan: {$activity->title}",
|
||||||
'source_type' => 'activity',
|
'source_type' => \App\Models\Activity::class,
|
||||||
'source_id' => $activity->id,
|
'source_id' => $activity->id,
|
||||||
]);
|
]);
|
||||||
} elseif (($data['status'] ?? 'hadir') !== 'hadir' && $existing) {
|
} elseif (($data['status'] ?? 'hadir') !== 'hadir' && $existing) {
|
||||||
@@ -90,7 +90,7 @@ class ParticipantsRelationManager extends RelationManager
|
|||||||
->after(function ($record) {
|
->after(function ($record) {
|
||||||
$activity = $this->getOwnerRecord();
|
$activity = $this->getOwnerRecord();
|
||||||
MemberPoint::where('user_id', $record->id)
|
MemberPoint::where('user_id', $record->id)
|
||||||
->where('source_type', 'activity')
|
->where('source_type', \App\Models\Activity::class)
|
||||||
->where('source_id', $activity->id)
|
->where('source_id', $activity->id)
|
||||||
->delete();
|
->delete();
|
||||||
}),
|
}),
|
||||||
@@ -100,7 +100,7 @@ class ParticipantsRelationManager extends RelationManager
|
|||||||
DetachBulkAction::make()
|
DetachBulkAction::make()
|
||||||
->after(function ($records) {
|
->after(function ($records) {
|
||||||
$activity = $this->getOwnerRecord();
|
$activity = $this->getOwnerRecord();
|
||||||
MemberPoint::where('source_type', 'activity')
|
MemberPoint::where('source_type', \App\Models\Activity::class)
|
||||||
->where('source_id', $activity->id)
|
->where('source_id', $activity->id)
|
||||||
->whereIn('user_id', $records->pluck('id'))
|
->whereIn('user_id', $records->pluck('id'))
|
||||||
->delete();
|
->delete();
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class ActivityForm
|
|||||||
Textarea::make('description')->label('Deskripsi')->rows(3)->columnSpanFull(),
|
Textarea::make('description')->label('Deskripsi')->rows(3)->columnSpanFull(),
|
||||||
DatePicker::make('start_date')->label('Mulai')->required(),
|
DatePicker::make('start_date')->label('Mulai')->required(),
|
||||||
DatePicker::make('end_date')->label('Selesai')->required(),
|
DatePicker::make('end_date')->label('Selesai')->required(),
|
||||||
|
TextInput::make('budget')->label('Estimasi Budget (Rp)')->numeric()
|
||||||
|
->helperText('Kosongkan jika tidak ada budget. < Rp500.000: langsung | Rp500.000–2.000.000: approval ketua | > Rp2.000.000: voting'),
|
||||||
DateTimePicker::make('executed_at')->label('Waktu Pelaksanaan')
|
DateTimePicker::make('executed_at')->label('Waktu Pelaksanaan')
|
||||||
->visible(fn ($record) => $record?->status === 'approved'),
|
->visible(fn ($record) => $record?->status === 'approved'),
|
||||||
Textarea::make('execution_notes')->label('Catatan Pelaksanaan')->rows(3)->columnSpanFull()
|
Textarea::make('execution_notes')->label('Catatan Pelaksanaan')->rows(3)->columnSpanFull()
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ class ActivitiesTable
|
|||||||
->icon('heroicon-o-paper-airplane')
|
->icon('heroicon-o-paper-airplane')
|
||||||
->color('info')
|
->color('info')
|
||||||
->requiresConfirmation()
|
->requiresConfirmation()
|
||||||
->visible(fn ($record) => $record->status === 'draft')
|
->visible(fn ($record) => $record->status === 'draft'
|
||||||
|
&& $record->created_by === auth()->id())
|
||||||
->action(fn ($record) => $record->update(['status' => 'pending'])),
|
->action(fn ($record) => $record->update(['status' => 'pending'])),
|
||||||
Action::make('approve')
|
Action::make('approve')
|
||||||
->label('Setujui')
|
->label('Setujui')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Filament\Resources\CashRecords\Schemas;
|
namespace App\Filament\Resources\CashRecords\Schemas;
|
||||||
|
|
||||||
|
use App\Models\Activity;
|
||||||
use App\Models\CashCategory;
|
use App\Models\CashCategory;
|
||||||
use Filament\Forms\Components\DatePicker;
|
use Filament\Forms\Components\DatePicker;
|
||||||
use Filament\Forms\Components\Placeholder;
|
use Filament\Forms\Components\Placeholder;
|
||||||
@@ -27,6 +28,9 @@ class CashRecordForm
|
|||||||
->live(),
|
->live(),
|
||||||
Textarea::make('description')->label('Keterangan')->required()->columnSpanFull(),
|
Textarea::make('description')->label('Keterangan')->required()->columnSpanFull(),
|
||||||
DatePicker::make('date')->label('Tanggal')->required(),
|
DatePicker::make('date')->label('Tanggal')->required(),
|
||||||
|
Select::make('activity_id')->label('Kegiatan Terkait')
|
||||||
|
->options(Activity::whereIn('status', ['approved'])->pluck('title', 'id'))
|
||||||
|
->searchable()->nullable(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Filament\Resources\Divisions\Schemas;
|
namespace App\Filament\Resources\Divisions\Schemas;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
@@ -13,6 +15,12 @@ class DivisionForm
|
|||||||
return $schema->components([
|
return $schema->components([
|
||||||
TextInput::make('name')->label('Nama')->required(),
|
TextInput::make('name')->label('Nama')->required(),
|
||||||
Textarea::make('description')->label('Deskripsi')->rows(3)->columnSpanFull(),
|
Textarea::make('description')->label('Deskripsi')->rows(3)->columnSpanFull(),
|
||||||
|
Select::make('leader_id')->label('Penanggung Jawab')
|
||||||
|
->options(
|
||||||
|
User::role('pengurus')->where('status', 'aktif')->pluck('name', 'id')
|
||||||
|
)
|
||||||
|
->searchable()
|
||||||
|
->nullable(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class DivisionsTable
|
|||||||
->columns([
|
->columns([
|
||||||
TextColumn::make('name')->label('Nama')->searchable()->sortable(),
|
TextColumn::make('name')->label('Nama')->searchable()->sortable(),
|
||||||
TextColumn::make('description')->label('Deskripsi')->limit(50),
|
TextColumn::make('description')->label('Deskripsi')->limit(50),
|
||||||
|
TextColumn::make('leader.name')->label('Penanggung Jawab')->default('-'),
|
||||||
TextColumn::make('members_count')->counts('members')->label('Anggota'),
|
TextColumn::make('members_count')->counts('members')->label('Anggota'),
|
||||||
])
|
])
|
||||||
->recordActions([EditAction::make()])
|
->recordActions([EditAction::make()])
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ class MemberPointResource extends Resource
|
|||||||
protected static string|\UnitEnum|null $navigationGroup = 'Organisasi';
|
protected static string|\UnitEnum|null $navigationGroup = 'Organisasi';
|
||||||
protected static ?string $navigationLabel = 'Poin Anggota';
|
protected static ?string $navigationLabel = 'Poin Anggota';
|
||||||
|
|
||||||
|
public static function canAccess(): bool
|
||||||
|
{
|
||||||
|
return auth()->user()->hasRole('super_admin');
|
||||||
|
}
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
{
|
{
|
||||||
return MemberPointForm::configure($schema);
|
return MemberPointForm::configure($schema);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class PostResource extends Resource
|
|||||||
protected static ?string $model = Post::class;
|
protected static ?string $model = Post::class;
|
||||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-newspaper';
|
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-newspaper';
|
||||||
protected static string|\UnitEnum|null $navigationGroup = 'Blog';
|
protected static string|\UnitEnum|null $navigationGroup = 'Blog';
|
||||||
protected static ?string $navigationLabel = 'Post';
|
protected static ?string $navigationLabel = 'Artikel';
|
||||||
|
|
||||||
// Label dinamis sesuai role
|
// Label dinamis sesuai role
|
||||||
public static function getModelLabel(): string
|
public static function getModelLabel(): string
|
||||||
|
|||||||
@@ -36,7 +36,25 @@ class UserForm
|
|||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
DatePicker::make('last_activity_date')->label('Terakhir Aktif'),
|
DatePicker::make('last_activity_date')->label('Terakhir Aktif'),
|
||||||
Select::make('roles')->relationship('roles', 'name')
|
Select::make('roles')->relationship('roles', 'name')
|
||||||
->multiple()->preload()->label('Role'),
|
->multiple()->preload()->label('Role')
|
||||||
|
->getOptionLabelFromRecordUsing(fn ($record) => $record->name)
|
||||||
|
->afterStateHydrated(function ($component, $state) {
|
||||||
|
if (is_array($state)) {
|
||||||
|
$filtered = array_filter($state, fn ($id) => \Spatie\Permission\Models\Role::find($id)?->name !== 'anggota');
|
||||||
|
$component->state(array_values($filtered));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->options(function () {
|
||||||
|
$user = auth()->user();
|
||||||
|
$query = \Spatie\Permission\Models\Role::query()
|
||||||
|
->whereNotIn('name', ['super_admin', 'panel_user', 'anggota']);
|
||||||
|
|
||||||
|
if (! $user->can('AssignKoordinator')) {
|
||||||
|
$query->where('name', '!=', 'koordinator');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->pluck('name', 'id');
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ class UsersTable
|
|||||||
TextColumn::make('division.name')->label('Divisi')->sortable(),
|
TextColumn::make('division.name')->label('Divisi')->sortable(),
|
||||||
TextColumn::make('status')->badge()
|
TextColumn::make('status')->badge()
|
||||||
->color(fn ($state) => $state === 'aktif' ? 'success' : 'danger'),
|
->color(fn ($state) => $state === 'aktif' ? 'success' : 'danger'),
|
||||||
TextColumn::make('roles.name')->label('Role')->badge(),
|
TextColumn::make('roles.name')->label('Role')->badge()
|
||||||
|
->getStateUsing(fn ($record) => $record->roles->pluck('name')->filter(fn ($r) => $r !== 'anggota')->values())
|
||||||
|
->placeholder('-'),
|
||||||
])
|
])
|
||||||
->filters([
|
->filters([
|
||||||
SelectFilter::make('status')
|
SelectFilter::make('status')
|
||||||
|
|||||||
@@ -63,7 +63,10 @@ class PublicController extends Controller
|
|||||||
return view('public.kontak');
|
return view('public.kontak');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function kontakStore(\Illuminate\Http\Request $request)
|
public function guide()
|
||||||
|
{
|
||||||
|
return view('public.guide');
|
||||||
|
} public function kontakStore(\Illuminate\Http\Request $request)
|
||||||
{
|
{
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'name' => 'required|string|max:100',
|
'name' => 'required|string|max:100',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|||||||
class Activity extends Model
|
class Activity extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'title', 'description', 'start_date', 'end_date',
|
'title', 'description', 'budget', 'start_date', 'end_date',
|
||||||
'created_by', 'status', 'approved_by', 'approved_at',
|
'created_by', 'status', 'approved_by', 'approved_at',
|
||||||
'executed_at', 'execution_notes',
|
'executed_at', 'execution_notes',
|
||||||
];
|
];
|
||||||
@@ -44,4 +44,9 @@ class Activity extends Model
|
|||||||
return $this->belongsToMany(User::class, 'activity_member')
|
return $this->belongsToMany(User::class, 'activity_member')
|
||||||
->withPivot('status', 'notes');
|
->withPivot('status', 'notes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function cashRecords(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(CashRecord::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
class CashRecord extends Model
|
class CashRecord extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'amount', 'category_id', 'description',
|
'amount', 'category_id', 'activity_id', 'description',
|
||||||
'date', 'created_by', 'verified_by', 'verified_at',
|
'date', 'created_by', 'verified_by', 'verified_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -29,6 +29,11 @@ class CashRecord extends Model
|
|||||||
return $this->belongsTo(CashCategory::class, 'category_id');
|
return $this->belongsTo(CashCategory::class, 'category_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function activity(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Activity::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function creator(): BelongsTo
|
public function creator(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class, 'created_by');
|
return $this->belongsTo(User::class, 'created_by');
|
||||||
|
|||||||
@@ -7,10 +7,15 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
|
|
||||||
class Division extends Model
|
class Division extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = ['name', 'description'];
|
protected $fillable = ['name', 'description', 'leader_id'];
|
||||||
|
|
||||||
public function members(): HasMany
|
public function members(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(User::class);
|
return $this->hasMany(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function leader(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'leader_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -67,7 +67,7 @@ class User extends Authenticatable implements FilamentUser
|
|||||||
|
|
||||||
public function canAccessPanel(Panel $panel): bool
|
public function canAccessPanel(Panel $panel): bool
|
||||||
{
|
{
|
||||||
return true;
|
return $this->status === 'aktif';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canImpersonate(): bool
|
public function canImpersonate(): bool
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ class Vote extends Model
|
|||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'title', 'description', 'type',
|
'title', 'description', 'type',
|
||||||
'related_id', 'status', 'deadline', 'created_by',
|
'related_id', 'related_type', 'status', 'deadline', 'created_by',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace App\Observers;
|
|||||||
|
|
||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
use App\Models\ActivityLog;
|
use App\Models\ActivityLog;
|
||||||
|
use App\Models\Approval;
|
||||||
|
use App\Models\Vote;
|
||||||
use App\Services\NotificationService;
|
use App\Services\NotificationService;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@@ -30,13 +32,6 @@ class ActivityObserver
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($new === 'approved' && $activity->wasChanged('executed_at') && empty($activity->execution_notes)) {
|
|
||||||
Notification::make()->title('Catatan pelaksanaan wajib diisi')
|
|
||||||
->danger()->send();
|
|
||||||
$activity->executed_at = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ActivityLog::create([
|
ActivityLog::create([
|
||||||
'user_id' => Auth::id(),
|
'user_id' => Auth::id(),
|
||||||
'action' => 'status_changed',
|
'action' => 'status_changed',
|
||||||
@@ -49,6 +44,33 @@ class ActivityObserver
|
|||||||
NotificationService::toRole('ketua', 'Kegiatan Menunggu Persetujuan',
|
NotificationService::toRole('ketua', 'Kegiatan Menunggu Persetujuan',
|
||||||
"Kegiatan \"{$activity->title}\" diajukan untuk disetujui.", 'warning',
|
"Kegiatan \"{$activity->title}\" diajukan untuk disetujui.", 'warning',
|
||||||
route('filament.admin.resources.activities.edit', $activity));
|
route('filament.admin.resources.activities.edit', $activity));
|
||||||
|
|
||||||
|
// Threshold budget
|
||||||
|
$budget = $activity->budget;
|
||||||
|
if ($budget !== null && $budget >= 500_000 && $budget <= 2_000_000) {
|
||||||
|
Approval::firstOrCreate(
|
||||||
|
['model_type' => Activity::class, 'model_id' => $activity->id],
|
||||||
|
['required_approvals' => 1, 'status' => 'pending']
|
||||||
|
);
|
||||||
|
} elseif ($budget !== null && $budget > 2_000_000) {
|
||||||
|
$exists = Vote::where('related_type', Activity::class)
|
||||||
|
->where('related_id', $activity->id)
|
||||||
|
->where('type', 'finance')
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if (! $exists) {
|
||||||
|
Vote::create([
|
||||||
|
'title' => "Persetujuan Budget Kegiatan: {$activity->title}",
|
||||||
|
'description' => "Budget kegiatan senilai Rp " . number_format($budget, 0, ',', '.') . " memerlukan persetujuan voting.",
|
||||||
|
'type' => 'finance',
|
||||||
|
'related_id' => $activity->id,
|
||||||
|
'related_type' => Activity::class,
|
||||||
|
'status' => 'open',
|
||||||
|
'deadline' => now()->addDays(3),
|
||||||
|
'created_by' => Auth::id() ?? $activity->created_by,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($new, ['approved', 'rejected']) && $activity->creator) {
|
if (in_array($new, ['approved', 'rejected']) && $activity->creator) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class CashRecordObserver
|
|||||||
|
|
||||||
// Threshold: 500rb–2jt → buat approval ketua + notif
|
// Threshold: 500rb–2jt → buat approval ketua + notif
|
||||||
if ($record->amount >= 500_000 && $record->amount <= 2_000_000) {
|
if ($record->amount >= 500_000 && $record->amount <= 2_000_000) {
|
||||||
$approval = Approval::create([
|
Approval::create([
|
||||||
'model_type' => CashRecord::class,
|
'model_type' => CashRecord::class,
|
||||||
'model_id' => $record->id,
|
'model_id' => $record->id,
|
||||||
'required_approvals' => 1,
|
'required_approvals' => 1,
|
||||||
@@ -41,13 +41,14 @@ class CashRecordObserver
|
|||||||
// Threshold: > 2jt → buat voting + notif semua anggota
|
// Threshold: > 2jt → buat voting + notif semua anggota
|
||||||
if ($record->amount > 2_000_000) {
|
if ($record->amount > 2_000_000) {
|
||||||
Vote::create([
|
Vote::create([
|
||||||
'title' => "Persetujuan Transaksi: {$record->description}",
|
'title' => "Persetujuan Transaksi: {$record->description}",
|
||||||
'description' => "Transaksi senilai Rp " . number_format($record->amount, 0, ',', '.') . " memerlukan persetujuan voting.",
|
'description' => "Transaksi senilai Rp " . number_format($record->amount, 0, ',', '.') . " memerlukan persetujuan voting.",
|
||||||
'type' => 'finance',
|
'type' => 'finance',
|
||||||
'related_id' => $record->id,
|
'related_id' => $record->id,
|
||||||
'status' => 'open',
|
'related_type' => CashRecord::class,
|
||||||
'deadline' => now()->addDays(3),
|
'status' => 'open',
|
||||||
'created_by' => Auth::id() ?? $record->created_by,
|
'deadline' => now()->addDays(3),
|
||||||
|
'created_by' => Auth::id() ?? $record->created_by,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
NotificationService::toRole('ketua',
|
NotificationService::toRole('ketua',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class PostObserver
|
|||||||
{
|
{
|
||||||
public function updated(Post $post): void
|
public function updated(Post $post): void
|
||||||
{
|
{
|
||||||
if ($post->wasChanged('status') && $post->status === 'approved') {
|
if ($post->wasChanged('status') && $post->status === 'published') {
|
||||||
MemberPoint::firstOrCreate(
|
MemberPoint::firstOrCreate(
|
||||||
[
|
[
|
||||||
'user_id' => $post->author_id,
|
'user_id' => $post->author_id,
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ class UserObserver
|
|||||||
{
|
{
|
||||||
public function updated(User $user): void
|
public function updated(User $user): void
|
||||||
{
|
{
|
||||||
|
// Pastikan role anggota selalu ada
|
||||||
|
if (! $user->hasRole('anggota')) {
|
||||||
|
$user->assignRole('anggota');
|
||||||
|
}
|
||||||
|
|
||||||
// Log perubahan status anggota
|
// Log perubahan status anggota
|
||||||
if ($user->wasChanged('status')) {
|
if ($user->wasChanged('status')) {
|
||||||
MemberStatusLog::create([
|
MemberStatusLog::create([
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ use App\Observers\CashRecordObserver;
|
|||||||
use App\Observers\PostObserver;
|
use App\Observers\PostObserver;
|
||||||
use App\Observers\UserObserver;
|
use App\Observers\UserObserver;
|
||||||
use App\Observers\VoteObserver;
|
use App\Observers\VoteObserver;
|
||||||
|
use Filament\Support\Facades\FilamentView;
|
||||||
|
use Filament\View\PanelsRenderHook;
|
||||||
|
use Illuminate\Support\HtmlString;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
@@ -23,5 +26,19 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
Activity::observe(ActivityObserver::class);
|
Activity::observe(ActivityObserver::class);
|
||||||
Vote::observe(VoteObserver::class);
|
Vote::observe(VoteObserver::class);
|
||||||
Post::observe(PostObserver::class);
|
Post::observe(PostObserver::class);
|
||||||
|
|
||||||
|
FilamentView::registerRenderHook(
|
||||||
|
PanelsRenderHook::TOPBAR_LOGO_AFTER,
|
||||||
|
fn () => new HtmlString(
|
||||||
|
'<a href="/" target="_blank" title="Website Publik"
|
||||||
|
style="display:flex;align-items:center;color:#9ca3af;margin-left:1rem;padding-left:1rem;border-left:1px solid #9ca3af"
|
||||||
|
onmouseover="this.style.color=\'#4b5563\'" onmouseout="this.style.color=\'#9ca3af\'">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="height:2rem" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||||
|
</svg>
|
||||||
|
</a>'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
|
|||||||
use Filament\Http\Middleware\AuthenticateSession;
|
use Filament\Http\Middleware\AuthenticateSession;
|
||||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||||
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||||
use Filament\Navigation\NavigationItem;
|
|
||||||
use Filament\Pages\Dashboard;
|
use Filament\Pages\Dashboard;
|
||||||
use Filament\Panel;
|
use Filament\Panel;
|
||||||
use Filament\PanelProvider;
|
use Filament\PanelProvider;
|
||||||
@@ -33,6 +32,7 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
->path('dashboard')
|
->path('dashboard')
|
||||||
->viteTheme('resources/css/filament/admin/theme.css')
|
->viteTheme('resources/css/filament/admin/theme.css')
|
||||||
->login()
|
->login()
|
||||||
|
->profile(\App\Filament\Pages\EditProfile::class)
|
||||||
->colors([
|
->colors([
|
||||||
'primary' => Color::Amber,
|
'primary' => Color::Amber,
|
||||||
])
|
])
|
||||||
@@ -51,12 +51,7 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
'Konten',
|
'Konten',
|
||||||
'Organisasi',
|
'Organisasi',
|
||||||
])
|
])
|
||||||
->navigationItems([
|
|
||||||
NavigationItem::make('Website Publik')
|
|
||||||
->url('/', shouldOpenInNewTab: true)
|
|
||||||
->icon('heroicon-o-globe-alt')
|
|
||||||
->sort(99),
|
|
||||||
])
|
|
||||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
|
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
|
||||||
->widgets([
|
->widgets([
|
||||||
AccountWidget::class,
|
AccountWidget::class,
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ class NotificationService
|
|||||||
|
|
||||||
public static function toAll(string $title, string $body, string $color = 'info', ?string $url = null): void
|
public static function toAll(string $title, string $body, string $color = 'info', ?string $url = null): void
|
||||||
{
|
{
|
||||||
self::send(User::all(), $title, $body, $color, $url);
|
self::send(User::where('status', 'aktif')->get(), $title, $body, $color, $url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,8 +233,9 @@ return [
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
'custom_permissions' => [
|
'custom_permissions' => [
|
||||||
'ViewDraft:Activity', // Lihat kegiatan berstatus draft milik user lain (hanya super_admin)
|
'ViewDraft:Activity', // Lihat kegiatan berstatus draft milik user lain (hanya super_admin)
|
||||||
'Publish:Post', // Publish / unpublish artikel (editor)
|
'Publish:Post', // Publish / unpublish artikel (editor)
|
||||||
|
'AssignKoordinator', // Assign/cabut role koordinator ke anggota (hanya ketua)
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?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::table('divisions', function (Blueprint $table) {
|
||||||
|
$table->foreignId('leader_id')->nullable()->constrained('users')->nullOnDelete()->after('description');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('divisions', function (Blueprint $table) {
|
||||||
|
$table->dropForeignIdFor(\App\Models\User::class, 'leader_id');
|
||||||
|
$table->dropColumn('leader_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
+31
@@ -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
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('activities', function (Blueprint $table) {
|
||||||
|
$table->unsignedBigInteger('budget')->nullable()->after('description');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('cash_records', function (Blueprint $table) {
|
||||||
|
$table->foreignId('activity_id')->nullable()->constrained('activities')->nullOnDelete()->after('category_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('cash_records', function (Blueprint $table) {
|
||||||
|
$table->dropForeignIdFor(\App\Models\Activity::class, 'activity_id');
|
||||||
|
$table->dropColumn('activity_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('activities', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('budget');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?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::table('votes', function (Blueprint $table) {
|
||||||
|
$table->string('related_type')->nullable()->after('related_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('votes', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('related_type');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -15,6 +15,7 @@ class DivisionSeeder extends Seeder
|
|||||||
['name' => 'Olahraga', 'description' => 'Bidang olahraga dan kesehatan'],
|
['name' => 'Olahraga', 'description' => 'Bidang olahraga dan kesehatan'],
|
||||||
['name' => 'Seni & Budaya', 'description' => 'Bidang seni dan pelestarian budaya'],
|
['name' => 'Seni & Budaya', 'description' => 'Bidang seni dan pelestarian budaya'],
|
||||||
['name' => 'Lingkungan', 'description' => 'Bidang lingkungan hidup'],
|
['name' => 'Lingkungan', 'description' => 'Bidang lingkungan hidup'],
|
||||||
|
['name' => 'Teknologi Informasi', 'description' => 'Bidang teknologi dan informasi digital'],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($divisions as $division) {
|
foreach ($divisions as $division) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class PermissionSeeder extends Seeder
|
|||||||
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
|
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
|
||||||
|
|
||||||
// Buat roles jika belum ada
|
// Buat roles jika belum ada
|
||||||
foreach (['super_admin', 'ketua', 'bendahara', 'pengurus', 'anggota', 'auditor', 'editor'] as $role) {
|
foreach (['super_admin', 'ketua', 'bendahara', 'pengurus', 'anggota', 'auditor', 'editor', 'koordinator'] as $role) {
|
||||||
Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
|
Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,12 +29,25 @@ class PermissionSeeder extends Seeder
|
|||||||
$anggota = Role::findByName('anggota');
|
$anggota = Role::findByName('anggota');
|
||||||
$auditor = Role::findByName('auditor');
|
$auditor = Role::findByName('auditor');
|
||||||
$editor = Role::findByName('editor');
|
$editor = Role::findByName('editor');
|
||||||
|
$koordinator = Role::findByName('koordinator');
|
||||||
|
|
||||||
$ketua->syncPermissions(Permission::where('name', 'not like', '%Role%')
|
$ketua->syncPermissions(Permission::where('name', 'not like', '%Role%')
|
||||||
->where('name', 'not like', '%Permission%')
|
->where('name', 'not like', '%Permission%')
|
||||||
->where('name', '!=', 'ViewDraft:Activity')
|
->where('name', '!=', 'ViewDraft:Activity')
|
||||||
->get());
|
->get());
|
||||||
|
|
||||||
|
// Pastikan ketua punya AssignKoordinator
|
||||||
|
if ($p = Permission::where('name', 'AssignKoordinator')->first()) {
|
||||||
|
$ketua->givePermissionTo($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
$koordinator->syncPermissions(Permission::whereIn('name', [
|
||||||
|
'ViewAny:Activity', 'View:Activity', 'Create:Activity', 'Update:Activity', 'Delete:Activity',
|
||||||
|
'ViewAny:Vote', 'View:Vote',
|
||||||
|
'ViewAny:Post', 'View:Post', 'Create:Post', 'Update:Post', 'Delete:Post',
|
||||||
|
'ViewAny:MemberPoint', 'View:MemberPoint',
|
||||||
|
])->get());
|
||||||
|
|
||||||
$bendahara->syncPermissions(Permission::where('name', 'like', '%CashRecord%')
|
$bendahara->syncPermissions(Permission::where('name', 'like', '%CashRecord%')
|
||||||
->orWhere('name', 'like', '%CashCategory%')
|
->orWhere('name', 'like', '%CashCategory%')
|
||||||
->orWhere('name', 'like', '%MemberDue%')
|
->orWhere('name', 'like', '%MemberDue%')
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
use App\Models\Division;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
@@ -10,9 +9,9 @@ class UserSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$divisions = Division::pluck('id')->toArray();
|
$divisions = \App\Models\Division::all();
|
||||||
|
|
||||||
// super_admin
|
// super_admin (tanpa divisi)
|
||||||
User::factory()->createOne([
|
User::factory()->createOne([
|
||||||
'name' => 'Super Admin',
|
'name' => 'Super Admin',
|
||||||
'email' => 'admin@admin.com',
|
'email' => 'admin@admin.com',
|
||||||
@@ -21,22 +20,29 @@ class UserSeeder extends Seeder
|
|||||||
'status' => 'aktif',
|
'status' => 'aktif',
|
||||||
])->assignRole('super_admin');
|
])->assignRole('super_admin');
|
||||||
|
|
||||||
// 2 user per role
|
// ketua, bendahara, auditor — tanpa divisi spesifik
|
||||||
foreach (['ketua', 'bendahara', 'pengurus', 'auditor', 'anggota'] as $role) {
|
foreach (['ketua', 'bendahara', 'auditor'] as $role) {
|
||||||
User::factory(2)->create(['division_id' => fake()->randomElement($divisions)])
|
User::factory(2)->create()->each(fn ($u) => $u->assignRole($role));
|
||||||
->each(fn ($user) => $user->assignRole($role));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 editor
|
// 1 editor
|
||||||
User::factory()->createOne([
|
User::factory()->createOne([
|
||||||
'name' => 'Editor Konten',
|
'name' => 'Editor Konten',
|
||||||
'email' => 'editor@persegi.test',
|
'email' => 'editor@persegi.test',
|
||||||
'password' => bcrypt('password'),
|
'password' => bcrypt('password'),
|
||||||
'status' => 'aktif',
|
'status' => 'aktif',
|
||||||
'division_id' => fake()->randomElement($divisions),
|
|
||||||
])->assignRole('editor');
|
])->assignRole('editor');
|
||||||
|
|
||||||
// 2 user tanpa role
|
// Setiap divisi: 1 pengurus (jadi leader) + 3–8 anggota
|
||||||
User::factory(2)->create(['division_id' => fake()->randomElement($divisions)]);
|
foreach ($divisions as $division) {
|
||||||
|
$pengurus = User::factory()->create(['division_id' => $division->id]);
|
||||||
|
$pengurus->assignRole('pengurus');
|
||||||
|
|
||||||
|
$division->update(['leader_id' => $pengurus->id]);
|
||||||
|
|
||||||
|
$count = rand(3, 8);
|
||||||
|
User::factory($count)->create(['division_id' => $division->id])
|
||||||
|
->each(fn ($u) => $u->assignRole('anggota'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
# Panduan Penggunaan Sistem Persegi
|
||||||
|
|
||||||
|
Sistem manajemen internal Organisasi Pemuda Desa Karangdadap.
|
||||||
|
Akses panel: **https://persegi.nyawiji.net/admin**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Daftar Isi
|
||||||
|
|
||||||
|
1. [Login & Akses Panel](#1-login--akses-panel)
|
||||||
|
2. [Dashboard](#2-dashboard)
|
||||||
|
3. [Manajemen Anggota](#3-manajemen-anggota)
|
||||||
|
4. [Divisi](#4-divisi)
|
||||||
|
5. [Kegiatan](#5-kegiatan)
|
||||||
|
6. [Keuangan (Kas)](#6-keuangan-kas)
|
||||||
|
7. [Iuran Anggota](#7-iuran-anggota)
|
||||||
|
8. [Voting](#8-voting)
|
||||||
|
9. [Approval](#9-approval)
|
||||||
|
10. [Audit Internal](#10-audit-internal)
|
||||||
|
11. [Konten & Blog](#11-konten--blog)
|
||||||
|
12. [Poin Anggota](#12-poin-anggota)
|
||||||
|
13. [Notifikasi](#13-notifikasi)
|
||||||
|
14. [Hak Akses per Role](#14-hak-akses-per-role)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Login & Akses Panel
|
||||||
|
|
||||||
|
1. Buka **https://persegi.nyawiji.net/admin**
|
||||||
|
2. Masukkan email dan password yang diberikan pengurus
|
||||||
|
3. Klik **Masuk**
|
||||||
|
|
||||||
|
> Hanya anggota dengan status **aktif** yang bisa login. Jika tidak bisa masuk, hubungi pengurus.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Dashboard
|
||||||
|
|
||||||
|
Setelah login, halaman utama menampilkan:
|
||||||
|
- **Statistik** — jumlah anggota aktif, kegiatan, kas masuk/keluar
|
||||||
|
- **Log Aktivitas** — perubahan terbaru di sistem
|
||||||
|
- **Leaderboard Poin** — 10 anggota dengan poin tertinggi
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Manajemen Anggota
|
||||||
|
|
||||||
|
**Menu:** Organisasi → Anggota
|
||||||
|
|
||||||
|
### Tambah Anggota Baru
|
||||||
|
1. Klik tombol **Tambah Anggota**
|
||||||
|
2. Isi nama, email, nomor telepon, alamat, dan divisi
|
||||||
|
3. Atur status: **Aktif** atau **Nonaktif**
|
||||||
|
4. Klik **Simpan**
|
||||||
|
|
||||||
|
> Anggota baru otomatis mendapat role `anggota` dan bisa login ke panel.
|
||||||
|
|
||||||
|
### Nonaktifkan Anggota
|
||||||
|
1. Buka halaman edit anggota
|
||||||
|
2. Ubah status ke **Nonaktif**
|
||||||
|
3. Isi alasan nonaktif
|
||||||
|
4. Klik **Simpan**
|
||||||
|
|
||||||
|
> Anggota nonaktif tidak bisa login ke panel.
|
||||||
|
|
||||||
|
### Assign Role Tambahan
|
||||||
|
Role bisa ditambahkan di field **Role** saat edit anggota.
|
||||||
|
- `koordinator` — hanya bisa di-assign oleh **ketua**
|
||||||
|
- Role lain (`pengurus`, `bendahara`, dll) — bisa di-assign oleh yang punya akses
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Divisi
|
||||||
|
|
||||||
|
**Menu:** Organisasi → Divisi
|
||||||
|
|
||||||
|
- Tambah, edit, atau hapus divisi
|
||||||
|
- Setiap divisi bisa memiliki **Penanggung Jawab** — dipilih dari anggota dengan role `pengurus`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Kegiatan
|
||||||
|
|
||||||
|
**Menu:** Kegiatan → Kegiatan
|
||||||
|
|
||||||
|
### Alur Status Kegiatan
|
||||||
|
|
||||||
|
```
|
||||||
|
Draft → Pending (diajukan) → Approved / Rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buat Kegiatan Baru (Pengurus / Koordinator)
|
||||||
|
1. Klik **Tambah Kegiatan**
|
||||||
|
2. Isi judul, deskripsi, tanggal mulai & selesai
|
||||||
|
3. Isi **Estimasi Budget** jika ada (opsional)
|
||||||
|
4. Klik **Simpan** — kegiatan tersimpan sebagai **Draft**
|
||||||
|
|
||||||
|
### Ajukan Kegiatan
|
||||||
|
1. Buka kegiatan yang sudah dibuat
|
||||||
|
2. Ubah status ke **Pending**
|
||||||
|
3. Klik **Simpan**
|
||||||
|
|
||||||
|
> Ketua akan mendapat notifikasi untuk menyetujui.
|
||||||
|
> Jika budget ≥ Rp500.000, akan dibuat **Approval** otomatis.
|
||||||
|
> Jika budget > Rp2.000.000, akan dibuat **Voting** otomatis.
|
||||||
|
|
||||||
|
### Setujui / Tolak Kegiatan (Ketua)
|
||||||
|
1. Buka kegiatan dengan status **Pending**
|
||||||
|
2. Ubah status ke **Approved** atau **Rejected**
|
||||||
|
3. Klik **Simpan**
|
||||||
|
|
||||||
|
### Catat Kehadiran Peserta
|
||||||
|
1. Buka kegiatan yang sudah **Approved**
|
||||||
|
2. Buka tab **Kehadiran Peserta**
|
||||||
|
3. Klik **Tambah Peserta** → pilih anggota → atur status kehadiran
|
||||||
|
4. Anggota yang hadir otomatis mendapat **+10 poin**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Keuangan (Kas)
|
||||||
|
|
||||||
|
**Menu:** Keuangan → Transaksi
|
||||||
|
|
||||||
|
### Catat Transaksi Baru (Bendahara)
|
||||||
|
1. Klik **Tambah Transaksi**
|
||||||
|
2. Pilih kategori, isi jumlah, keterangan, dan tanggal
|
||||||
|
3. Pilih **Kegiatan Terkait** jika transaksi untuk kegiatan tertentu (opsional)
|
||||||
|
4. Klik **Simpan**
|
||||||
|
|
||||||
|
### Alur Verifikasi Otomatis
|
||||||
|
|
||||||
|
| Jumlah | Alur |
|
||||||
|
|---|---|
|
||||||
|
| < Rp500.000 | Bisa langsung diverifikasi |
|
||||||
|
| Rp500.000 – Rp2.000.000 | Approval ketua diperlukan |
|
||||||
|
| > Rp2.000.000 | Voting anggota diperlukan |
|
||||||
|
|
||||||
|
### Verifikasi Transaksi (Ketua)
|
||||||
|
1. Buka transaksi yang menunggu verifikasi
|
||||||
|
2. Klik tombol **Verifikasi**
|
||||||
|
|
||||||
|
> Transaksi yang sudah diverifikasi **tidak bisa diubah atau dihapus**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Iuran Anggota
|
||||||
|
|
||||||
|
**Menu:** Organisasi → Iuran Anggota
|
||||||
|
|
||||||
|
- Catat iuran per anggota per periode (format: `YYYY-MM`, contoh: `2026-04`)
|
||||||
|
- Status iuran: **Lunas** atau **Belum Lunas**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Voting
|
||||||
|
|
||||||
|
**Menu:** Keputusan → Voting
|
||||||
|
|
||||||
|
- Voting dibuat otomatis oleh sistem saat ada transaksi atau budget kegiatan > Rp2.000.000
|
||||||
|
- Anggota bisa melihat dan memilih suara di halaman detail voting
|
||||||
|
- Voting otomatis tertutup setelah deadline
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Approval
|
||||||
|
|
||||||
|
**Menu:** Keputusan → Approval
|
||||||
|
|
||||||
|
- Approval dibuat otomatis untuk transaksi atau budget kegiatan Rp500.000–Rp2.000.000
|
||||||
|
- Ketua bisa menyetujui atau menolak di halaman detail approval
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Audit Internal
|
||||||
|
|
||||||
|
**Menu:** Audit → Temuan Audit
|
||||||
|
|
||||||
|
- Auditor bisa membuat temuan audit
|
||||||
|
- Pengurus terkait bisa memberikan respons terhadap temuan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Konten & Blog
|
||||||
|
|
||||||
|
**Menu:** Konten → Post
|
||||||
|
|
||||||
|
### Buat Artikel
|
||||||
|
1. Klik **Tambah Post**
|
||||||
|
2. Isi judul, konten, dan slug
|
||||||
|
3. Simpan sebagai **Draft** atau langsung **Publish**
|
||||||
|
|
||||||
|
> Artikel yang dipublish otomatis memberikan **+5 poin** ke penulis.
|
||||||
|
> Editor bisa me-review dan publish/unpublish artikel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Poin Anggota
|
||||||
|
|
||||||
|
**Menu:** Organisasi → Poin Anggota
|
||||||
|
|
||||||
|
Poin diberikan otomatis:
|
||||||
|
| Aktivitas | Poin |
|
||||||
|
|---|---|
|
||||||
|
| Hadir di kegiatan | +10 |
|
||||||
|
| Artikel dipublish | +5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Notifikasi
|
||||||
|
|
||||||
|
Ikon lonceng di pojok kanan atas menampilkan notifikasi masuk, seperti:
|
||||||
|
- Kegiatan menunggu persetujuan
|
||||||
|
- Status kegiatan diubah
|
||||||
|
- Transaksi butuh approval/voting
|
||||||
|
- Status keanggotaan diubah
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Hak Akses per Role
|
||||||
|
|
||||||
|
| Role | Yang Bisa Dilakukan |
|
||||||
|
|---|---|
|
||||||
|
| `super_admin` | Semua akses |
|
||||||
|
| `ketua` | Approve kegiatan, verifikasi kas, lihat semua data |
|
||||||
|
| `bendahara` | Input kas & iuran |
|
||||||
|
| `pengurus` | Buat & ajukan kegiatan, lihat anggota & divisi |
|
||||||
|
| `koordinator` | Buat & kelola kegiatan milik sendiri (sebelum disetujui) |
|
||||||
|
| `anggota` | Lihat kegiatan, voting, poin, buat artikel |
|
||||||
|
| `auditor` | Lihat semua data + buat temuan audit |
|
||||||
|
| `editor` | Review & publish artikel |
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,282 @@
|
|||||||
|
@extends('public.layout')
|
||||||
|
@section('title', 'Panduan Penggunaan')
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<div class="max-w-4xl mx-auto px-6 py-24">
|
||||||
|
|
||||||
|
<div class="mb-12">
|
||||||
|
<p class="text-xs font-semibold text-gray-400 uppercase tracking-widest mb-4 flex items-center gap-2">
|
||||||
|
<span class="w-4 h-px bg-gray-400 inline-block"></span> Panduan
|
||||||
|
</p>
|
||||||
|
<h1 class="text-5xl font-bold leading-tight mb-4">Panduan<br>Penggunaan</h1>
|
||||||
|
<p class="text-gray-500">Panduan lengkap penggunaan sistem manajemen internal Persegi.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid md:grid-cols-4 gap-8">
|
||||||
|
|
||||||
|
{{-- Sidebar navigasi --}}
|
||||||
|
<aside class="md:col-span-1">
|
||||||
|
<nav class="sticky top-8 space-y-1 text-sm">
|
||||||
|
@foreach([
|
||||||
|
'#login' => 'Login & Akses',
|
||||||
|
'#dashboard' => 'Dashboard',
|
||||||
|
'#anggota' => 'Anggota',
|
||||||
|
'#divisi' => 'Divisi',
|
||||||
|
'#kegiatan' => 'Kegiatan',
|
||||||
|
'#kas' => 'Keuangan',
|
||||||
|
'#iuran' => 'Iuran',
|
||||||
|
'#voting' => 'Voting',
|
||||||
|
'#approval' => 'Approval',
|
||||||
|
'#audit' => 'Audit',
|
||||||
|
'#konten' => 'Konten & Blog',
|
||||||
|
'#poin' => 'Poin',
|
||||||
|
'#notifikasi' => 'Notifikasi',
|
||||||
|
'#role' => 'Hak Akses',
|
||||||
|
] as $href => $label)
|
||||||
|
<a href="{{ $href }}" class="block text-gray-500 hover:text-gray-900 py-1 transition">{{ $label }}</a>
|
||||||
|
@endforeach
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{{-- Konten --}}
|
||||||
|
<main class="md:col-span-3 space-y-16 text-gray-700 leading-relaxed">
|
||||||
|
|
||||||
|
{{-- Login --}}
|
||||||
|
<section id="login">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">1. Login & Akses Panel</h2>
|
||||||
|
<ol class="list-decimal list-inside space-y-2 text-sm">
|
||||||
|
<li>Buka <a href="{{ config('app.url') }}/admin" class="underline text-gray-900" target="_blank">{{ config('app.url') }}/admin</a></li>
|
||||||
|
<li>Masukkan email dan password yang diberikan pengurus</li>
|
||||||
|
<li>Klik <strong>Masuk</strong></li>
|
||||||
|
</ol>
|
||||||
|
<p class="mt-3 text-sm text-gray-500 bg-gray-50 border border-gray-200 rounded-xl px-4 py-3">
|
||||||
|
Hanya anggota dengan status <strong>aktif</strong> yang bisa login. Jika tidak bisa masuk, hubungi pengurus.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Dashboard --}}
|
||||||
|
<section id="dashboard">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">2. Dashboard</h2>
|
||||||
|
<p class="text-sm mb-3">Setelah login, halaman utama menampilkan:</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li><strong>Statistik</strong> — jumlah anggota aktif, kegiatan, kas masuk/keluar</li>
|
||||||
|
<li><strong>Log Aktivitas</strong> — perubahan terbaru di sistem</li>
|
||||||
|
<li><strong>Leaderboard Poin</strong> — 10 anggota dengan poin tertinggi</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Anggota --}}
|
||||||
|
<section id="anggota">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">3. Manajemen Anggota</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Organisasi → Anggota</p>
|
||||||
|
|
||||||
|
<h3 class="font-semibold text-gray-900 mb-2">Tambah Anggota Baru</h3>
|
||||||
|
<ol class="list-decimal list-inside space-y-1 text-sm mb-6">
|
||||||
|
<li>Klik tombol <strong>Tambah Anggota</strong></li>
|
||||||
|
<li>Isi nama, email, nomor telepon, alamat, dan divisi</li>
|
||||||
|
<li>Atur status: <strong>Aktif</strong> atau <strong>Nonaktif</strong></li>
|
||||||
|
<li>Klik <strong>Simpan</strong></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3 class="font-semibold text-gray-900 mb-2">Nonaktifkan Anggota</h3>
|
||||||
|
<ol class="list-decimal list-inside space-y-1 text-sm mb-4">
|
||||||
|
<li>Buka halaman edit anggota</li>
|
||||||
|
<li>Ubah status ke <strong>Nonaktif</strong> dan isi alasan</li>
|
||||||
|
<li>Klik <strong>Simpan</strong></li>
|
||||||
|
</ol>
|
||||||
|
<p class="text-sm text-gray-500 bg-gray-50 border border-gray-200 rounded-xl px-4 py-3">
|
||||||
|
Anggota nonaktif tidak bisa login ke panel.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Divisi --}}
|
||||||
|
<section id="divisi">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">4. Divisi</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Organisasi → Divisi</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li>Tambah, edit, atau hapus divisi</li>
|
||||||
|
<li>Setiap divisi bisa memiliki <strong>Penanggung Jawab</strong> — dipilih dari anggota dengan role pengurus</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Kegiatan --}}
|
||||||
|
<section id="kegiatan">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">5. Kegiatan</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Kegiatan → Kegiatan</p>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2 text-sm mb-6">
|
||||||
|
<span class="bg-gray-100 px-3 py-1 rounded-full">Draft</span>
|
||||||
|
<span class="text-gray-400">→</span>
|
||||||
|
<span class="bg-gray-100 px-3 py-1 rounded-full">Pending</span>
|
||||||
|
<span class="text-gray-400">→</span>
|
||||||
|
<span class="bg-green-100 text-green-700 px-3 py-1 rounded-full">Approved</span>
|
||||||
|
<span class="text-gray-400">/</span>
|
||||||
|
<span class="bg-red-100 text-red-700 px-3 py-1 rounded-full">Rejected</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="font-semibold text-gray-900 mb-2">Buat Kegiatan Baru</h3>
|
||||||
|
<ol class="list-decimal list-inside space-y-1 text-sm mb-6">
|
||||||
|
<li>Klik <strong>Tambah Kegiatan</strong></li>
|
||||||
|
<li>Isi judul, deskripsi, tanggal, dan estimasi budget (opsional)</li>
|
||||||
|
<li>Klik <strong>Simpan</strong> — tersimpan sebagai <strong>Draft</strong></li>
|
||||||
|
<li>Ubah status ke <strong>Pending</strong> untuk mengajukan ke ketua</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p class="text-sm text-gray-500 bg-gray-50 border border-gray-200 rounded-xl px-4 py-3 mb-6">
|
||||||
|
Budget ≥ Rp500.000 → approval ketua otomatis dibuat.<br>
|
||||||
|
Budget > Rp2.000.000 → voting otomatis dibuat.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 class="font-semibold text-gray-900 mb-2">Catat Kehadiran Peserta</h3>
|
||||||
|
<ol class="list-decimal list-inside space-y-1 text-sm">
|
||||||
|
<li>Buka kegiatan yang sudah <strong>Approved</strong></li>
|
||||||
|
<li>Buka tab <strong>Kehadiran Peserta</strong></li>
|
||||||
|
<li>Tambah peserta dan atur status kehadiran</li>
|
||||||
|
<li>Anggota yang hadir otomatis mendapat <strong>+10 poin</strong></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Kas --}}
|
||||||
|
<section id="kas">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">6. Keuangan (Kas)</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Keuangan → Transaksi</p>
|
||||||
|
|
||||||
|
<h3 class="font-semibold text-gray-900 mb-2">Catat Transaksi</h3>
|
||||||
|
<ol class="list-decimal list-inside space-y-1 text-sm mb-6">
|
||||||
|
<li>Klik <strong>Tambah Transaksi</strong></li>
|
||||||
|
<li>Pilih kategori, isi jumlah, keterangan, dan tanggal</li>
|
||||||
|
<li>Pilih kegiatan terkait jika ada (opsional)</li>
|
||||||
|
<li>Klik <strong>Simpan</strong></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-sm border border-gray-200 rounded-xl overflow-hidden">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-500">Jumlah</th>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-500">Alur</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-100">
|
||||||
|
<tr><td class="px-4 py-3">< Rp500.000</td><td class="px-4 py-3">Langsung diverifikasi</td></tr>
|
||||||
|
<tr><td class="px-4 py-3">Rp500.000 – Rp2.000.000</td><td class="px-4 py-3">Perlu approval ketua</td></tr>
|
||||||
|
<tr><td class="px-4 py-3">> Rp2.000.000</td><td class="px-4 py-3">Perlu voting anggota</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Iuran --}}
|
||||||
|
<section id="iuran">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">7. Iuran Anggota</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Organisasi → Iuran Anggota</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li>Catat iuran per anggota per periode (format: <code class="bg-gray-100 px-1 rounded">YYYY-MM</code>, contoh: <code class="bg-gray-100 px-1 rounded">2026-04</code>)</li>
|
||||||
|
<li>Status: <strong>Lunas</strong> atau <strong>Belum Lunas</strong></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Voting --}}
|
||||||
|
<section id="voting">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">8. Voting</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Keputusan → Voting</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li>Voting dibuat otomatis saat transaksi atau budget kegiatan > Rp2.000.000</li>
|
||||||
|
<li>Anggota bisa memilih suara di halaman detail voting</li>
|
||||||
|
<li>Voting tertutup otomatis setelah deadline</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Approval --}}
|
||||||
|
<section id="approval">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">9. Approval</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Keputusan → Approval</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li>Approval dibuat otomatis untuk transaksi atau budget Rp500.000–Rp2.000.000</li>
|
||||||
|
<li>Ketua bisa menyetujui atau menolak di halaman detail approval</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Audit --}}
|
||||||
|
<section id="audit">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">10. Audit Internal</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Audit → Temuan Audit</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li>Auditor bisa membuat temuan audit</li>
|
||||||
|
<li>Pengurus terkait bisa memberikan respons terhadap temuan</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Konten --}}
|
||||||
|
<section id="konten">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">11. Konten & Blog</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Konten → Post</p>
|
||||||
|
<ol class="list-decimal list-inside space-y-1 text-sm mb-4">
|
||||||
|
<li>Klik <strong>Tambah Post</strong></li>
|
||||||
|
<li>Isi judul, konten, dan slug</li>
|
||||||
|
<li>Simpan sebagai <strong>Draft</strong> atau langsung <strong>Publish</strong></li>
|
||||||
|
</ol>
|
||||||
|
<p class="text-sm text-gray-500 bg-gray-50 border border-gray-200 rounded-xl px-4 py-3">
|
||||||
|
Artikel yang dipublish otomatis memberikan <strong>+5 poin</strong> ke penulis.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Poin --}}
|
||||||
|
<section id="poin">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">12. Poin Anggota</h2>
|
||||||
|
<p class="text-xs text-gray-400 uppercase tracking-widest mb-4">Menu: Organisasi → Poin Anggota</p>
|
||||||
|
<table class="w-full text-sm border border-gray-200 rounded-xl overflow-hidden">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-500">Aktivitas</th>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-500">Poin</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-100">
|
||||||
|
<tr><td class="px-4 py-3">Hadir di kegiatan</td><td class="px-4 py-3 font-semibold text-green-600">+10</td></tr>
|
||||||
|
<tr><td class="px-4 py-3">Artikel dipublish</td><td class="px-4 py-3 font-semibold text-green-600">+5</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Notifikasi --}}
|
||||||
|
<section id="notifikasi">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">13. Notifikasi</h2>
|
||||||
|
<p class="text-sm mb-3">Ikon lonceng di pojok kanan atas menampilkan notifikasi masuk:</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||||
|
<li>Kegiatan menunggu persetujuan</li>
|
||||||
|
<li>Status kegiatan diubah</li>
|
||||||
|
<li>Transaksi butuh approval atau voting</li>
|
||||||
|
<li>Status keanggotaan diubah</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{-- Role --}}
|
||||||
|
<section id="role">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">14. Hak Akses per Role</h2>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-sm border border-gray-200 rounded-xl overflow-hidden">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-500">Role</th>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-500">Yang Bisa Dilakukan</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-100 text-sm">
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">ketua</td><td class="px-4 py-3">Approve kegiatan, verifikasi kas, lihat semua data</td></tr>
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">bendahara</td><td class="px-4 py-3">Input kas & iuran</td></tr>
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">pengurus</td><td class="px-4 py-3">Buat & ajukan kegiatan, lihat anggota & divisi</td></tr>
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">koordinator</td><td class="px-4 py-3">Buat & kelola kegiatan milik sendiri (sebelum disetujui)</td></tr>
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">anggota</td><td class="px-4 py-3">Lihat kegiatan, voting, poin, buat artikel</td></tr>
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">auditor</td><td class="px-4 py-3">Lihat semua data + buat temuan audit</td></tr>
|
||||||
|
<tr><td class="px-4 py-3 font-mono text-xs">editor</td><td class="px-4 py-3">Review & publish artikel</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
||||||
@@ -64,11 +64,12 @@
|
|||||||
<footer class="border-t border-gray-100 mt-24">
|
<footer class="border-t border-gray-100 mt-24">
|
||||||
<div class="max-w-6xl mx-auto px-6 py-8 flex flex-col md:flex-row items-center justify-between gap-4">
|
<div class="max-w-6xl mx-auto px-6 py-8 flex flex-col md:flex-row items-center justify-between gap-4">
|
||||||
<p class="text-sm text-gray-400">
|
<p class="text-sm text-gray-400">
|
||||||
Copyright © {{ date('Y') }} — Persegi, Desa Karangdadap, Kalibagor, Banyumas
|
Copyright © {{ date('Y') }} — Persegi, Desa Karangdadap
|
||||||
</p>
|
</p>
|
||||||
<div class="flex items-center gap-4 text-gray-400">
|
<div class="flex items-center gap-4 text-gray-400">
|
||||||
<a href="{{ route('kontak') }}" class="hover:text-gray-700 text-sm transition">Kontak</a>
|
<a href="{{ route('kontak') }}" class="hover:text-gray-700 text-sm transition">Kontak</a>
|
||||||
<a href="{{ route('tentang') }}" class="hover:text-gray-700 text-sm transition">Tentang</a>
|
<a href="{{ route('tentang') }}" class="hover:text-gray-700 text-sm transition">Tentang</a>
|
||||||
|
<a href="{{ route('guide') }}" class="hover:text-gray-700 text-sm transition">Panduan</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ Route::get('/blog', [\App\Http\Controllers\PublicController::class, 'blog'])->na
|
|||||||
Route::get('/blog/{post:slug}', [\App\Http\Controllers\PublicController::class, 'blogDetail'])->name('blog.detail');
|
Route::get('/blog/{post:slug}', [\App\Http\Controllers\PublicController::class, 'blogDetail'])->name('blog.detail');
|
||||||
Route::get('/kontak', [\App\Http\Controllers\PublicController::class, 'kontak'])->name('kontak');
|
Route::get('/kontak', [\App\Http\Controllers\PublicController::class, 'kontak'])->name('kontak');
|
||||||
Route::post('/kontak', [\App\Http\Controllers\PublicController::class, 'kontakStore'])->name('kontak.store');
|
Route::post('/kontak', [\App\Http\Controllers\PublicController::class, 'kontakStore'])->name('kontak.store');
|
||||||
|
Route::get('/panduan', [\App\Http\Controllers\PublicController::class, 'guide'])->name('guide');
|
||||||
|
|||||||
Reference in New Issue
Block a user