Initialize repo and add session changes (2025-09-08)
This commit is contained in:
203
app/Filament/Resources/TaskResource.php
Normal file
203
app/Filament/Resources/TaskResource.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\TaskResource\Pages;
|
||||
use App\Models\Task;
|
||||
use Filament\Schemas\Schema as Schema;
|
||||
use Filament\Schemas\Components as SC;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class TaskResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Task::class;
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-clipboard-document-check';
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Library';
|
||||
protected static ?int $navigationSort = 30;
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema->components([
|
||||
SC\Select::make('emotion_id')->relationship('emotion', 'name')->required()->searchable()->preload(),
|
||||
SC\Select::make('event_type_id')->relationship('eventType', 'name')->searchable()->preload()->label('Event Type (optional)'),
|
||||
SC\KeyValue::make('title')->label('Title (de/en)')->default(['de' => '', 'en' => ''])->required(),
|
||||
SC\KeyValue::make('description')->label('Description (de/en)'),
|
||||
SC\Select::make('difficulty')->options([
|
||||
'easy' => 'Easy',
|
||||
'medium' => 'Medium',
|
||||
'hard' => 'Hard',
|
||||
])->default('easy'),
|
||||
SC\KeyValue::make('example_text')->label('Example (de/en)'),
|
||||
SC\TextInput::make('sort_order')->numeric()->default(0),
|
||||
SC\Toggle::make('is_active')->default(true),
|
||||
])->columns(2);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('id')->sortable(),
|
||||
Tables\Columns\TextColumn::make('emotion.name')->label('Emotion')->sortable()->searchable(),
|
||||
Tables\Columns\TextColumn::make('eventType.name')->label('Event Type')->toggleable(),
|
||||
Tables\Columns\TextColumn::make('title')->searchable()->limit(40),
|
||||
Tables\Columns\TextColumn::make('difficulty')->badge(),
|
||||
Tables\Columns\IconColumn::make('is_active')->boolean(),
|
||||
Tables\Columns\TextColumn::make('sort_order')->sortable(),
|
||||
])
|
||||
->filters([])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ManageTasks::route('/'),
|
||||
'import' => Pages\ImportTasks::route('/import'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
namespace App\Filament\Resources\TaskResource\Pages;
|
||||
|
||||
use App\Filament\Resources\TaskResource;
|
||||
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 ManageTasks extends ManageRecords
|
||||
{
|
||||
protected static string $resource = TaskResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\Action::make('import')
|
||||
->label('Import CSV')
|
||||
->icon('heroicon-o-arrow-up-tray')
|
||||
->url(TaskResource::getUrl('import')),
|
||||
Actions\Action::make('template')
|
||||
->label('Download CSV Template')
|
||||
->icon('heroicon-o-document-arrow-down')
|
||||
->url(url('/super-admin/templates/tasks.csv')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class ImportTasks extends Page
|
||||
{
|
||||
protected static string $resource = TaskResource::class;
|
||||
protected string $view = 'filament.pages.blank';
|
||||
protected ?string $heading = 'Import Tasks (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->importTasksCsv($full);
|
||||
Notification::make()->success()->title("Imported {$ok} rows")->body($fail ? "${fail} failed" : null)->send();
|
||||
}
|
||||
|
||||
private function importTasksCsv(string $file): array
|
||||
{
|
||||
$h = fopen($file, 'r');
|
||||
if (! $h) return [0,0];
|
||||
$ok = 0; $fail = 0;
|
||||
// Expected headers: emotion_name,emotion_name_de,emotion_name_en,event_type_slug,title_de,title_en,description_de,description_en,difficulty,example_text_de,example_text_en,sort_order,is_active
|
||||
$headers = fgetcsv($h, 0, ',');
|
||||
if (! $headers) return [0,0];
|
||||
$map = array_flip($headers);
|
||||
while (($row = fgetcsv($h, 0, ',')) !== false) {
|
||||
try {
|
||||
$emotionName = trim($row[$map['emotion_name']] ?? '');
|
||||
$emotionNameDe = trim($row[$map['emotion_name_de']] ?? '');
|
||||
$emotionNameEn = trim($row[$map['emotion_name_en']] ?? '');
|
||||
$emotionId = null;
|
||||
if ($emotionName !== '') {
|
||||
$emotionId = DB::table('emotions')->where('name', $emotionName)->value('id');
|
||||
}
|
||||
if (! $emotionId && $emotionNameDe !== '') {
|
||||
$emotionId = DB::table('emotions')->where('name', 'like', '%"de":"'.str_replace('"','""',$emotionNameDe).'"%')->value('id');
|
||||
}
|
||||
if (! $emotionId && $emotionNameEn !== '') {
|
||||
$emotionId = DB::table('emotions')->where('name', 'like', '%"en":"'.str_replace('"','""',$emotionNameEn).'"%')->value('id');
|
||||
}
|
||||
if (! $emotionId) { $fail++; continue; }
|
||||
$eventTypeSlug = trim($row[$map['event_type_slug']] ?? '');
|
||||
$eventTypeId = null;
|
||||
if ($eventTypeSlug !== '') {
|
||||
$eventTypeId = DB::table('event_types')->where('slug', $eventTypeSlug)->value('id');
|
||||
}
|
||||
$data = [
|
||||
'emotion_id' => $emotionId,
|
||||
'event_type_id' => $eventTypeId,
|
||||
'title' => [
|
||||
'de' => $row[$map['title_de']] ?? null,
|
||||
'en' => $row[$map['title_en']] ?? null,
|
||||
],
|
||||
'description' => [
|
||||
'de' => $row[$map['description_de']] ?? null,
|
||||
'en' => $row[$map['description_en']] ?? null,
|
||||
],
|
||||
'difficulty' => $row[$map['difficulty']] ?? 'easy',
|
||||
'example_text' => [
|
||||
'de' => $row[$map['example_text_de']] ?? null,
|
||||
'en' => $row[$map['example_text_en']] ?? null,
|
||||
],
|
||||
'sort_order' => (int)($row[$map['sort_order']] ?? 0),
|
||||
'is_active' => (int)($row[$map['is_active']] ?? 1) ? 1 : 0,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
DB::table('tasks')->insert($data);
|
||||
$ok++;
|
||||
} catch (\Throwable $e) {
|
||||
$fail++;
|
||||
}
|
||||
}
|
||||
fclose($h);
|
||||
return [$ok, $fail];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user