diff --git a/app/Filament/Exports/ContentRecapExcelExport.php b/app/Filament/Exports/ContentRecapExcelExport.php new file mode 100644 index 0000000..2201dc8 --- /dev/null +++ b/app/Filament/Exports/ContentRecapExcelExport.php @@ -0,0 +1,95 @@ +withFilename(fn ($resource) => $resource::getModelLabel().'-'.date('Y-m-d')); + + $this->withColumns([ + Column::make('title') + ->heading('Judul'), + + Column::make('type') + ->heading('Tipe') + ->formatStateUsing(fn ($state) => $state?->getLabel()), + + Column::make('channel') + ->heading('Kanal') + ->formatStateUsing(fn ($state) => Channel::tryFrom($state)?->getLabel() ?? $state), + + Column::make('social_media') + ->heading('Media Sosial') + ->formatStateUsing(fn ($state) => SocialMedia::tryFrom($state)?->getLabel() ?? $state), + + Column::make('classification.name') + ->heading('Klasifikasi'), + + Column::make('link') + ->heading('Tautan'), + + Column::make('posting_date') + ->heading('Tgl Posting') + ->formatStateUsing(fn ($state) => $state?->translatedFormat('l, d F Y')), + ]); + } + + public function headings(): array + { + return [ + ['Data Rekap Konten'], + $this->getHeadings(), + ]; + } + + public function styles(Worksheet $sheet) + { + return [ + 1 => [ + 'font' => ['bold' => true, 'size' => 14], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER], + ], + 2 => [ + 'font' => ['bold' => true], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], + ], + ]; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function (AfterSheet $event) { + $sheet = $event->sheet->getDelegate(); + $lastColumn = 'G'; // A-G + + $sheet->mergeCells("A1:{$lastColumn}1"); + + $sheet->getStyle("A2:{$lastColumn}".$sheet->getHighestRow()) + ->getAlignment() + ->setVertical(Alignment::VERTICAL_TOP) + ->setWrapText(true); + + for ($i = 3; $i <= $sheet->getHighestRow(); $i++) { + $sheet->getRowDimension($i)->setRowHeight(-1); + } + + foreach (range('A', $lastColumn) as $columnID) { + $sheet->getColumnDimension($columnID)->setAutoSize(true); + } + }, + ]; + } +} diff --git a/app/Filament/Exports/CooperationExcelExport.php b/app/Filament/Exports/CooperationExcelExport.php new file mode 100644 index 0000000..be54fc5 --- /dev/null +++ b/app/Filament/Exports/CooperationExcelExport.php @@ -0,0 +1,98 @@ +withFilename(fn ($resource) => $resource::getModelLabel().'-'.date('Y-m-d')); + + $this->withColumns([ + Column::make('title') + ->heading('Judul'), + + Column::make('partnerMedia.name') + ->heading('Media') + ->getStateUsing(fn ($record) => $record->partnerMedia->pluck('name')->join(', ')), + + Column::make('initial_submission_date') + ->heading('Tgl Pengajuan Awal') + ->formatStateUsing(fn ($state) => $state?->translatedFormat('l, d F Y')), + + Column::make('final_submission_date') + ->heading('Tgl Pengajuan Akhir') + ->formatStateUsing(fn ($state) => $state?->translatedFormat('l, d F Y')), + + Column::make('status') + ->heading('Status') + ->formatStateUsing(fn ($state) => $state?->getLabel()), + + Column::make('payment_amount') + ->heading('Jumlah Pembayaran') + ->formatStateUsing(fn ($state) => 'Rp '.number_format($state, 0, ',', '.')), + + Column::make('payment_date') + ->heading('Tanggal Pembayaran') + ->formatStateUsing(fn ($state) => $state?->translatedFormat('l, d F Y')), + + Column::make('description') + ->heading('Deskripsi'), + ]); + } + + public function headings(): array + { + return [ + ['Data Kerja Sama'], + $this->getHeadings(), + ]; + } + + public function styles(Worksheet $sheet) + { + return [ + 1 => [ + 'font' => ['bold' => true, 'size' => 14], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER], + ], + 2 => [ + 'font' => ['bold' => true], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], + ], + ]; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function (AfterSheet $event) { + $sheet = $event->sheet->getDelegate(); + $lastColumn = 'H'; // A-H + + $sheet->mergeCells("A1:{$lastColumn}1"); + + $sheet->getStyle("A2:{$lastColumn}".$sheet->getHighestRow()) + ->getAlignment() + ->setVertical(Alignment::VERTICAL_TOP) + ->setWrapText(true); + + for ($i = 3; $i <= $sheet->getHighestRow(); $i++) { + $sheet->getRowDimension($i)->setRowHeight(-1); + } + + foreach (range('A', $lastColumn) as $columnID) { + $sheet->getColumnDimension($columnID)->setAutoSize(true); + } + }, + ]; + } +} diff --git a/app/Filament/Exports/IssueManagementExcelExport.php b/app/Filament/Exports/IssueManagementExcelExport.php new file mode 100644 index 0000000..7f95d74 --- /dev/null +++ b/app/Filament/Exports/IssueManagementExcelExport.php @@ -0,0 +1,98 @@ +withFilename(fn ($resource) => $resource::getModelLabel().'-'.date('Y-m-d')); + + $this->withColumns([ + Column::make('mediaMonitoring.title') + ->heading('Judul Berita'), + + Column::make('issue') + ->heading('Isu') + ->formatStateUsing(fn ($state) => $state?->getLabel()), + + Column::make('response') + ->heading('Respon') + ->formatStateUsing(fn ($state) => $state?->getLabel()), + + Column::make('location.name') + ->heading('Lokus'), + + Column::make('subLocation.name') + ->heading('Sub Lokus'), + + Column::make('classification.name') + ->heading('Klasifikasi'), + + Column::make('subClassification.name') + ->heading('Sub Klasifikasi'), + + Column::make('description') + ->heading('Keterangan'), + + Column::make('mediaMonitoring.release_date') + ->heading('Tgl Rilis') + ->formatStateUsing(fn ($state) => $state?->translatedFormat('l, d F Y')), + ]); + } + + public function headings(): array + { + return [ + ['Data Manajemen Isu'], + $this->getHeadings(), + ]; + } + + public function styles(Worksheet $sheet) + { + return [ + 1 => [ + 'font' => ['bold' => true, 'size' => 14], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER], + ], + 2 => [ + 'font' => ['bold' => true], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], + ], + ]; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function (AfterSheet $event) { + $sheet = $event->sheet->getDelegate(); + $lastColumn = 'I'; // A-I + + $sheet->mergeCells("A1:{$lastColumn}1"); + + $sheet->getStyle("A2:{$lastColumn}".$sheet->getHighestRow()) + ->getAlignment() + ->setVertical(Alignment::VERTICAL_TOP) + ->setWrapText(true); + + for ($i = 3; $i <= $sheet->getHighestRow(); $i++) { + $sheet->getRowDimension($i)->setRowHeight(-1); + } + + foreach (range('A', $lastColumn) as $columnID) { + $sheet->getColumnDimension($columnID)->setAutoSize(true); + } + }, + ]; + } +} diff --git a/app/Filament/Exports/MediaMonitoringExcelExport.php b/app/Filament/Exports/MediaMonitoringExcelExport.php new file mode 100644 index 0000000..3a280ce --- /dev/null +++ b/app/Filament/Exports/MediaMonitoringExcelExport.php @@ -0,0 +1,109 @@ +withFilename(fn ($resource) => $resource::getModelLabel().'-'.date('Y-m-d')); + + $this->withColumns([ + Column::make('code') + ->heading('Kode'), + + Column::make('media_name') + ->heading('Nama Media'), + + Column::make('title') + ->heading('Judul'), + + Column::make('channel') + ->heading('Kanal') + ->formatStateUsing(fn ($state) => Channel::tryFrom($state)?->getLabel() ?? $state), + + Column::make('writter') + ->heading('Penulis'), + + Column::make('link') + ->heading('Tautan'), + + Column::make('news_page') + ->heading('Halaman Berita'), + + Column::make('influencer') + ->heading('Influencer'), + + Column::make('keyword') + ->heading('Kata Kunci'), + + Column::make('themes') + ->heading('Tema') + ->getStateUsing(fn ($record) => $record->themes->pluck('name')->join(', ')), + + Column::make('release_date') + ->heading('Tgl Rilis'), + ]); + } + + public function headings(): array + { + return [ + ['Data Monitoring Media'], + $this->getHeadings(), + ]; + } + + public function styles(Worksheet $sheet) + { + return [ + 1 => [ + 'font' => ['bold' => true, 'size' => 14], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER], + ], + 2 => [ + 'font' => ['bold' => true], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], + ], + ]; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function (AfterSheet $event) { + $sheet = $event->sheet->getDelegate(); + $lastColumn = 'K'; // Based on column count (A-K) + + // Merge Title Row + // Merge Title Row + $sheet->mergeCells("A1:{$lastColumn}1"); + + // Alignment and Wrap Text + $sheet->getStyle("A2:{$lastColumn}".$sheet->getHighestRow()) + ->getAlignment() + ->setVertical(Alignment::VERTICAL_TOP) + ->setWrapText(true); + + // Ensure data rows auto-expand to fit wrapped text ("jadi 2 row") + // We start from row 3 (after Title and Header) + for ($i = 3; $i <= $sheet->getHighestRow(); $i++) { + $sheet->getRowDimension($i)->setRowHeight(-1); + } + + foreach (range('A', $lastColumn) as $columnID) { + $sheet->getColumnDimension($columnID)->setAutoSize(true); + } + }, + ]; + } +} diff --git a/app/Filament/Resources/Manage/Cooperations/Tables/CooperationsTable.php b/app/Filament/Resources/Manage/Cooperations/Tables/CooperationsTable.php index 0e3a610..3ad1031 100644 --- a/app/Filament/Resources/Manage/Cooperations/Tables/CooperationsTable.php +++ b/app/Filament/Resources/Manage/Cooperations/Tables/CooperationsTable.php @@ -3,11 +3,13 @@ namespace App\Filament\Resources\Manage\Cooperations\Tables; use App\Enums\ApprovalStatus; +use App\Enums\CooperationStatus; use App\Enums\RoleEnum; use App\Filament\Actions\Cheerful\DeleteAction; use App\Filament\Actions\Cheerful\EditAction; use App\Filament\Actions\DefaultBulkActions; use App\Filament\Columns\TimestampColumns; +use App\Filament\Exports\CooperationExcelExport; use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\AcceptAction; use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\CompleteCooperationAction; use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\CreateTaskAssignmentAction; @@ -18,11 +20,18 @@ use Carbon\CarbonInterface; use Filament\Actions\BulkActionGroup; use Filament\Actions\ViewAction; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\TextInput; +use Filament\Schemas\Components\Utilities\Get; use Filament\Support\Icons\Heroicon; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\Filter; +use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; +use pxlrbt\FilamentExcel\Actions\Tables\ExportAction; class CooperationsTable { @@ -133,6 +142,102 @@ public static function configure(Table $table): Table ]) ->filters([ TrashedFilter::make()->native(false), + + SelectFilter::make('status') + ->label('Status') + ->options(CooperationStatus::class), + + SelectFilter::make('partnerMedia') + ->label('Media') + ->relationship('partnerMedia', 'name') + ->searchable() + ->preload(), + + Filter::make('title') + ->schema([ + TextInput::make('title') + ->label('Nama Kegiatan') + ->placeholder('Cari nama kegiatan...'), + ]) + ->query(function (Builder $query, array $data): Builder { + return $query->when( + $data['title'], + fn (Builder $query, $title): Builder => $query->where('title', 'like', "%{$title}%"), + ); + }), + + Filter::make('date_filter') + ->schema([ + Select::make('date_type') + ->label('Jenis Filter Tanggal') + ->options([ + 'date' => 'Tanggal Penyerahan Awal', + 'month' => 'Bulan & Tahun', + 'year' => 'Tahun', + 'range' => 'Rentang Tanggal', + ]) + ->default('date') + ->live(), + + DatePicker::make('specific_date') + ->label('Tanggal') + ->visible(fn (Get $get) => $get('date_type') === 'date'), + + Select::make('month') + ->label('Bulan') + ->options([ + 1 => 'Januari', + 2 => 'Februari', + 3 => 'Maret', + 4 => 'April', + 5 => 'Mei', + 6 => 'Juni', + 7 => 'Juli', + 8 => 'Agustus', + 9 => 'September', + 10 => 'Oktober', + 11 => 'November', + 12 => 'Desember', + ]) + ->visible(fn (Get $get) => $get('date_type') === 'month'), + + Select::make('year') + ->label('Tahun') + ->options(function () { + $years = range(date('Y'), 2020); + + return array_combine($years, $years); + }) + ->visible(fn (Get $get) => in_array($get('date_type'), ['month', 'year'])), + + DatePicker::make('start_date') + ->label('Mulai Tanggal') + ->visible(fn (Get $get) => $get('date_type') === 'range'), + + DatePicker::make('end_date') + ->label('Sampai Tanggal') + ->visible(fn (Get $get) => $get('date_type') === 'range'), + ]) + ->query(function (Builder $query, array $data): Builder { + $type = $data['date_type'] ?? null; + $column = 'initial_submission_date'; + + if ($type === 'date' && ! empty($data['specific_date'])) { + return $query->whereDate($column, $data['specific_date']); + } + if ($type === 'month' && ! empty($data['month']) && ! empty($data['year'])) { + return $query->whereMonth($column, $data['month']) + ->whereYear($column, $data['year']); + } + if ($type === 'year' && ! empty($data['year'])) { + return $query->whereYear($column, $data['year']); + } + if ($type === 'range' && ! empty($data['start_date']) && ! empty($data['end_date'])) { + return $query->whereBetween($column, [$data['start_date'], $data['end_date']]); + } + + return $query; + }), ]) ->recordActions([ ViewAction::make(), @@ -155,6 +260,14 @@ public static function configure(Table $table): Table DeleteAction::make() ->visible(fn (): bool => ! auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)), ]) + ->headerActions([ + ExportAction::make() + ->exports([ + CooperationExcelExport::make(), + ]) + ->color('success'), + + ]) ->toolbarActions([ BulkActionGroup::make([ ...DefaultBulkActions::make('Kerja Sama'), diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php b/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php index 5956ae4..c1058d3 100644 --- a/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php @@ -2,17 +2,29 @@ namespace App\Filament\Resources\Monitoring\ContentRecaps\Tables; +use App\Enums\Channel; +use App\Enums\ContentType; +use App\Enums\SocialMedia; use App\Filament\Actions\Cheerful\DeleteAction; use App\Filament\Actions\Cheerful\EditAction; use App\Filament\Actions\Cheerful\ForceDeleteAction; use App\Filament\Actions\Cheerful\RestoreAction; use App\Filament\Actions\DefaultBulkActions; use App\Filament\Columns\TimestampColumns; +use App\Filament\Exports\ContentRecapExcelExport; use Filament\Actions\BulkActionGroup; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; +use Filament\Schemas\Components\Utilities\Get; use Filament\Support\Icons\Heroicon; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Enums\FiltersLayout; +use Filament\Tables\Filters\Filter; +use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; +use Illuminate\Database\Eloquent\Builder; +use pxlrbt\FilamentExcel\Actions\Tables\ExportAction; class ContentRecapsTable { @@ -61,7 +73,114 @@ public static function configure(Table $table): Table ]) ->filters([ TrashedFilter::make()->native(false), - ]) + + SelectFilter::make('social_media') + ->label('Media Sosial') + ->options(SocialMedia::class) + ->native(false), + + SelectFilter::make('classification') + ->label('Klasifikasi') + ->relationship('classification', 'name') + ->searchable() + ->preload() + ->native(false), + + SelectFilter::make('type') + ->label('Jenis Konten') + ->options(ContentType::class) + ->native(false), + + SelectFilter::make('channel') + ->label('Kanal') + ->options(Channel::class) + ->native(false), + + Filter::make('date_filter') + ->schema([ + Select::make('date_type') + ->label('Jenis Filter Tanggal') + ->options([ + 'date' => 'Tanggal Posting', + 'month' => 'Bulan & Tahun', + 'year' => 'Tahun', + 'range' => 'Rentang Tanggal', + ]) + ->default('date') + ->live() + ->native(false), + + DatePicker::make('specific_date') + ->label('Tanggal') + ->placeholder('Pilih tanggal') + ->visible(fn (Get $get) => $get('date_type') === 'date') + ->native(false), + + Select::make('month') + ->label('Bulan') + ->options([ + 1 => 'Januari', + 2 => 'Februari', + 3 => 'Maret', + 4 => 'April', + 5 => 'Mei', + 6 => 'Juni', + 7 => 'Juli', + 8 => 'Agustus', + 9 => 'September', + 10 => 'Oktober', + 11 => 'November', + 12 => 'Desember', + ]) + ->visible(fn (Get $get) => $get('date_type') === 'month') + ->native(false), + + Select::make('year') + ->label('Tahun') + ->options(function () { + $years = range(date('Y'), 2020); + + return array_combine($years, $years); + }) + ->visible(fn (Get $get) => in_array($get('date_type'), ['month', 'year'])) + ->native(false), + + DatePicker::make('start_date') + ->label('Tanggal Mulai') + ->placeholder('Pilih tanggal mulai') + ->visible(fn (Get $get) => $get('date_type') === 'range') + ->native(false) + ->displayFormat('l, d F Y'), + + DatePicker::make('end_date') + ->label('Tanggal Selesai') + ->placeholder('Pilih tanggal selesai') + ->visible(fn (Get $get) => $get('date_type') === 'range') + ->native(false) + ->displayFormat('l, d F Y'), + ]) + ->query(function (Builder $query, array $data): Builder { + $type = $data['date_type'] ?? null; + $column = 'posting_date'; + + if ($type === 'date' && ! empty($data['specific_date'])) { + return $query->whereDate($column, $data['specific_date']); + } + if ($type === 'month' && ! empty($data['month']) && ! empty($data['year'])) { + return $query->whereMonth($column, $data['month']) + ->whereYear($column, $data['year']); + } + if ($type === 'year' && ! empty($data['year'])) { + return $query->whereYear($column, $data['year']); + } + if ($type === 'range' && ! empty($data['start_date']) && ! empty($data['end_date'])) { + return $query->whereBetween($column, [$data['start_date'], $data['end_date']]); + } + + return $query; + }), + ], FiltersLayout::AboveContentCollapsible) + ->filtersFormColumns(3) ->recordActions([ EditAction::make(), @@ -71,6 +190,15 @@ public static function configure(Table $table): Table RestoreAction::make(), ]) + ->headerActions([ + ExportAction::make() + ->exports([ + ContentRecapExcelExport::make() + ->fromTable(), + ]) + ->color('success'), + + ]) ->toolbarActions([ BulkActionGroup::make([ ...DefaultBulkActions::make('Rekap Konten'), diff --git a/app/Filament/Resources/Monitoring/IssueManagements/Tables/IssueManagementTable.php b/app/Filament/Resources/Monitoring/IssueManagements/Tables/IssueManagementTable.php index 74b106f..d517086 100644 --- a/app/Filament/Resources/Monitoring/IssueManagements/Tables/IssueManagementTable.php +++ b/app/Filament/Resources/Monitoring/IssueManagements/Tables/IssueManagementTable.php @@ -9,12 +9,20 @@ use App\Filament\Actions\Cheerful\RestoreAction; use App\Filament\Actions\DefaultBulkActions; use App\Filament\Columns\TimestampColumns; +use App\Filament\Exports\IssueManagementExcelExport; use Filament\Actions\BulkActionGroup; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; +use Filament\Schemas\Components\Utilities\Get; use Filament\Support\Icons\Heroicon; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Enums\FiltersLayout; +use Filament\Tables\Filters\Filter; use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; +use Illuminate\Database\Eloquent\Builder; +use pxlrbt\FilamentExcel\Actions\Tables\ExportAction; class IssueManagementTable { @@ -76,7 +84,121 @@ public static function configure(Table $table): Table ->label('Filter Respon') ->options(IssueSentiment::class) ->native(false), - ]) + + SelectFilter::make('location') + ->label('Lokus') + ->relationship('location', 'name') + ->searchable() + ->preload() + ->native(false), + + SelectFilter::make('subLocation') + ->label('Sub Lokus') + ->relationship('subLocation', 'name') + ->searchable() + ->preload() + ->native(false), + + SelectFilter::make('classification') + ->label('Klasifikasi') + ->relationship('classification', 'name') + ->searchable() + ->preload() + ->native(false), + + SelectFilter::make('subClassification') + ->label('Sub Klasifikasi') + ->relationship('subClassification', 'name') + ->searchable() + ->preload() + ->native(false), + + Filter::make('date_filter') + ->schema([ + Select::make('date_type') + ->label('Jenis Filter Tanggal') + ->options([ + 'date' => 'Tanggal Rilis Berita', + 'month' => 'Bulan & Tahun', + 'year' => 'Tahun', + 'range' => 'Rentang Tanggal', + ]) + ->default('date') + ->live() + ->native(false), + + DatePicker::make('specific_date') + ->label('Tanggal') + ->placeholder('Pilih tanggal') + ->visible(fn (Get $get) => $get('date_type') === 'date') + ->native(false), + + Select::make('month') + ->label('Bulan') + ->options([ + 1 => 'Januari', + 2 => 'Februari', + 3 => 'Maret', + 4 => 'April', + 5 => 'Mei', + 6 => 'Juni', + 7 => 'Juli', + 8 => 'Agustus', + 9 => 'September', + 10 => 'Oktober', + 11 => 'November', + 12 => 'Desember', + ]) + ->visible(fn (Get $get) => $get('date_type') === 'month') + ->native(false), + + Select::make('year') + ->label('Tahun') + ->options(function () { + $years = range(date('Y'), 2020); + + return array_combine($years, $years); + }) + ->visible(fn (Get $get) => in_array($get('date_type'), ['month', 'year'])) + ->native(false), + + DatePicker::make('start_date') + ->label('Tanggal Mulai') + ->placeholder('Pilih tanggal mulai') + ->visible(fn (Get $get) => $get('date_type') === 'range') + ->native(false) + ->displayFormat('l, d F Y'), + + DatePicker::make('end_date') + ->label('Tanggal Selesai') + ->placeholder('Pilih tanggal selesai') + ->visible(fn (Get $get) => $get('date_type') === 'range') + ->native(false) + ->displayFormat('l, d F Y'), + ]) + ->query(function (Builder $query, array $data): Builder { + $type = $data['date_type'] ?? null; + + // Filtering based on mediaMonitoring.release_date relationship + $query->whereHas('mediaMonitoring', function (Builder $query) use ($type, $data) { + $column = 'release_date'; + + if ($type === 'date' && ! empty($data['specific_date'])) { + $query->whereDate($column, $data['specific_date']); + } elseif ($type === 'month' && ! empty($data['month']) && ! empty($data['year'])) { + $query->whereMonth($column, $data['month']) + ->whereYear($column, $data['year']); + } elseif ($type === 'year' && ! empty($data['year'])) { + $query->whereYear($column, $data['year']); + } elseif ($type === 'range' && ! empty($data['start_date']) && ! empty($data['end_date'])) { + $query->whereBetween($column, [$data['start_date'], $data['end_date']]); + } + }); + + return $query; + }), + ], FiltersLayout::AboveContentCollapsible) + ->filtersFormColumns(3) ->recordActions([ EditAction::make(), @@ -86,6 +208,13 @@ public static function configure(Table $table): Table ForceDeleteAction::make(), ]) + ->headerActions([ + ExportAction::make() + ->exports([ + IssueManagementExcelExport::make(), + ]) + ->color('success'), + ]) ->toolbarActions([ BulkActionGroup::make([ ...DefaultBulkActions::make('Manajemen Isu'), diff --git a/app/Filament/Resources/Monitoring/MediaMonitorings/Actions/CrawlNewsAction.php b/app/Filament/Resources/Monitoring/MediaMonitorings/Actions/CrawlNewsAction.php index 317f9bc..d017a2d 100644 --- a/app/Filament/Resources/Monitoring/MediaMonitorings/Actions/CrawlNewsAction.php +++ b/app/Filament/Resources/Monitoring/MediaMonitorings/Actions/CrawlNewsAction.php @@ -19,7 +19,7 @@ protected function setUp(): void $this->label('Crawl Berita') ->icon(Heroicon::MagnifyingGlass) - ->color('info') + ->color('slate') ->schema([ TextInput::make('keyword') ->label('Kata Kunci') diff --git a/app/Filament/Resources/Monitoring/MediaMonitorings/Tables/MediaMonitoringsTable.php b/app/Filament/Resources/Monitoring/MediaMonitorings/Tables/MediaMonitoringsTable.php index ae75eda..3d3e348 100644 --- a/app/Filament/Resources/Monitoring/MediaMonitorings/Tables/MediaMonitoringsTable.php +++ b/app/Filament/Resources/Monitoring/MediaMonitorings/Tables/MediaMonitoringsTable.php @@ -2,18 +2,29 @@ namespace App\Filament\Resources\Monitoring\MediaMonitorings\Tables; +use App\Enums\Channel; use App\Filament\Actions\Cheerful\DeleteAction; use App\Filament\Actions\Cheerful\EditAction; use App\Filament\Actions\Cheerful\ForceDeleteAction; use App\Filament\Actions\Cheerful\RestoreAction; use App\Filament\Actions\DefaultBulkActions; use App\Filament\Columns\TimestampColumns; +use App\Filament\Exports\MediaMonitoringExcelExport; use App\Filament\Resources\Monitoring\MediaMonitorings\Actions\CrawlNewsAction; use Filament\Actions\BulkActionGroup; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\TextInput; +use Filament\Schemas\Components\Utilities\Get; use Filament\Support\Icons\Heroicon; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Enums\FiltersLayout; +use Filament\Tables\Filters\Filter; +use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; +use Illuminate\Database\Eloquent\Builder; +use pxlrbt\FilamentExcel\Actions\Tables\ExportAction; class MediaMonitoringsTable { @@ -86,7 +97,117 @@ public static function configure(Table $table): Table ]) ->filters([ TrashedFilter::make()->native(false), - ]) + + SelectFilter::make('themes') + ->label('Tema') + ->relationship('themes', 'name') + ->searchable() + ->preload() + ->native(false), + + SelectFilter::make('channel') + ->label('Kanal') + ->options(Channel::class) + ->native(false), + + Filter::make('writter') + ->form([ + TextInput::make('writter') + ->label('Penulis') + ->placeholder('Cari penulis...'), + ]) + ->query(function (Builder $query, array $data): Builder { + return $query->when( + $data['writter'], + fn (Builder $query, $term): Builder => $query->where('writter', 'like', "%{$term}%"), + ); + }), + + Filter::make('date_filter') + ->schema([ + Select::make('date_type') + ->label('Jenis Filter Tanggal') + ->options([ + 'date' => 'Tanggal Rilis', + 'month' => 'Bulan & Tahun', + 'year' => 'Tahun', + 'range' => 'Rentang Tanggal', + ]) + ->default('date') + ->live() + ->native(false), + + DatePicker::make('specific_date') + ->label('Tanggal') + ->placeholder('Pilih tanggal') + ->visible(fn (Get $get) => $get('date_type') === 'date') + ->native(false), + + Select::make('month') + ->label('Bulan') + ->options([ + 1 => 'Januari', + 2 => 'Februari', + 3 => 'Maret', + 4 => 'April', + 5 => 'Mei', + 6 => 'Juni', + 7 => 'Juli', + 8 => 'Agustus', + 9 => 'September', + 10 => 'Oktober', + 11 => 'November', + 12 => 'Desember', + ]) + ->visible(fn (Get $get) => $get('date_type') === 'month') + ->native(false), + + Select::make('year') + ->label('Tahun') + ->options(function () { + $years = range(date('Y'), 2020); + + return array_combine($years, $years); + }) + ->visible(fn (Get $get) => in_array($get('date_type'), ['month', 'year'])) + ->native(false), + + DatePicker::make('start_date') + ->label('Tanggal Mulai') + ->placeholder('Pilih tanggal mulai') + ->visible(fn (Get $get) => $get('date_type') === 'range') + ->native(false) + ->displayFormat('l, d F Y'), + + DatePicker::make('end_date') + ->label('Tanggal Selesai') + ->placeholder('Pilih tanggal selesai') + ->visible(fn (Get $get) => $get('date_type') === 'range') + ->native(false) + ->displayFormat('l, d F Y'), + ]) + ->query(function (Builder $query, array $data): Builder { + $type = $data['date_type'] ?? null; + $column = 'release_date'; + + if ($type === 'date' && ! empty($data['specific_date'])) { + return $query->whereDate($column, $data['specific_date']); + } + if ($type === 'month' && ! empty($data['month']) && ! empty($data['year'])) { + return $query->whereMonth($column, $data['month']) + ->whereYear($column, $data['year']); + } + if ($type === 'year' && ! empty($data['year'])) { + return $query->whereYear($column, $data['year']); + } + if ($type === 'range' && ! empty($data['start_date']) && ! empty($data['end_date'])) { + return $query->whereBetween($column, [$data['start_date'], $data['end_date']]); + } + + return $query; + }), + ], FiltersLayout::AboveContentCollapsible) + ->filtersFormColumns(3) ->recordActions([ EditAction::make(), @@ -98,6 +219,12 @@ public static function configure(Table $table): Table ]) ->headerActions([ CrawlNewsAction::make('crawlNews'), + + ExportAction::make() + ->exports([ + MediaMonitoringExcelExport::make(), + ]) + ->color('success'), ]) ->toolbarActions([ BulkActionGroup::make([ diff --git a/app/Models/MediaMonitoring.php b/app/Models/MediaMonitoring.php index 0057061..7bf5e02 100644 --- a/app/Models/MediaMonitoring.php +++ b/app/Models/MediaMonitoring.php @@ -18,6 +18,13 @@ class MediaMonitoring extends Model implements HasMedia protected $guarded = ['id']; + protected function casts(): array + { + return [ + 'release_date' => 'date', + ]; + } + public function issueManagement(): HasOne { return $this->hasOne(IssueManagement::class); diff --git a/composer.json b/composer.json index 0521536..e5fbe36 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", "livewire/livewire": "^3.7", + "pxlrbt/filament-excel": "^3.4", "spatie/laravel-settings": "^3.6", "spatie/laravel-sluggable": "^3.7", "spatie/laravel-tags": "^4.10", diff --git a/composer.lock b/composer.lock index 0f77b74..3f4c705 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "90e78103def2be6a7e1905f567c53f06", + "content-hash": "4f6cfc9fed7cb0e122dae7b27fc317b1", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -938,6 +938,85 @@ ], "time": "2024-07-16T11:13:48+00:00" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, { "name": "composer/semver", "version": "3.4.4", @@ -1541,6 +1620,67 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.19.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + }, + "time": "2025-10-17T16:34:55+00:00" + }, { "name": "filament/actions", "version": "v4.4.0", @@ -4144,6 +4284,87 @@ ], "time": "2025-12-19T02:00:29+00:00" }, + { + "name": "maatwebsite/excel", + "version": "3.1.67", + "source": { + "type": "git", + "url": "https://github.com/SpartnerNL/Laravel-Excel.git", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/e508e34a502a3acc3329b464dad257378a7edb4d", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d", + "shasum": "" + }, + "require": { + "composer/semver": "^3.3", + "ext-json": "*", + "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", + "php": "^7.0||^8.0", + "phpoffice/phpspreadsheet": "^1.30.0", + "psr/simple-cache": "^1.0||^2.0||^3.0" + }, + "require-dev": { + "laravel/scout": "^7.0||^8.0||^9.0||^10.0", + "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0", + "predis/predis": "^1.1" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Excel": "Maatwebsite\\Excel\\Facades\\Excel" + }, + "providers": [ + "Maatwebsite\\Excel\\ExcelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Maatwebsite\\Excel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Patrick Brouwers", + "email": "patrick@spartner.nl" + } + ], + "description": "Supercharged Excel exports and imports in Laravel", + "keywords": [ + "PHPExcel", + "batch", + "csv", + "excel", + "export", + "import", + "laravel", + "php", + "phpspreadsheet" + ], + "support": { + "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.67" + }, + "funding": [ + { + "url": "https://laravel-excel.com/commercial-support", + "type": "custom" + }, + { + "url": "https://github.com/patrickbrouwers", + "type": "github" + } + ], + "time": "2025-08-26T09:13:16+00:00" + }, { "name": "maennchen/zipstream-php", "version": "3.1.2", @@ -4222,6 +4443,113 @@ ], "time": "2025-01-27T12:07:53+00:00" }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, { "name": "masterminds/html5", "version": "2.10.0", @@ -5141,6 +5469,114 @@ }, "time": "2025-11-21T15:09:14+00:00" }, + { + "name": "phpoffice/phpspreadsheet", + "version": "1.30.2", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "09cdde5e2f078b9a3358dd217e2c8cb4dac84be2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/09cdde5e2f078b9a3358dd217e2c8cb4dac84be2", + "reference": "09cdde5e2f078b9a3358dd217e2c8cb4dac84be2", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "ezyang/htmlpurifier": "^4.15", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": ">=7.4.0 <8.5.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "doctrine/instantiator": "^1.5", + "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^8.5 || ^9.0", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.2" + }, + "time": "2026-01-11T05:58:24+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.5", @@ -5873,6 +6309,73 @@ }, "time": "2025-12-17T14:35:46+00:00" }, + { + "name": "pxlrbt/filament-excel", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/pxlrbt/filament-excel.git", + "reference": "1f4c958a8c4aa8386c5e5c96eba956ac307b6e19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pxlrbt/filament-excel/zipball/1f4c958a8c4aa8386c5e5c96eba956ac307b6e19", + "reference": "1f4c958a8c4aa8386c5e5c96eba956ac307b6e19", + "shasum": "" + }, + "require": { + "anourvalar/eloquent-serialize": "^1.2", + "filament/filament": "^4.0|^5.0", + "maatwebsite/excel": "^3.1", + "php": "^8.1" + }, + "require-dev": { + "laravel/pint": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "pxlrbt\\FilamentExcel\\FilamentExcelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "pxlrbt\\FilamentExcel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dennis Koch", + "email": "info@pixelarbeit.de" + } + ], + "description": "Supercharged Excel exports for Filament Resources", + "keywords": [ + "PHPExcel", + "actions", + "filament", + "laravel", + "laravel-filament", + "phpspreadsheet" + ], + "support": { + "issues": "https://github.com/pxlrbt/filament-excel/issues", + "source": "https://github.com/pxlrbt/filament-excel/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/pxlrbt", + "type": "github" + } + ], + "time": "2026-01-16T17:09:19+00:00" + }, { "name": "ralouphie/getallheaders", "version": "3.0.3",