feat: implement comprehensive cooperation management module with models, migrations, and Filament resources
This commit is contained in:
parent
b9a266d35a
commit
c7a72a169e
320
COOPERATION_MODULE.md
Normal file
320
COOPERATION_MODULE.md
Normal file
@ -0,0 +1,320 @@
|
||||
# Modul Kerja Sama - Dokumentasi
|
||||
|
||||
## Overview
|
||||
Modul Kerja Sama adalah sistem manajemen kerjasama antara Admin dan Media Partner. Modul ini mengelola seluruh flow dari pengajuan kerjasama hingga pelaporan.
|
||||
|
||||
## Flow Kerja Sama
|
||||
|
||||
### 1. Admin Mengajukan Kerja Sama
|
||||
- Admin membuat data Cooperation baru dengan informasi:
|
||||
- Judul Kerja Sama
|
||||
- Tanggal Pengajuan Awal (initial_submission_date)
|
||||
- Tanggal Pengajuan Akhir (final_submission_date)
|
||||
- Deadline Penugasan (assignment_deadline)
|
||||
- Deadline Verifikasi (verification_deadline)
|
||||
- Deskripsi
|
||||
- Admin mengirim pengajuan ke Media Partner (CooperationMedia)
|
||||
- Status awal: **MENUNGGU**
|
||||
|
||||
### 2. Media Menerima/Menolak Pengajuan
|
||||
- Media mereview pengajuan kerjasama dalam periode initial_submission_date sampai final_submission_date
|
||||
- Media dapat:
|
||||
- **MENERIMA**: Status berubah menjadi ACCEPTED, lanjut ke step 3
|
||||
- **MENOLAK**: Status berubah menjadi REJECTED, flow selesai (dengan alasan penolakan/rejection reason)
|
||||
|
||||
### 3. Media Mengajukan Proposal
|
||||
- Jika Media menerima, Media mengajukan proposal (CooperationProposal)
|
||||
- Proposal berisi:
|
||||
- Deskripsi
|
||||
- Tanggal mulai dan akhir proposed
|
||||
- E-Catalog
|
||||
- File dokumen (via Spatie Media Library)
|
||||
- Jurnalis yang terlibat
|
||||
- Status proposal: **MENUNGGU**
|
||||
|
||||
### 4. Admin Meninjau Proposal
|
||||
- Admin mereview proposal yang diajukan Media
|
||||
- Admin dapat:
|
||||
- **MENERIMA**: Status proposal menjadi ACCEPTED, lanjut ke step 5
|
||||
- **MENOLAK**: Status proposal menjadi REJECTED, Media harus ajukan ulang (dengan alasan penolakan)
|
||||
|
||||
### 5. Admin Kirim Penugasan
|
||||
- Jika Admin menerima proposal, Admin membuat Task Assignment
|
||||
- Task Assignment berisi:
|
||||
- Start Date
|
||||
- End Date
|
||||
- Task Description
|
||||
- Report Amount (jumlah report yang harus dibuat)
|
||||
- Admin assign task ke Media Partner (TaskAssignmentMedia)
|
||||
- Status: **MENUNGGU**
|
||||
|
||||
### 6. Media Mengirim Laporan
|
||||
- Media membuat Report berdasarkan Task Assignment
|
||||
- Report berisi:
|
||||
- Title
|
||||
- Publication Date
|
||||
- Link
|
||||
- Description
|
||||
- Image (via Spatie Media Library)
|
||||
- Status report: **MENUNGGU** (untuk verifikasi admin)
|
||||
- Media dapat membuat multiple reports sesuai report_amount yang diminta
|
||||
|
||||
### 7. Admin Menerima/Menolak Laporan
|
||||
- Admin mereview laporan yang dikirim Media
|
||||
- Admin dapat:
|
||||
- **MENERIMA**: Status report menjadi ACCEPTED
|
||||
- **MENOLAK**: Status report menjadi REJECTED, Media harus kirim ulang (dengan alasan penolakan)
|
||||
|
||||
## Status Otomatis Berdasarkan Deadline
|
||||
|
||||
### Status Cooperation (otomatis berubah berdasarkan waktu):
|
||||
- **MENUNGGU**: Status awal ketika admin kirim kerja sama
|
||||
- **PENUGASAN**: Ketika tanggal final_submission_date habis (media sudah tidak bisa ajukan proposal)
|
||||
- **VERIFIKASI**: Ketika assignment_deadline habis (media sudah selesai kirim laporan)
|
||||
- **PEMBAYARAN**: Ketika verification_deadline habis (admin sudah selesai verifikasi laporan)
|
||||
- **COMPLETED**: Ketika pembayaran sudah selesai (payment_date diisi)
|
||||
|
||||
### Status Individual Components:
|
||||
- **ACCEPTED**: Diterima (untuk proposal, report, dll)
|
||||
- **REJECTED**: Ditolak (untuk proposal, report, dll) - selalu dengan alasan penolakan
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Table: cooperations
|
||||
- id
|
||||
- title (varchar 200)
|
||||
- initial_submission_date (date) - Tanggal mulai pengajuan proposal
|
||||
- final_submission_date (date) - Tanggal akhir pengajuan proposal
|
||||
- assignment_deadline (date, nullable) - Deadline penyelesaian penugasan
|
||||
- verification_deadline (date, nullable) - Deadline verifikasi laporan
|
||||
- description (text)
|
||||
- payment_amount (unsigned integer, nullable) - Jumlah pembayaran
|
||||
- payment_date (date, nullable) - Tanggal pembayaran
|
||||
- status (enum: MENUNGGU, PENUGASAN, VERIFIKASI, PEMBAYARAN, COMPLETED, ACCEPTED, REJECTED)
|
||||
- created_at, updated_at, deleted_at
|
||||
|
||||
### Table: cooperation_media (Pivot)
|
||||
- id
|
||||
- cooperation_id (FK)
|
||||
- partner_media_id (FK)
|
||||
- status (enum: PENDING, ACCEPTED, REJECTED)
|
||||
- created_at, updated_at
|
||||
|
||||
### Table: cooperation_proposals
|
||||
- id
|
||||
- cooperation_id (FK)
|
||||
- partner_media_id (FK)
|
||||
- description (text, nullable)
|
||||
- e_catalog (varchar 50, nullable)
|
||||
- status (enum: PENDING, ACCEPTED, REJECTED)
|
||||
- created_at, updated_at
|
||||
|
||||
### Table: task_assignments
|
||||
- id
|
||||
- cooperation_id (FK)
|
||||
- start_date (date)
|
||||
- end_date (date)
|
||||
- task_description (text)
|
||||
- report_amount (unsigned integer)
|
||||
- status (enum: PENDING, ACCEPTED, REJECTED)
|
||||
- created_at, updated_at
|
||||
|
||||
### Table: task_assignment_media (Pivot)
|
||||
- id
|
||||
- task_assignment_id (FK)
|
||||
- partner_media_id (FK)
|
||||
- status (enum: PENDING, ACCEPTED, REJECTED)
|
||||
- created_at, updated_at
|
||||
|
||||
### Table: reports
|
||||
- id
|
||||
- task_assignment_id (FK)
|
||||
- title (varchar 200)
|
||||
- publication_date (date)
|
||||
- link (varchar 50, nullable)
|
||||
- description (text, nullable)
|
||||
- status (enum: MENUNGGU, ACCEPTED, REJECTED)
|
||||
- created_at, updated_at, deleted_at
|
||||
- Media files (handled by Spatie Media Library)
|
||||
|
||||
### Table: rejection_reasons (Polymorphic)
|
||||
- id
|
||||
- rejectable_type (morphs)
|
||||
- rejectable_id (morphs)
|
||||
- reason (text)
|
||||
- created_at, updated_at
|
||||
|
||||
## Models
|
||||
|
||||
### Cooperation
|
||||
**Location**: `app/Models/Cooperation.php`
|
||||
|
||||
**Relationships**:
|
||||
- `partnerMedia()`: BelongsToMany - Media yang terlibat dalam kerjasama
|
||||
- `cooperationMedia()`: HasMany - Pivot records dengan status
|
||||
- `cooperationProposals()`: HasMany - Proposal yang diupload media
|
||||
- `taskAssignments()`: HasMany - Task yang diberikan ke media
|
||||
|
||||
### CooperationMedia (Pivot)
|
||||
**Location**: `app/Models/CooperationMedia.php`
|
||||
|
||||
**Relationships**:
|
||||
- `cooperation()`: BelongsTo
|
||||
- `partnerMedia()`: BelongsTo
|
||||
- `rejectionReasons()`: MorphMany
|
||||
|
||||
### CooperationProposal
|
||||
**Location**: `app/Models/CooperationProposal.php`
|
||||
|
||||
**Relationships**:
|
||||
- `cooperation()`: BelongsTo
|
||||
- `partnerMedia()`: BelongsTo
|
||||
- `rejectionReasons()`: MorphMany
|
||||
|
||||
### TaskAssignment
|
||||
**Location**: `app/Models/TaskAssignment.php`
|
||||
|
||||
**Relationships**:
|
||||
- `cooperation()`: BelongsTo
|
||||
- `partnerMedia()`: BelongsToMany (via task_assignment_media)
|
||||
- `taskAssignmentMedia()`: HasMany
|
||||
- `rejectionReasons()`: MorphMany
|
||||
- `reports()`: HasMany (commented, needs to be uncommented)
|
||||
|
||||
### Report
|
||||
**Location**: `app/Models/Report.php`
|
||||
|
||||
**Relationships**:
|
||||
- `taskAssignment()`: BelongsTo
|
||||
- `rejectionReasons()`: MorphMany
|
||||
|
||||
**Implements**: HasMedia (Spatie Media Library) untuk handle image dan file attachments
|
||||
|
||||
**Implements**: HasMedia (Spatie Media Library)
|
||||
|
||||
### RejectionReason
|
||||
**Location**: `app/Models/RejectionReason.php`
|
||||
|
||||
**Relationships**:
|
||||
- `rejectable()`: MorphTo - Dapat digunakan untuk CooperationMedia, CooperationProposal, TaskAssignment, Report
|
||||
|
||||
## Filament Resources
|
||||
|
||||
### CooperationResource
|
||||
**Location**: `app/Filament/Resources/Manage/Cooperations/CooperationResource.php`
|
||||
|
||||
**Navigation**:
|
||||
- Group: Manage
|
||||
- Label: Kerja Sama
|
||||
- Icon: HandRaised
|
||||
- Sort: 20
|
||||
- Slug: manage/cooperations
|
||||
|
||||
**Pages**:
|
||||
- List: `/manage/cooperations`
|
||||
- Create: `/manage/cooperations/create`
|
||||
- Edit: `/manage/cooperations/{record}/edit`
|
||||
|
||||
**Form Fields** (CooperationForm):
|
||||
- Title (required, max 200)
|
||||
- Initial Submission Date (required, date)
|
||||
- Final Submission Date (required, date, after initial_submission_date)
|
||||
- Description (required, textarea)
|
||||
|
||||
**Table Columns** (CooperationsTable):
|
||||
- Title
|
||||
- Initial Submission Date
|
||||
- Final Submission Date
|
||||
- Description
|
||||
- Created At
|
||||
- Updated At
|
||||
|
||||
## Status Enum
|
||||
|
||||
### CooperationStatus
|
||||
**Location**: `app/Enums/CooperationStatus.php`
|
||||
|
||||
**Values**:
|
||||
- `MENUNGGU` (1): Menunggu - Status awal kerja sama
|
||||
- `PENUGASAN` (2): Penugasan - Ketika deadline proposal habis
|
||||
- `VERIFIKASI` (3): Verifikasi - Ketika deadline penugasan habis
|
||||
- `PEMBAYARAN` (4): Pembayaran - Ketika verifikasi selesai
|
||||
- `COMPLETED` (5): Selesai - Ketika pembayaran selesai
|
||||
- `ACCEPTED` (6): Diterima - Untuk proposal/report yang diterima
|
||||
- `REJECTED` (7): Ditolak - Untuk proposal/report yang ditolak
|
||||
|
||||
**Colors**:
|
||||
- MENUNGGU: warning (yellow)
|
||||
- PENUGASAN: info (blue)
|
||||
- VERIFIKASI: primary (blue)
|
||||
- PEMBAYARAN: secondary (gray)
|
||||
- COMPLETED: success (green)
|
||||
- ACCEPTED: success (green)
|
||||
- REJECTED: danger (red)
|
||||
|
||||
## Next Steps / TODO
|
||||
|
||||
1. **Uncomment Report Relationship** di TaskAssignment model
|
||||
2. **Fix Status Colors** di CooperationStatus enum (ACCEPTED should be success, REJECTED should be danger)
|
||||
3. **Create Relation Managers** untuk:
|
||||
- CooperationMedia (di Cooperation Resource)
|
||||
- CooperationProposal (di Cooperation Resource)
|
||||
- TaskAssignment (di Cooperation Resource)
|
||||
- Report (di TaskAssignment Resource)
|
||||
4. **Create Custom Actions** untuk:
|
||||
- Send to Media (dari Cooperation)
|
||||
- Accept/Reject Cooperation (dari Media side)
|
||||
- Upload Proposal (dari Media side)
|
||||
- Accept/Reject Proposal (dari Admin side)
|
||||
- Create Task Assignment (dari Admin side)
|
||||
- Create Report (dari Media side)
|
||||
5. **Add Permissions** untuk role Admin dan Media
|
||||
6. **Create Notifications** untuk setiap perubahan status
|
||||
7. **Add Validation** untuk memastikan flow berjalan dengan benar
|
||||
8. **Create Dashboard Widgets** untuk monitoring kerjasama
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
app/
|
||||
├── Enums/
|
||||
│ └── CooperationStatus.php
|
||||
├── Filament/
|
||||
│ └── Resources/
|
||||
│ └── Manage/
|
||||
│ └── Cooperations/
|
||||
│ ├── CooperationResource.php
|
||||
│ ├── Pages/
|
||||
│ │ ├── ListCooperations.php
|
||||
│ │ ├── CreateCooperation.php
|
||||
│ │ └── EditCooperation.php
|
||||
│ ├── Schemas/
|
||||
│ │ └── CooperationForm.php
|
||||
│ └── Tables/
|
||||
│ └── CooperationsTable.php
|
||||
└── Models/
|
||||
├── Cooperation.php
|
||||
├── CooperationMedia.php
|
||||
├── CooperationProposal.php
|
||||
├── TaskAssignment.php
|
||||
├── Report.php
|
||||
└── RejectionReason.php
|
||||
|
||||
database/
|
||||
└── migrations/
|
||||
├── 2025_12_12_111212_create_cooperations_table.php
|
||||
├── 2025_12_12_111307_create_cooperation_media_table.php
|
||||
├── 2025_12_12_132245_create_cooperation_proposals_table.php
|
||||
├── 2025_12_12_132453_create_task_assignments_table.php
|
||||
├── 2025_12_12_133823_create_media_task_assignment_table.php
|
||||
├── 2025_12_12_135847_create_reports_table.php
|
||||
└── 2025_12_12_135855_create_rejection_reasons_table.php
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Semua tabel sudah di-migrate
|
||||
- Model Cooperation sudah dilengkapi dengan relationships
|
||||
- Resource Cooperation sudah dibuat dan siap digunakan
|
||||
- Typo di migration task_assignments sudah diperbaiki (task_description dari date ke text)
|
||||
- Icon Handshake diganti dengan HandRaised karena tidak tersedia di Heroicon
|
||||
42
app/Enums/ApprovalStatus.php
Normal file
42
app/Enums/ApprovalStatus.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Traits\Enum\WithComment;
|
||||
use App\Traits\Enum\WithValue;
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum ApprovalStatus: int implements HasColor, HasLabel
|
||||
{
|
||||
use WithComment, WithValue;
|
||||
|
||||
case PENDING = 1;
|
||||
case ACCEPTED = 2;
|
||||
case REJECTED = 3;
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return match ($this) {
|
||||
self::PENDING => 'Menunggu',
|
||||
self::ACCEPTED => 'Menerima',
|
||||
self::REJECTED => 'Menolak',
|
||||
};
|
||||
}
|
||||
|
||||
public function getColor(): string|array|null
|
||||
{
|
||||
return match ($this) {
|
||||
self::PENDING => 'warning',
|
||||
self::ACCEPTED => 'success',
|
||||
self::REJECTED => 'danger',
|
||||
};
|
||||
}
|
||||
|
||||
public static function options(): array
|
||||
{
|
||||
return collect(self::cases())
|
||||
->mapWithKeys(fn ($case) => [$case->value => $case->getLabel()])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
48
app/Enums/CooperationStatus.php
Normal file
48
app/Enums/CooperationStatus.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Traits\Enum\WithComment;
|
||||
use App\Traits\Enum\WithValue;
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum CooperationStatus: int implements HasColor, HasLabel
|
||||
{
|
||||
use WithComment, WithValue;
|
||||
|
||||
case PENDING = 1;
|
||||
case ASSIGNMENT = 2;
|
||||
case VERIFICATION = 3;
|
||||
case PAYMENT = 4;
|
||||
case COMPLETED = 5;
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return match ($this) {
|
||||
self::PENDING => 'Menunggu',
|
||||
self::ASSIGNMENT => 'Penugasan',
|
||||
self::VERIFICATION => 'Verifikasi',
|
||||
self::PAYMENT => 'Pembayaran',
|
||||
self::COMPLETED => 'Selesai',
|
||||
};
|
||||
}
|
||||
|
||||
public function getColor(): string|array|null
|
||||
{
|
||||
return match ($this) {
|
||||
self::PENDING => 'warning',
|
||||
self::ASSIGNMENT => 'info',
|
||||
self::VERIFICATION => 'primary',
|
||||
self::PAYMENT => 'secondary',
|
||||
self::COMPLETED => 'success',
|
||||
};
|
||||
}
|
||||
|
||||
public static function options(): array
|
||||
{
|
||||
return collect(self::cases())
|
||||
->mapWithKeys(fn ($case) => [$case->value => $case->getLabel()])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,7 @@ public function form(Schema $schema): Schema
|
||||
->components([
|
||||
TextInput::make('login')
|
||||
->label('Nama Pengguna atau Alamat Surel')
|
||||
->placeholder('johndoe@puskesmas.com')
|
||||
->placeholder('johndoe@gmail.com')
|
||||
->required()
|
||||
->autocomplete(false)
|
||||
->autofocus(),
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations;
|
||||
|
||||
use App\Filament\Resources\Manage\Cooperations\Pages\CreateCooperation;
|
||||
use App\Filament\Resources\Manage\Cooperations\Pages\EditCooperation;
|
||||
use App\Filament\Resources\Manage\Cooperations\Pages\ListCooperations;
|
||||
use App\Filament\Resources\Manage\Cooperations\Pages\ViewCooperation;
|
||||
use App\Filament\Resources\Manage\Cooperations\RelationManagers\CooperationMediaRelationManager;
|
||||
use App\Filament\Resources\Manage\Cooperations\RelationManagers\CooperationProposalRelationManager;
|
||||
use App\Filament\Resources\Manage\Cooperations\RelationManagers\ReportRelationManager;
|
||||
use App\Filament\Resources\Manage\Cooperations\RelationManagers\TaskAssignmentRelationManager;
|
||||
use App\Filament\Resources\Manage\Cooperations\Schemas\CooperationForm;
|
||||
use App\Filament\Resources\Manage\Cooperations\Tables\CooperationsTable;
|
||||
use App\Models\Cooperation;
|
||||
use BackedEnum;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use UnitEnum;
|
||||
|
||||
class CooperationResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Cooperation::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::HandRaised;
|
||||
|
||||
protected static ?string $navigationLabel = 'Kerja Sama';
|
||||
|
||||
protected static string|UnitEnum|null $navigationGroup = 'Manage';
|
||||
|
||||
protected static ?string $slug = 'manage/cooperations';
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'title';
|
||||
|
||||
protected static ?int $navigationSort = 20;
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return CooperationForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return CooperationsTable::configure($table)
|
||||
->modifyQueryUsing(function (Builder $query) {
|
||||
if (auth()->user()->hasRole('Perusahaan')) {
|
||||
$query->whereHas('cooperationMedia', function ($subQuery) {
|
||||
$subQuery->whereHas('partnerMedia', function ($mediaQuery) {
|
||||
$mediaQuery->whereHas('company', function ($companyQuery) {
|
||||
$companyQuery->where('user_id', auth()->id());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
});
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make('Informasi Kerja Sama')
|
||||
->headerActions([
|
||||
Action::make('status')
|
||||
->label(fn ($record) => $record->status->getLabel())
|
||||
->badge()
|
||||
->color(fn ($record) => $record->status->getColor())
|
||||
->disabled(),
|
||||
])
|
||||
->schema([
|
||||
TextEntry::make('title')
|
||||
->label('Judul'),
|
||||
|
||||
Grid::make(4)
|
||||
->schema([
|
||||
TextEntry::make('initial_submission_date')
|
||||
->label('Tanggal Pengajuan Awal')
|
||||
->date('l, d F Y'),
|
||||
|
||||
TextEntry::make('final_submission_date')
|
||||
->label('Tanggal Pengajuan Akhir')
|
||||
->date('l, d F Y'),
|
||||
|
||||
TextEntry::make('assignment_deadline')
|
||||
->label('Deadline Penugasan')
|
||||
->date('l, d F Y')
|
||||
->placeholder('Belum ditentukan'),
|
||||
|
||||
TextEntry::make('verification_deadline')
|
||||
->label('Deadline Verifikasi')
|
||||
->date('l, d F Y')
|
||||
->placeholder('Belum ditentukan'),
|
||||
]),
|
||||
|
||||
Section::make('Informasi Pembayaran')
|
||||
->schema([
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
TextEntry::make('payment_amount')
|
||||
->label('Jumlah Pembayaran')
|
||||
->money('IDR')
|
||||
->placeholder('Belum ditentukan'),
|
||||
|
||||
TextEntry::make('payment_date')
|
||||
->label('Tanggal Pembayaran')
|
||||
->date('l, d F Y')
|
||||
->placeholder('Belum dibayar'),
|
||||
]),
|
||||
])
|
||||
->visible(fn ($record) => $record->payment_amount || $record->payment_date),
|
||||
|
||||
TextEntry::make('description')
|
||||
->label('Deskripsi')
|
||||
->markdown(),
|
||||
]),
|
||||
])
|
||||
->columns(1);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
CooperationMediaRelationManager::class,
|
||||
CooperationProposalRelationManager::class,
|
||||
TaskAssignmentRelationManager::class,
|
||||
ReportRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListCooperations::route('/'),
|
||||
'create' => CreateCooperation::route('/create'),
|
||||
'view' => ViewCooperation::route('/{record}'),
|
||||
'edit' => EditCooperation::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getRecordRouteBindingEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\Pages;
|
||||
|
||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateCooperation extends CreateRecord
|
||||
{
|
||||
protected static string $resource = CooperationResource::class;
|
||||
|
||||
protected static ?string $title = 'Tambah Kerja Sama';
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('index');
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('back')
|
||||
->label('Kembali')
|
||||
->url(fn (): string => route('filament.dashboard.resources.manage.cooperations.index'))
|
||||
->outlined()
|
||||
->color('secondary'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\Pages;
|
||||
|
||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditCooperation extends EditRecord
|
||||
{
|
||||
protected static string $resource = CooperationResource::class;
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('index');
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('back')
|
||||
->label('Kembali')
|
||||
->url(fn (): string => route('filament.dashboard.resources.manage.cooperations.index'))
|
||||
->outlined()
|
||||
->color('secondary'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\Pages;
|
||||
|
||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ListCooperations extends ListRecords
|
||||
{
|
||||
protected static string $resource = CooperationResource::class;
|
||||
|
||||
protected static ?string $title = 'Kerja Sama';
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
if (! Auth::user()->hasRole('Perusahaan')) {
|
||||
$actions[] = CreateAction::make()
|
||||
->label('Tambah');
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\Pages;
|
||||
|
||||
use App\Enums\CooperationStatus;
|
||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Filament\Schemas\Components\RepeatableEntry;
|
||||
use Filament\Schemas\Components\TextEntry;
|
||||
use Filament\Support\Enums\FontWeight;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ViewCooperation extends ViewRecord
|
||||
{
|
||||
protected static string $resource = CooperationResource::class;
|
||||
|
||||
protected function getWorkflowSteps()
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
$steps = [
|
||||
[
|
||||
'step' => 1,
|
||||
'title' => 'Pengajuan Kerja Sama',
|
||||
'description' => 'Admin mengajukan kerja sama ke media',
|
||||
'status' => 'completed', // Always completed since we're viewing it
|
||||
'date' => $record->created_at?->format('d/m/Y H:i'),
|
||||
],
|
||||
[
|
||||
'step' => 2,
|
||||
'title' => 'Respon Media',
|
||||
'description' => 'Media menerima atau menolak pengajuan',
|
||||
'status' => $this->getMediaResponseStatus($record),
|
||||
'date' => $this->getMediaResponseDate($record),
|
||||
],
|
||||
[
|
||||
'step' => 3,
|
||||
'title' => 'Upload Proposal',
|
||||
'description' => 'Media mengupload dokumen proposal',
|
||||
'status' => $this->getProposalStatus($record),
|
||||
'date' => $this->getProposalDate($record),
|
||||
],
|
||||
[
|
||||
'step' => 4,
|
||||
'title' => 'Review Proposal',
|
||||
'description' => 'Admin mereview dan menyetujui proposal',
|
||||
'status' => $this->getProposalReviewStatus($record),
|
||||
'date' => $this->getProposalReviewDate($record),
|
||||
],
|
||||
[
|
||||
'step' => 5,
|
||||
'title' => 'Penugasan',
|
||||
'description' => 'Admin membuat task assignment',
|
||||
'status' => $this->getTaskStatus($record),
|
||||
'date' => $this->getTaskDate($record),
|
||||
],
|
||||
[
|
||||
'step' => 6,
|
||||
'title' => 'Upload Laporan',
|
||||
'description' => 'Media mengupload laporan',
|
||||
'status' => $this->getReportStatus($record),
|
||||
'date' => $this->getReportDate($record),
|
||||
],
|
||||
[
|
||||
'step' => 7,
|
||||
'title' => 'Selesai',
|
||||
'description' => 'Kerja sama telah selesai',
|
||||
'status' => $this->getCompletionStatus($record),
|
||||
'date' => $this->getCompletionDate($record),
|
||||
],
|
||||
];
|
||||
|
||||
return RepeatableEntry::make('workflow_steps')
|
||||
->label('')
|
||||
->schema([
|
||||
TextEntry::make('step')
|
||||
->label('Langkah')
|
||||
->formatStateUsing(fn ($state) => "Langkah {$state}")
|
||||
->weight(FontWeight::Bold),
|
||||
|
||||
TextEntry::make('title')
|
||||
->label('Judul'),
|
||||
|
||||
TextEntry::make('description')
|
||||
->label('Deskripsi'),
|
||||
|
||||
TextEntry::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'completed' => 'success',
|
||||
'in_progress' => 'warning',
|
||||
'pending' => 'gray',
|
||||
'rejected' => 'danger',
|
||||
default => 'gray',
|
||||
})
|
||||
->formatStateUsing(fn (string $state): string => match ($state) {
|
||||
'completed' => 'Selesai',
|
||||
'in_progress' => 'Sedang Berjalan',
|
||||
'pending' => 'Menunggu',
|
||||
'rejected' => 'Ditolak',
|
||||
default => $state,
|
||||
}),
|
||||
|
||||
TextEntry::make('date')
|
||||
->label('Tanggal')
|
||||
->placeholder('Belum ada'),
|
||||
])
|
||||
->columns(5)
|
||||
->state($steps);
|
||||
}
|
||||
|
||||
protected function getMediaResponseStatus($record): string
|
||||
{
|
||||
$mediaStatuses = $record->cooperationMedia->pluck('status')->unique();
|
||||
|
||||
if ($mediaStatuses->contains(CooperationStatus::ACCEPTED)) {
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
if ($mediaStatuses->contains(CooperationStatus::REJECTED)) {
|
||||
return 'rejected';
|
||||
}
|
||||
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
protected function getMediaResponseDate($record)
|
||||
{
|
||||
$latestResponse = $record->cooperationMedia
|
||||
->whereNotNull('updated_at')
|
||||
->sortByDesc('updated_at')
|
||||
->first();
|
||||
|
||||
return $latestResponse?->updated_at?->format('d/m/Y H:i');
|
||||
}
|
||||
|
||||
protected function getProposalStatus($record): string
|
||||
{
|
||||
if ($record->proposals->isNotEmpty()) {
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
protected function getProposalDate($record)
|
||||
{
|
||||
$latestProposal = $record->proposals->sortByDesc('created_at')->first();
|
||||
|
||||
return $latestProposal?->created_at?->format('d/m/Y H:i');
|
||||
}
|
||||
|
||||
protected function getProposalReviewStatus($record): string
|
||||
{
|
||||
$proposals = $record->proposals;
|
||||
|
||||
if ($proposals->where('status', CooperationStatus::ACCEPTED)->isNotEmpty()) {
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
if ($proposals->where('status', CooperationStatus::REJECTED)->isNotEmpty()) {
|
||||
return 'rejected';
|
||||
}
|
||||
|
||||
if ($proposals->isNotEmpty()) {
|
||||
return 'in_progress';
|
||||
}
|
||||
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
protected function getProposalReviewDate($record)
|
||||
{
|
||||
$latestProposal = $record->proposals->sortByDesc('updated_at')->first();
|
||||
|
||||
return $latestProposal?->updated_at?->format('d/m/Y H:i');
|
||||
}
|
||||
|
||||
protected function getTaskStatus($record): string
|
||||
{
|
||||
if ($record->taskAssignments->isNotEmpty()) {
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
protected function getTaskDate($record)
|
||||
{
|
||||
$latestTask = $record->taskAssignments->sortByDesc('created_at')->first();
|
||||
|
||||
return $latestTask?->created_at?->format('d/m/Y H:i');
|
||||
}
|
||||
|
||||
protected function getReportStatus($record): string
|
||||
{
|
||||
$tasks = $record->taskAssignments;
|
||||
|
||||
if ($tasks->isEmpty()) {
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
$totalReports = $tasks->sum('report_amount');
|
||||
$completedReports = $tasks->flatMap->reports->count();
|
||||
|
||||
if ($completedReports >= $totalReports && $totalReports > 0) {
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
if ($completedReports > 0) {
|
||||
return 'in_progress';
|
||||
}
|
||||
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
protected function getReportDate($record)
|
||||
{
|
||||
$latestReport = $record->taskAssignments
|
||||
->flatMap->reports
|
||||
->sortByDesc('created_at')
|
||||
->first();
|
||||
|
||||
return $latestReport?->created_at?->format('d/m/Y H:i');
|
||||
}
|
||||
|
||||
protected function getCompletionStatus($record): string
|
||||
{
|
||||
// Logic for completion - could be based on all reports accepted, etc.
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
protected function getCompletionDate($record)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
$isPerusahaan = Auth::user()->hasRole('Perusahaan');
|
||||
$record = $this->getRecord();
|
||||
|
||||
$actions = [];
|
||||
|
||||
if (! $isPerusahaan) {
|
||||
$actions[] = Action::make('mark_completed')
|
||||
->label('Tandai Selesai')
|
||||
->icon('heroicon-o-check-circle')
|
||||
->color('success')
|
||||
->action(function () {
|
||||
// Logic to mark cooperation as completed
|
||||
Notification::make()
|
||||
->title('Kerja Sama Ditandai Selesai')
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->visible(fn () => $this->canMarkAsCompleted());
|
||||
} else {
|
||||
// For Perusahaan users, add accept/reject actions if they have a cooperation media record
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', Auth::id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($cooperationMedia && $cooperationMedia->status === CooperationStatus::PENDING) {
|
||||
$actions[] = Action::make('accept')
|
||||
->label('Terima Kerja Sama')
|
||||
->icon('heroicon-o-check-circle')
|
||||
->color('success')
|
||||
->action(function () use ($cooperationMedia) {
|
||||
$cooperationMedia->update(['status' => CooperationStatus::ACCEPTED]);
|
||||
Notification::make()
|
||||
->title('Kerja Sama Diterima')
|
||||
->success()
|
||||
->send();
|
||||
});
|
||||
|
||||
$actions[] = Action::make('reject')
|
||||
->label('Tolak Kerja Sama')
|
||||
->icon('heroicon-o-x-circle')
|
||||
->color('danger')
|
||||
->action(function () use ($cooperationMedia) {
|
||||
$cooperationMedia->update(['status' => CooperationStatus::REJECTED]);
|
||||
Notification::make()
|
||||
->title('Kerja Sama Ditolak')
|
||||
->success()
|
||||
->send();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$actions[] = Action::make('back')
|
||||
->label('Kembali')
|
||||
->url(fn (): string => static::getResource()::getUrl('index'))
|
||||
->outlined()
|
||||
->color('secondary');
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
protected function canMarkAsCompleted(): bool
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
// Check if all tasks have required number of reports
|
||||
foreach ($record->taskAssignments as $task) {
|
||||
$reportCount = $task->reports()->count();
|
||||
if ($reportCount < $task->report_amount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $record->taskAssignments->isNotEmpty();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CooperationMediaRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'cooperationMedia';
|
||||
|
||||
protected static ?string $title = 'Media';
|
||||
|
||||
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
|
||||
{
|
||||
return ! auth()->user()->hasRole('Perusahaan');
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('partnerMedia.name')
|
||||
->columns([
|
||||
TextColumn::make('partnerMedia.name')
|
||||
->label('Nama')
|
||||
->searchable(),
|
||||
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge(),
|
||||
])
|
||||
->filters([])
|
||||
->headerActions([])
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
])
|
||||
->toolbarActions([])
|
||||
->recordAction(null);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\CooperationProposal;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CooperationProposalRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'proposals';
|
||||
|
||||
protected static ?string $title = 'Proposal';
|
||||
|
||||
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
|
||||
{
|
||||
return $ownerRecord->proposals()->count();
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('description')
|
||||
->columns([
|
||||
TextColumn::make('partnerMedia.name')
|
||||
->label('Media')
|
||||
->searchable()
|
||||
->visible(! auth()->user()->hasRole('Perusahaan')),
|
||||
|
||||
TextColumn::make('e_catalog')
|
||||
->label('E-Catalog')
|
||||
->url(fn ($record) => $record->e_catalog)
|
||||
->openUrlInNewTab()
|
||||
->color('primary'),
|
||||
|
||||
TextColumn::make('description')
|
||||
->label('Deskripsi'),
|
||||
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge(),
|
||||
|
||||
TextColumn::make('submitted_at')
|
||||
->label('Diajukan')
|
||||
->dateTime('l, d F Y H:i:s')
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('responded_at')
|
||||
->label('Ditanggapi')
|
||||
->dateTime('l, d F Y H:i:s')
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('rejectionReasons.reason')
|
||||
->label('Alasan Penolakan')
|
||||
->searchable(),
|
||||
])
|
||||
->filters([])
|
||||
->headerActions([])
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
Action::make('accept')
|
||||
->label('Terima')
|
||||
->icon(Heroicon::OutlinedCheck)
|
||||
->color('success')
|
||||
->action(function (CooperationProposal $record) {
|
||||
$record->update([
|
||||
'status' => ApprovalStatus::ACCEPTED,
|
||||
'responded_at' => now(),
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->title('Proposal Diterima')
|
||||
->body("Proposal dari {$record->partnerMedia->name} telah diterima.")
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->visible(fn (CooperationProposal $record) => ! auth()->user()->hasRole('Perusahaan') && $record->status === ApprovalStatus::PENDING),
|
||||
Action::make('reject')
|
||||
->label('Tolak')
|
||||
->icon(Heroicon::XMark)
|
||||
->color('danger')
|
||||
->schema([
|
||||
Textarea::make('reason')
|
||||
->label('Alasan Penolakan')
|
||||
->placeholder('...')
|
||||
->required(),
|
||||
])
|
||||
->action(function (CooperationProposal $record, array $data) {
|
||||
$record->update([
|
||||
'status' => ApprovalStatus::REJECTED,
|
||||
'responded_at' => now(),
|
||||
]);
|
||||
|
||||
$record->rejectionReasons()->create(['reason' => $data['reason']]);
|
||||
|
||||
Notification::make()
|
||||
->title('Proposal Ditolak')
|
||||
->body("Proposal dari {$record->partnerMedia->name} telah ditolak.")
|
||||
->warning()
|
||||
->send();
|
||||
})
|
||||
->visible(fn (CooperationProposal $record) => ! auth()->user()->hasRole('Perusahaan') && $record->status === ApprovalStatus::PENDING)
|
||||
->modalWidth('lg'),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
])
|
||||
->recordAction(null);
|
||||
}
|
||||
|
||||
// public function infolist(Infolist $infolist): Infolist
|
||||
// {
|
||||
// return $infolist
|
||||
// ->schema([
|
||||
// TextEntry::make('e_catalog')
|
||||
// ->label('File Proposal')
|
||||
// ->url(fn($record) => $record->e_catalog)
|
||||
// ->openUrlInNewTab()
|
||||
// ->columnSpanFull(),
|
||||
// ]);
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\Report;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class ReportRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'reports';
|
||||
|
||||
protected static ?string $title = 'Laporan';
|
||||
|
||||
public function isReadOnly(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
TextInput::make('title')
|
||||
->placeholder('Dirgahayu Purwakarta')
|
||||
->required()
|
||||
->maxLength(200),
|
||||
DatePicker::make('publication_date')
|
||||
->label('Tanggal Publikasi')
|
||||
->placeholder(fn () => now()->format('l, d F Y'))
|
||||
->native(false)
|
||||
->displayFormat('l, d F Y')
|
||||
->required(),
|
||||
TextInput::make('link')
|
||||
->label('Tautan')
|
||||
->placeholder('https://example.com')
|
||||
->maxLength(50)
|
||||
->url()
|
||||
->required(),
|
||||
Textarea::make('description')
|
||||
->label('Deskripsi')
|
||||
->placeholder('....')
|
||||
->required(),
|
||||
SpatieMediaLibraryFileUpload::make('image')
|
||||
->label('Gambar')
|
||||
->disk(config('filesystems.default'))
|
||||
->acceptedFileTypes(['images/*'])
|
||||
->maxSize(1024 * 3)
|
||||
->collection('reports')
|
||||
->image()
|
||||
->required(),
|
||||
])
|
||||
->columns(1);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('title')
|
||||
->columns([
|
||||
TextColumn::make('title')
|
||||
->label('Judul')
|
||||
->searchable(),
|
||||
TextColumn::make('publication_date')
|
||||
->label('Tanggal Publikasi')
|
||||
->date('l, d F Y'),
|
||||
TextColumn::make('link')
|
||||
->label('Link')
|
||||
->limit(30)
|
||||
->url(fn ($record) => $record->link)
|
||||
->openUrlInNewTab(),
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge(),
|
||||
TextColumn::make('rejectionReasons.reason')
|
||||
->label('Alasan Penolakan')
|
||||
->searchable(),
|
||||
])
|
||||
->filters([])
|
||||
->recordActions([
|
||||
Action::make('accept')
|
||||
->label('Terima')
|
||||
->icon(Heroicon::OutlinedCheck)
|
||||
->color('success')
|
||||
->action(function (Report $record) {
|
||||
$record->update([
|
||||
'status' => ApprovalStatus::ACCEPTED,
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->title('Laporan Diterima')
|
||||
->body("Laporan '{$record->title}' telah diterima.")
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->visible(fn (Report $record) => ! auth()->user()->hasRole('Perusahaan') && $record->status === ApprovalStatus::PENDING),
|
||||
Action::make('reject')
|
||||
->label('Tolak')
|
||||
->icon(Heroicon::XMark)
|
||||
->color('danger')
|
||||
->schema([
|
||||
Textarea::make('reason')
|
||||
->label('Alasan Penolakan')
|
||||
->placeholder('...')
|
||||
->required(),
|
||||
])
|
||||
->action(function (Report $record, array $data) {
|
||||
$record->update([
|
||||
'status' => ApprovalStatus::REJECTED,
|
||||
]);
|
||||
|
||||
$record->rejectionReasons()->create(['reason' => $data['reason']]);
|
||||
|
||||
Notification::make()
|
||||
->title('Laporan Ditolak')
|
||||
->body("Laporan '{$record->title}' telah ditolak.")
|
||||
->warning()
|
||||
->send();
|
||||
})
|
||||
->visible(fn (Report $record) => ! auth()->user()->hasRole('Perusahaan') && $record->status === ApprovalStatus::PENDING)
|
||||
->modalWidth('lg'),
|
||||
])
|
||||
->headerActions([
|
||||
CreateAction::make()
|
||||
->label('Buat Laporan')
|
||||
->modalWidth('lg')
|
||||
->modalHeading('Buat Laporan')
|
||||
->mutateDataUsing(function (array $data): array {
|
||||
$data['task_assignment_id'] = $this->getOwnerRecord()->taskAssignments()->first()?->id;
|
||||
|
||||
return $data;
|
||||
}),
|
||||
])
|
||||
->toolbarActions([]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\TaskAssignment;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class TaskAssignmentRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'taskAssignments';
|
||||
|
||||
protected static ?string $title = 'Penugasan';
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
DatePicker::make('start_date')
|
||||
->label('Tanggal Mulai')
|
||||
->required()
|
||||
->native(false),
|
||||
|
||||
DatePicker::make('end_date')
|
||||
->label('Tanggal Selesai')
|
||||
->required()
|
||||
->native(false)
|
||||
->after('start_date'),
|
||||
|
||||
Textarea::make('task_description')
|
||||
->label('Deskripsi Tugas')
|
||||
->required()
|
||||
->rows(4)
|
||||
->maxLength(65535),
|
||||
|
||||
TextInput::make('report_amount')
|
||||
->label('Jumlah Laporan')
|
||||
->numeric()
|
||||
->required()
|
||||
->minValue(1)
|
||||
->default(1),
|
||||
|
||||
Select::make('partner_media_ids')
|
||||
->label('Media yang Ditugaskan')
|
||||
->relationship('partnerMedia', 'name')
|
||||
->multiple()
|
||||
->preload()
|
||||
->required(),
|
||||
|
||||
Select::make('status')
|
||||
->options(ApprovalStatus::options())
|
||||
->required()
|
||||
->default(ApprovalStatus::PENDING),
|
||||
|
||||
Textarea::make('rejection_reason')
|
||||
->label('Alasan Penolakan')
|
||||
->rows(3)
|
||||
->columnSpanFull(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('task_description')
|
||||
->columns([
|
||||
TextColumn::make('start_date')
|
||||
->label('Mulai')
|
||||
->date('l, d F Y'),
|
||||
|
||||
TextColumn::make('end_date')
|
||||
->label('Selesai')
|
||||
->date('l, d F Y'),
|
||||
|
||||
TextColumn::make('task_description')
|
||||
->label('Deskripsi Tugas')
|
||||
->limit(50),
|
||||
|
||||
TextColumn::make('report_amount')
|
||||
->label('Jumlah Laporan'),
|
||||
|
||||
TextColumn::make('report_count')
|
||||
->label('Laporan Diterima')
|
||||
->getStateUsing(function (TaskAssignment $record) {
|
||||
return $record->reports()->count().' / '.($record->report_amount * $record->partnerMedia()->count());
|
||||
}),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->headerActions([
|
||||
CreateAction::make()
|
||||
->label('Buat Penugasan')
|
||||
->visible(fn () => $this->canCreateTaskAssignment()),
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function canCreateTaskAssignment(): bool
|
||||
{
|
||||
$cooperation = $this->getOwnerRecord();
|
||||
|
||||
// Can create task assignment only if there are accepted proposals
|
||||
return $cooperation->proposals()->where('status', ApprovalStatus::ACCEPTED)->exists();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\Schemas;
|
||||
|
||||
use App\Models\PartnerMedia;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
|
||||
class CooperationForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make([
|
||||
TextInput::make('title')
|
||||
->label('Judul')
|
||||
->placeholder('Publikasi Pembangunan Daerah')
|
||||
->autocomplete(false)
|
||||
->autofocus()
|
||||
->required()
|
||||
->maxLength(200),
|
||||
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
DatePicker::make('initial_submission_date')
|
||||
->label('Tanggal Pengajuan Awal')
|
||||
->placeholder(fn () => now()->format('Y-m-d'))
|
||||
->native(false)
|
||||
->displayFormat('l, d F Y')
|
||||
->required(),
|
||||
|
||||
DatePicker::make('final_submission_date')
|
||||
->label('Tanggal Pengajuan Akhir')
|
||||
->placeholder(fn () => now()->addDays(30)->format('Y-m-d'))
|
||||
->native(false)
|
||||
->displayFormat('l, d F Y')
|
||||
->required()
|
||||
->after('initial_submission_date'),
|
||||
]),
|
||||
|
||||
Select::make('partner_media_ids')
|
||||
->label('Media')
|
||||
->relationship('partnerMedia', 'name')
|
||||
->multiple()
|
||||
->preload()
|
||||
->searchable()
|
||||
->native(false)
|
||||
->required()
|
||||
->selectablePlaceholder(false)
|
||||
->helperText('Pilih media yang akan diajak kerja sama.')
|
||||
->suffixAction(
|
||||
Action::make('selectAll')
|
||||
->label('Select All')
|
||||
->icon(Heroicon::OutlinedCheckCircle)
|
||||
->action(function ($set, $livewire) {
|
||||
$allMediaIds = PartnerMedia::pluck('id')->toArray();
|
||||
$set('partner_media_ids', $allMediaIds);
|
||||
})
|
||||
),
|
||||
|
||||
Textarea::make('description')
|
||||
->label('Deskripsi')
|
||||
->placeholder('...')
|
||||
->required()
|
||||
->rows(5)
|
||||
->maxLength(65535),
|
||||
])
|
||||
->columnSpan(2),
|
||||
|
||||
Section::make([
|
||||
SpatieMediaLibraryFileUpload::make('banner')
|
||||
->label('Banner')
|
||||
->disk(config('filesystems.default'))
|
||||
->acceptedFileTypes(['images/*'])
|
||||
->maxSize(1024 * 3)
|
||||
->collection('cooperation')
|
||||
->image(),
|
||||
|
||||
SpatieMediaLibraryFileUpload::make('proposal_template')
|
||||
->label('Template Pengajuan')
|
||||
->disk(config('filesystems.default'))
|
||||
->acceptedFileTypes(['application/pdf'])
|
||||
->maxSize(1024 * 10)
|
||||
->collection('cooperation')
|
||||
->required(),
|
||||
]),
|
||||
])
|
||||
->columns(3);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Manage\Cooperations\Tables;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Enums\CooperationStatus;
|
||||
use App\Filament\Actions\DefaultBulkActions;
|
||||
use App\Filament\Columns\TimestampColumns;
|
||||
use App\Models\Cooperation;
|
||||
use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload;
|
||||
use Carbon\CarbonInterface;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\TrashedFilter;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class CooperationsTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('title')
|
||||
->label('Judul')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->wrap(),
|
||||
|
||||
TextColumn::make('partnerMedia.name')
|
||||
->label('Media')
|
||||
->listWithLineBreaks()
|
||||
->limitList(3)
|
||||
->expandableLimitedList()
|
||||
->badge()
|
||||
->visible(! auth()->user()->hasRole('Perusahaan')),
|
||||
|
||||
TextColumn::make('initial_submission_date')
|
||||
->label('Tgl Pengajuan Awal')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->date('l, d F Y'),
|
||||
|
||||
TextColumn::make('final_submission_date')
|
||||
->label('Tgl Pengajuan Akhir')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->date('l, d F Y')
|
||||
->description(
|
||||
fn (Cooperation $record): string => now()->greaterThan($record->final_submission_date)
|
||||
? 'Waktu Pengajuan Habis'
|
||||
: 'Sisa waktu pengajuan: '.now()->diffForHumans(
|
||||
$record->final_submission_date,
|
||||
['syntax' => CarbonInterface::DIFF_ABSOLUTE]
|
||||
)
|
||||
),
|
||||
|
||||
TextColumn::make('assignment_deadline')
|
||||
->label('Deadline Penugasan')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->date('l, d F Y')
|
||||
->placeholder('Belum ditentukan')
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('verification_deadline')
|
||||
->label('Deadline Verifikasi')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->date('l, d F Y')
|
||||
->placeholder('Belum ditentukan')
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('status')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->badge(),
|
||||
|
||||
TextColumn::make('media_count')
|
||||
->label('Tanggapan Media')
|
||||
->html()
|
||||
->getStateUsing(function (Cooperation $record) {
|
||||
$record->loadCount([
|
||||
'partnerMedia as approved_count' => fn ($query) => $query->where('status', ApprovalStatus::ACCEPTED),
|
||||
'partnerMedia as rejected_count' => fn ($query) => $query->where('status', ApprovalStatus::REJECTED),
|
||||
'partnerMedia as pending_count' => fn ($query) => $query->where('status', ApprovalStatus::PENDING),
|
||||
]);
|
||||
|
||||
$totalCount = $record->partnerMedia()->count();
|
||||
|
||||
return "Menerima: {$record->approved_count}<br>
|
||||
Menolak: {$record->rejected_count}<br>
|
||||
Belum Menanggapi: {$record->pending_count}<br>
|
||||
Total: {$totalCount}";
|
||||
})
|
||||
->visible(! auth()->user()->hasRole('Perusahaan')),
|
||||
|
||||
TextColumn::make('status_tanggapan')
|
||||
->label('Tanggapan')
|
||||
->getStateUsing(function ($record) {
|
||||
if (auth()->user()->hasRole('Perusahaan')) {
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
return $cooperationMedia?->status;
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
->badge()
|
||||
->visible(auth()->user()->hasRole('Perusahaan')),
|
||||
|
||||
TextColumn::make('payment_amount')
|
||||
->label('Jumlah Pembayaran')
|
||||
->money('IDR')
|
||||
->sortable()
|
||||
->placeholder('Belum ditentukan')
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('payment_date')
|
||||
->label('Tanggal Pembayaran')
|
||||
->date('l, d F Y')
|
||||
->sortable()
|
||||
->placeholder('Belum dibayar')
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('description')
|
||||
->label('Deskripsi')
|
||||
->searchable()
|
||||
->limit(50)
|
||||
->wrap()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
...TimestampColumns::make(),
|
||||
])
|
||||
->filters([
|
||||
TrashedFilter::make()->native(false),
|
||||
])
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
Action::make('accept')
|
||||
->label('Terima')
|
||||
->icon(Heroicon::OutlinedCheck)
|
||||
->color('success')
|
||||
->action(function ($record) {
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($cooperationMedia) {
|
||||
$cooperationMedia->update(['status' => ApprovalStatus::ACCEPTED]);
|
||||
Notification::make()
|
||||
->title('Kerja sama diterima')
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
})
|
||||
->visible(function ($record) {
|
||||
if (! auth()->user()->hasRole('Perusahaan')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
return $cooperationMedia && $cooperationMedia->status === ApprovalStatus::PENDING;
|
||||
}),
|
||||
Action::make('reject')
|
||||
->label('Tolak')
|
||||
->icon(Heroicon::XMark)
|
||||
->color('danger')
|
||||
->action(function ($record) {
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($cooperationMedia) {
|
||||
$cooperationMedia->update(['status' => ApprovalStatus::REJECTED]);
|
||||
Notification::make()
|
||||
->title('Kerja sama ditolak')
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
})
|
||||
->visible(function ($record) {
|
||||
if (! auth()->user()->hasRole('Perusahaan')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
return $cooperationMedia && $cooperationMedia->status === ApprovalStatus::PENDING;
|
||||
}),
|
||||
Action::make('send_proposal')
|
||||
->label('Ajukan Proposal')
|
||||
->icon(Heroicon::DocumentText)
|
||||
->color('info')
|
||||
->schema([
|
||||
TextInput::make('e_catalog')
|
||||
->label('E-Catalog')
|
||||
->placeholder('https://katalog.inaproc.id/purwakarta-kita-media-karya')
|
||||
->autocomplete(false)
|
||||
->autofocus()
|
||||
->nullable()
|
||||
->url(),
|
||||
|
||||
Textarea::make('description')
|
||||
->label('Deskripsi Proposal')
|
||||
->autocomplete(false)
|
||||
->required()
|
||||
->rows(4)
|
||||
->placeholder('...'),
|
||||
|
||||
AdvancedFileUpload::make('proposal_attachment')
|
||||
->label('Lampiran Proposal')
|
||||
->disk(config('filesystems.default'))
|
||||
->acceptedFileTypes(['application/pdf'])
|
||||
->maxSize(1024 * 10)
|
||||
->directory('cooperation/proposal/'.now()->toDateString())
|
||||
->required(),
|
||||
])
|
||||
->action(function ($record, array $data) {
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($cooperationMedia) {
|
||||
$record->proposals()->create([
|
||||
'partner_media_id' => $cooperationMedia->partner_media_id,
|
||||
'description' => $data['description'],
|
||||
'e_catalog' => $data['e_catalog'],
|
||||
'status' => ApprovalStatus::PENDING->value,
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->title('Proposal kerja sama berhasil dikirim')
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
})
|
||||
->modalHeading('Proposal Kerja Sama')
|
||||
->modalSubmitActionLabel('Kirim Proposal')
|
||||
->visible(function ($record) {
|
||||
if (! auth()->user()->hasRole('Perusahaan')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cooperationMedia = $record->cooperationMedia()
|
||||
->whereHas('partnerMedia', function ($query) {
|
||||
$query->whereHas('company', function ($subQuery) {
|
||||
$subQuery->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
return $cooperationMedia
|
||||
&& $cooperationMedia->status === ApprovalStatus::ACCEPTED
|
||||
&& ! $record->proposals()
|
||||
->where('partner_media_id', $cooperationMedia->partner_media_id)
|
||||
->whereIn('status', [
|
||||
ApprovalStatus::ACCEPTED,
|
||||
ApprovalStatus::PENDING,
|
||||
])
|
||||
->exists();
|
||||
})
|
||||
->modalWidth('lg'),
|
||||
Action::make('create_task_assignment')
|
||||
->label('Buat Penugasan')
|
||||
->icon(Heroicon::Plus)
|
||||
->color('primary')
|
||||
->schema([
|
||||
DatePicker::make('start_date')
|
||||
->label('Tanggal Mulai')
|
||||
->placeholder(fn () => now()->translatedFormat('l, d F Y'))
|
||||
->native(false)
|
||||
->displayFormat('l, d F Y')
|
||||
->locale('id')
|
||||
->required(),
|
||||
|
||||
DatePicker::make('end_date')
|
||||
->label('Tanggal Seleisa')
|
||||
->placeholder(fn () => now()->addDays(30)->translatedFormat('l, d F Y'))
|
||||
->native(false)
|
||||
->displayFormat('l, d F Y')
|
||||
->required()
|
||||
->after('start_date'),
|
||||
|
||||
TextInput::make('report_amount')
|
||||
->label('Jumlah Laporan')
|
||||
->placeholder(10)
|
||||
->numeric()
|
||||
->minValue(1)
|
||||
->required(),
|
||||
Textarea::make('task_description')
|
||||
->label('Deskripsi Tugas')
|
||||
->required()
|
||||
->rows(4)
|
||||
->placeholder('...'),
|
||||
])
|
||||
->action(function (array $data, Cooperation $record) {
|
||||
$taskAssignment = $record->taskAssignments()->create([
|
||||
'start_date' => $data['start_date'],
|
||||
'end_date' => $data['end_date'],
|
||||
'task_description' => $data['task_description'],
|
||||
'report_amount' => $data['report_amount'],
|
||||
]);
|
||||
|
||||
$partnerMedias = $record->partnerMedia()
|
||||
->wherePivot('status', ApprovalStatus::ACCEPTED)
|
||||
->get();
|
||||
|
||||
foreach ($partnerMedias as $partnerMedia) {
|
||||
$taskAssignment->mediaTaskAssignments()->create([
|
||||
'partner_media_id' => $partnerMedia->id,
|
||||
'status' => ApprovalStatus::PENDING,
|
||||
]);
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title('Penugasan berhasil dibuat')
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->modalHeading('Buat Penugasan')
|
||||
->modalSubmitActionLabel('Simpan')
|
||||
->visible(
|
||||
fn (Cooperation $record): bool => ! auth()->user()->hasRole('Perusahaan')
|
||||
&& ! $record->taskAssignments()->exists()
|
||||
&& $record->status === CooperationStatus::ASSIGNMENT
|
||||
)
|
||||
->modalWidth('lg'),
|
||||
EditAction::make()
|
||||
->visible(fn () => ! auth()->user()->hasRole('Perusahaan')),
|
||||
DeleteAction::make()
|
||||
->visible(fn () => ! auth()->user()->hasRole('Perusahaan')),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
...DefaultBulkActions::make('Kerja Sama'),
|
||||
]),
|
||||
])
|
||||
->emptyStateIcon(Heroicon::HandRaised)
|
||||
->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.')
|
||||
->defaultSort('created_at', 'desc')
|
||||
->deferFilters(false);
|
||||
}
|
||||
}
|
||||
@ -69,7 +69,7 @@ public static function table(Table $table): Table
|
||||
->label('Jurnalis')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->wrap(),
|
||||
->badge(),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label('Tgl Daftar')
|
||||
|
||||
59
app/Models/Cooperation.php
Normal file
59
app/Models/Cooperation.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\CooperationStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class Cooperation extends Model implements HasMedia
|
||||
{
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'initial_submission_date' => 'date',
|
||||
'final_submission_date' => 'date',
|
||||
'assignment_deadline' => 'date',
|
||||
'verification_deadline' => 'date',
|
||||
'payment_amount' => 'integer',
|
||||
'payment_date' => 'date',
|
||||
'status' => CooperationStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function proposals(): HasMany
|
||||
{
|
||||
return $this->hasMany(CooperationProposal::class);
|
||||
}
|
||||
|
||||
public function taskAssignments(): HasMany
|
||||
{
|
||||
return $this->hasMany(TaskAssignment::class);
|
||||
}
|
||||
|
||||
public function cooperationMedia(): HasMany
|
||||
{
|
||||
return $this->hasMany(CooperationMedia::class);
|
||||
}
|
||||
|
||||
public function partnerMedia(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PartnerMedia::class, 'cooperation_media')
|
||||
->withPivot(['status'])
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function reports(): \Illuminate\Database\Eloquent\Relations\HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(Report::class, TaskAssignment::class);
|
||||
}
|
||||
}
|
||||
40
app/Models/CooperationMedia.php
Normal file
40
app/Models/CooperationMedia.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class CooperationMedia extends Pivot
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'cooperation_media';
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => ApprovalStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function cooperation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Cooperation::class);
|
||||
}
|
||||
|
||||
public function partnerMedia(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PartnerMedia::class);
|
||||
}
|
||||
|
||||
public function rejectionReasons(): MorphMany
|
||||
{
|
||||
return $this->morphMany(RejectionReason::class, 'rejectable');
|
||||
}
|
||||
}
|
||||
40
app/Models/CooperationProposal.php
Normal file
40
app/Models/CooperationProposal.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
class CooperationProposal extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => ApprovalStatus::class,
|
||||
'proposed_start_date' => 'date',
|
||||
'proposed_end_date' => 'date',
|
||||
];
|
||||
}
|
||||
|
||||
public function cooperation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Cooperation::class);
|
||||
}
|
||||
|
||||
public function partnerMedia(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PartnerMedia::class);
|
||||
}
|
||||
|
||||
public function rejectionReasons(): MorphMany
|
||||
{
|
||||
return $this->morphMany(RejectionReason::class, 'rejectable');
|
||||
}
|
||||
}
|
||||
40
app/Models/MediaTaskAssignment.php
Normal file
40
app/Models/MediaTaskAssignment.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class MediaTaskAssignment extends Pivot
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'media_task_assignment';
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => ApprovalStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function taskAssignment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TaskAssignment::class);
|
||||
}
|
||||
|
||||
public function partnerMedia(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PartnerMedia::class);
|
||||
}
|
||||
|
||||
public function rejectionReasons(): MorphMany
|
||||
{
|
||||
return $this->morphMany(RejectionReason::class, 'rejectable');
|
||||
}
|
||||
}
|
||||
@ -41,4 +41,33 @@ public function announcements(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Announcement::class, 'announcement_media');
|
||||
}
|
||||
|
||||
public function cooperationProposals(): HasMany
|
||||
{
|
||||
return $this->hasMany(CooperationProposal::class);
|
||||
}
|
||||
|
||||
public function taskAssignments(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(TaskAssignment::class, 'task_assignment_media')
|
||||
->withPivot(['status'])
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function taskAssignmentMedias(): HasMany
|
||||
{
|
||||
return $this->hasMany(TaskAssignmentMedia::class);
|
||||
}
|
||||
|
||||
public function cooperations(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Cooperation::class, 'cooperation_media')
|
||||
->withPivot(['status'])
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function cooperationMedias(): HasMany
|
||||
{
|
||||
return $this->hasMany(CooperationMedia::class);
|
||||
}
|
||||
}
|
||||
|
||||
19
app/Models/RejectionReason.php
Normal file
19
app/Models/RejectionReason.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class RejectionReason extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
public function rejectable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
37
app/Models/Report.php
Normal file
37
app/Models/Report.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class Report extends Model implements HasMedia
|
||||
{
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'publication_date' => 'date',
|
||||
'status' => ApprovalStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function taskAssignment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TaskAssignment::class);
|
||||
}
|
||||
|
||||
public function rejectionReasons(): MorphMany
|
||||
{
|
||||
return $this->morphMany(RejectionReason::class, 'rejectable');
|
||||
}
|
||||
}
|
||||
52
app/Models/TaskAssignment.php
Normal file
52
app/Models/TaskAssignment.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?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\Relations\MorphMany;
|
||||
|
||||
class TaskAssignment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'start_date' => 'date',
|
||||
'end_date' => 'date',
|
||||
];
|
||||
}
|
||||
|
||||
public function cooperation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Cooperation::class);
|
||||
}
|
||||
|
||||
public function reports(): HasMany
|
||||
{
|
||||
return $this->hasMany(Report::class);
|
||||
}
|
||||
|
||||
public function rejectionReasons(): MorphMany
|
||||
{
|
||||
return $this->morphMany(RejectionReason::class, 'rejectable');
|
||||
}
|
||||
|
||||
public function mediaTaskAssignments(): HasMany
|
||||
{
|
||||
return $this->hasMany(MediaTaskAssignment::class);
|
||||
}
|
||||
|
||||
public function partnerMedia(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PartnerMedia::class, 'media_task_assignment')
|
||||
->withPivot(['status'])
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\CooperationStatus;
|
||||
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('cooperations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title', 200);
|
||||
$table->date('initial_submission_date');
|
||||
$table->date('final_submission_date');
|
||||
$table->date('assignment_deadline')->nullable();
|
||||
$table->date('verification_deadline')->nullable();
|
||||
$table->text('description');
|
||||
$table->unsignedInteger('payment_amount')->nullable();
|
||||
$table->date('payment_date')->nullable();
|
||||
$table->enum('status', [CooperationStatus::values()])->default(CooperationStatus::PENDING)->comment(CooperationStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cooperations');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\Cooperation;
|
||||
use App\Models\PartnerMedia;
|
||||
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('cooperation_media', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(Cooperation::class)->constrained()->cascadeOnDelete();
|
||||
$table->foreignIdFor(PartnerMedia::class)->constrained()->cascadeOnDelete();
|
||||
$table->enum('status', [ApprovalStatus::values()])->default(ApprovalStatus::PENDING)->comment(ApprovalStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cooperation_media');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\Cooperation;
|
||||
use App\Models\PartnerMedia;
|
||||
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('cooperation_proposals', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(Cooperation::class)->constrained()->cascadeOnDelete();
|
||||
$table->foreignIdFor(PartnerMedia::class)->constrained()->cascadeOnDelete();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('e_catalog', 50)->nullable();
|
||||
$table->enum('status', [ApprovalStatus::values()])->default(ApprovalStatus::PENDING)->comment(ApprovalStatus::comment());
|
||||
$table->timestamp('submitted_at')->useCurrent();
|
||||
$table->timestamp('responded_at')->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cooperation_proposals');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Cooperation;
|
||||
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('task_assignments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(Cooperation::class)->constrained()->cascadeOnDelete();
|
||||
$table->date('start_date');
|
||||
$table->date('end_date');
|
||||
$table->text('task_description');
|
||||
$table->unsignedInteger('report_amount');
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('task_assignments');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\PartnerMedia;
|
||||
use App\Models\TaskAssignment;
|
||||
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('media_task_assignment', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(TaskAssignment::class)->constrained()->cascadeOnDelete();
|
||||
$table->foreignIdFor(PartnerMedia::class)->constrained()->cascadeOnDelete();
|
||||
$table->enum('status', [ApprovalStatus::values()])->default(ApprovalStatus::PENDING)->comment(ApprovalStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('media_task_assignment');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Models\TaskAssignment;
|
||||
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('reports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(TaskAssignment::class)->constrained()->cascadeOnDelete();
|
||||
$table->string('title', 200);
|
||||
$table->date('publication_date');
|
||||
$table->string('link', 50);
|
||||
$table->text('description');
|
||||
$table->enum('status', [ApprovalStatus::values()])->default(ApprovalStatus::PENDING)->comment(ApprovalStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('reports');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,30 @@
|
||||
<?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('rejection_reasons', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('rejectable'); // Creates rejectable_type and rejectable_id
|
||||
$table->text('reason');
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('rejection_reasons');
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user