193 lines
7.0 KiB
PHP
193 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources;
|
|
|
|
use App\Filament\Resources\EmotionResource\Pages;
|
|
use App\Models\Emotion;
|
|
use Filament\Schemas\Schema as Schema;
|
|
use Filament\Schemas\Components as SC;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
|
|
class EmotionResource extends Resource
|
|
{
|
|
protected static ?string $model = Emotion::class;
|
|
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-face-smile';
|
|
protected static string|\UnitEnum|null $navigationGroup = 'Library';
|
|
protected static ?int $navigationSort = 10;
|
|
|
|
public static function form(Schema $schema): Schema
|
|
{
|
|
return $schema->components([
|
|
SC\KeyValue::make('name')->label('Name (de/en)')->keyLabel('locale')->valueLabel('value')->default(['de' => '', 'en' => ''])->required(),
|
|
SC\TextInput::make('icon')->label('Icon/Emoji')->maxLength(50),
|
|
SC\TextInput::make('color')->maxLength(7)->helperText('#RRGGBB'),
|
|
SC\KeyValue::make('description')->label('Description (de/en)')->keyLabel('locale')->valueLabel('value'),
|
|
SC\TextInput::make('sort_order')->numeric()->default(0),
|
|
SC\Toggle::make('is_active')->default(true),
|
|
SC\Select::make('eventTypes')
|
|
->label('Event Types')
|
|
->multiple()
|
|
->searchable()
|
|
->preload()
|
|
->relationship('eventTypes', 'name'),
|
|
])->columns(2);
|
|
}
|
|
|
|
public static function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->columns([
|
|
Tables\Columns\TextColumn::make('id')->sortable(),
|
|
Tables\Columns\TextColumn::make('name')->searchable(),
|
|
Tables\Columns\TextColumn::make('icon'),
|
|
Tables\Columns\TextColumn::make('color'),
|
|
Tables\Columns\IconColumn::make('is_active')->boolean(),
|
|
Tables\Columns\TextColumn::make('sort_order')->sortable(),
|
|
])
|
|
->filters([])
|
|
->actions([
|
|
Tables\Actions\EditAction::make(),
|
|
])
|
|
->bulkActions([
|
|
Tables\Actions\BulkActionGroup::make([
|
|
Tables\Actions\DeleteBulkAction::make(),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function getPages(): array
|
|
{
|
|
return [
|
|
'index' => Pages\ManageEmotions::route('/'),
|
|
'import' => Pages\ImportEmotions::route('/import'),
|
|
];
|
|
}
|
|
}
|
|
|
|
namespace App\Filament\Resources\EmotionResource\Pages;
|
|
|
|
use App\Filament\Resources\EmotionResource;
|
|
use Filament\Resources\Pages\ManageRecords;
|
|
use Filament\Actions;
|
|
use Filament\Resources\Pages\Page;
|
|
use Filament\Forms;
|
|
use Filament\Notifications\Notification;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class ManageEmotions extends ManageRecords
|
|
{
|
|
protected static string $resource = EmotionResource::class;
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
Actions\Action::make('import')
|
|
->label('Import CSV')
|
|
->icon('heroicon-o-arrow-up-tray')
|
|
->url(EmotionResource::getUrl('import')),
|
|
Actions\Action::make('template')
|
|
->label('Download CSV Template')
|
|
->icon('heroicon-o-document-arrow-down')
|
|
->url(url('/super-admin/templates/emotions.csv')),
|
|
];
|
|
}
|
|
}
|
|
|
|
class ImportEmotions extends Page
|
|
{
|
|
protected static string $resource = EmotionResource::class;
|
|
protected string $view = 'filament.pages.blank';
|
|
protected ?string $heading = 'Import Emotions (CSV)';
|
|
|
|
public ?string $file = null;
|
|
|
|
protected function getFormSchema(): array
|
|
{
|
|
return [
|
|
Forms\Components\FileUpload::make('file')
|
|
->label('CSV file')
|
|
->acceptedFileTypes(['text/csv', 'text/plain'])
|
|
->directory('imports')
|
|
->required(),
|
|
];
|
|
}
|
|
|
|
protected function getFormActions(): array
|
|
{
|
|
return [
|
|
Forms\Components\Actions\Action::make('import')
|
|
->label('Import')
|
|
->action('doImport')
|
|
->color('primary')
|
|
];
|
|
}
|
|
|
|
public function doImport(): void
|
|
{
|
|
$state = $this->form->getState();
|
|
$path = $state['file'] ?? null;
|
|
if (! $path || ! Storage::disk('public')->exists($path)) {
|
|
Notification::make()->danger()->title('File not found')->send();
|
|
return;
|
|
}
|
|
$full = Storage::disk('public')->path($path);
|
|
[$ok, $fail] = $this->importEmotionsCsv($full);
|
|
Notification::make()->success()->title("Imported {$ok} rows")->body($fail ? "{$fail} failed" : null)->send();
|
|
}
|
|
|
|
private function importEmotionsCsv(string $file): array
|
|
{
|
|
$h = fopen($file, 'r');
|
|
if (! $h) return [0,0];
|
|
$ok = 0; $fail = 0;
|
|
// Expected headers: name_de,name_en,icon,color,description_de,description_en,sort_order,is_active,event_types
|
|
$headers = fgetcsv($h, 0, ',');
|
|
if (! $headers) return [0,0];
|
|
$map = array_flip($headers);
|
|
while (($row = fgetcsv($h, 0, ',')) !== false) {
|
|
try {
|
|
$nameDe = trim($row[$map['name_de']] ?? '');
|
|
$nameEn = trim($row[$map['name_en']] ?? '');
|
|
$name = $nameDe ?: $nameEn;
|
|
if ($name === '') { $fail++; continue; }
|
|
$data = [
|
|
'name' => ['de' => $nameDe, 'en' => $nameEn],
|
|
'icon' => $row[$map['icon']] ?? null,
|
|
'color' => $row[$map['color']] ?? null,
|
|
'description' => [
|
|
'de' => $row[$map['description_de']] ?? null,
|
|
'en' => $row[$map['description_en']] ?? null,
|
|
],
|
|
'sort_order' => (int)($row[$map['sort_order']] ?? 0),
|
|
'is_active' => (int)($row[$map['is_active']] ?? 1) ? 1 : 0,
|
|
];
|
|
$id = DB::table('emotions')->insertGetId(array_merge($data, [
|
|
'created_at' => now(), 'updated_at' => now(),
|
|
]));
|
|
// Attach event types if provided (by slug list separated by '|')
|
|
$et = $row[$map['event_types']] ?? '';
|
|
if ($et) {
|
|
$slugs = array_filter(array_map('trim', explode('|', $et)));
|
|
if ($slugs) {
|
|
$ids = DB::table('event_types')->whereIn('slug', $slugs)->pluck('id')->all();
|
|
foreach ($ids as $eid) {
|
|
DB::table('emotion_event_type')->insertOrIgnore([
|
|
'emotion_id' => $id,
|
|
'event_type_id' => $eid,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
$ok++;
|
|
} catch (\Throwable $e) {
|
|
$fail++;
|
|
}
|
|
}
|
|
fclose($h);
|
|
return [$ok, $fail];
|
|
}
|
|
}
|