them log, chinh upload
Some checks failed
Tests / PHP 8.3 (push) Has been cancelled
Tests / PHP 8.4 (push) Has been cancelled
Tests / PHP 8.5 (push) Has been cancelled

This commit is contained in:
2026-05-09 11:00:59 +00:00
parent 96e1ce4f40
commit efa97b6c71
33 changed files with 1055 additions and 47 deletions

View File

@@ -0,0 +1,132 @@
<?php
namespace App\Filament\Resources\ActivityLogs;
use App\Filament\Resources\ActivityLogs\Pages\ListActivityLogs;
use App\Models\ActivityLog;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Illuminate\Database\Eloquent\Builder;
class ActivityLogResource extends Resource
{
protected static ?string $model = ActivityLog::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedClock;
protected static ?int $navigationSort = 7;
protected static ?string $recordTitleAttribute = 'description';
public static function getPluralLabel(): ?string
{
return __('app.resource_activity_logs');
}
public static function getNavigationGroup(): ?string
{
return __('app.nav_management');
}
public static function table(Table $table): Table
{
return $table
->recordTitleAttribute('description')
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('created_at')
->label(__('app.time'))
->dateTime('d/m/Y H:i:s')
->sortable(),
TextColumn::make('user.name')
->label(__('app.user'))
->placeholder(__('app.system')),
TextColumn::make('action')
->label(__('app.action'))
->badge()
->formatStateUsing(fn (string $state): string => match ($state) {
'import' => __('app.action_import'),
'export' => __('app.action_export'),
'transfer' => __('app.action_transfer'),
'close' => __('app.action_close'),
'create' => __('app.action_create'),
'update' => __('app.action_update'),
'delete' => __('app.action_delete'),
default => $state,
})
->color(fn (string $state): string => match ($state) {
'import' => 'info',
'export' => 'success',
'transfer' => 'warning',
'close' => 'success',
'create' => 'info',
'update' => 'gray',
'delete' => 'danger',
default => 'gray',
}),
TextColumn::make('description')
->label(__('app.description'))
->wrap()
->searchable(),
TextColumn::make('metadata')
->label(__('app.details'))
->formatStateUsing(function (?array $state): string {
if (empty($state)) return '';
$lines = [];
foreach ($state as $key => $val) {
if (is_scalar($val)) {
$lines[] = "<b>{$key}</b>: {$val}";
} elseif (is_array($val)) {
$nested = [];
foreach ($val as $k => $v) {
if (is_scalar($v)) {
$nested[] = "{$k}: {$v}";
}
}
$lines[] = "<b>{$key}</b>: " . implode(', ', $nested);
}
}
return implode('<br>', $lines);
})
->html()
->wrap()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('action')
->label(__('app.action'))
->options([
'import' => __('app.action_import'),
'export' => __('app.action_export'),
'transfer' => __('app.action_transfer'),
'close' => __('app.action_close'),
'create' => __('app.action_create'),
'update' => __('app.action_update'),
'delete' => __('app.action_delete'),
]),
SelectFilter::make('user_id')
->label(__('app.user'))
->relationship('user', 'name'),
])
->actions([])
->bulkActions([]);
}
public static function getPages(): array
{
return [
'index' => ListActivityLogs::route('/'),
];
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()
->with('user')
->orderBy('created_at', 'desc');
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\ActivityLogs\Pages;
use App\Filament\Resources\ActivityLogs\ActivityLogResource;
use Filament\Resources\Pages\ListRecords;
class ListActivityLogs extends ListRecords
{
protected static string $resource = ActivityLogResource::class;
}

View File

@@ -12,7 +12,10 @@ class FeedbacksRelationManager extends RelationManager
{
protected static string $relationship = 'feedbacks';
protected static ?string $title = 'app.feedback_history';
public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string
{
return __('app.feedback_history');
}
public function table(Table $table): Table
{

View File

@@ -49,6 +49,10 @@ class CreateFeedback extends CreateRecord
continue;
}
if ($feedback->attachments()->where('path', $path)->exists()) {
continue;
}
$feedback->attachments()->create([
'uuid' => (string) \Illuminate\Support\Str::uuid(),
'user_id' => auth()->id(),
@@ -56,9 +60,27 @@ class CreateFeedback extends CreateRecord
'path' => $path,
'disk' => 'public',
'collection' => $this->pendingCollection,
'mime_type' => $disk->mimeType($path),
'size' => $disk->size($path),
'mime_type' => $this->safeMimeType($disk, $path),
'size' => $this->safeFileSize($disk, $path),
]);
}
}
protected function safeMimeType($disk, string $path): ?string
{
try {
return $disk->mimeType($path);
} catch (\Exception $e) {
return null;
}
}
protected function safeFileSize($disk, string $path): ?int
{
try {
return $disk->size($path);
} catch (\Exception $e) {
return null;
}
}
}

View File

@@ -90,6 +90,10 @@ class EditFeedback extends EditRecord
continue;
}
if ($feedback->attachments()->where('path', $path)->exists()) {
continue;
}
$feedback->attachments()->create([
'uuid' => (string) \Illuminate\Support\Str::uuid(),
'user_id' => $sender->id,
@@ -97,8 +101,8 @@ class EditFeedback extends EditRecord
'path' => $path,
'disk' => 'public',
'collection' => 'general',
'mime_type' => $disk->mimeType($path),
'size' => $disk->size($path),
'mime_type' => $this->safeMimeType($disk, $path),
'size' => $this->safeFileSize($disk, $path),
]);
}
}
@@ -194,6 +198,10 @@ class EditFeedback extends EditRecord
continue;
}
if ($feedback->attachments()->where('path', $path)->exists()) {
continue;
}
$feedback->attachments()->create([
'uuid' => (string) \Illuminate\Support\Str::uuid(),
'user_id' => auth()->id(),
@@ -201,12 +209,30 @@ class EditFeedback extends EditRecord
'path' => $path,
'disk' => 'public',
'collection' => $this->pendingCollection,
'mime_type' => $disk->mimeType($path),
'size' => $disk->size($path),
'mime_type' => $this->safeMimeType($disk, $path),
'size' => $this->safeFileSize($disk, $path),
]);
}
}
protected function safeMimeType($disk, string $path): ?string
{
try {
return $disk->mimeType($path);
} catch (\Exception $e) {
return null;
}
}
protected function safeFileSize($disk, string $path): ?int
{
try {
return $disk->size($path);
} catch (\Exception $e) {
return null;
}
}
protected function mutateFormDataBeforeFill(array $data): array
{
$data['is_general'] = $data['customer_product_id'] === null;

View File

@@ -27,7 +27,10 @@ class InteractionsRelationManager extends RelationManager
{
protected static string $relationship = 'interactions';
protected static ?string $title = 'feedback.interaction_history';
public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string
{
return __('feedback.interaction_history');
}
public function form(Schema $schema): Schema
{
@@ -211,6 +214,10 @@ class InteractionsRelationManager extends RelationManager
continue;
}
if ($record->attachments()->where('path', $path)->exists()) {
continue;
}
$record->attachments()->create([
'uuid' => (string) \Illuminate\Support\Str::uuid(),
'feedback_id' => $record->feedback_id,
@@ -219,8 +226,8 @@ class InteractionsRelationManager extends RelationManager
'path' => $path,
'disk' => 'public',
'collection' => 'general',
'mime_type' => $disk->mimeType($path),
'size' => $disk->size($path),
'mime_type' => $this->safeMimeType($disk, $path),
'size' => $this->safeFileSize($disk, $path),
]);
}
}
@@ -262,4 +269,22 @@ class InteractionsRelationManager extends RelationManager
throw new \Filament\Exceptions\SilentActionException;
}
}
protected function safeMimeType($disk, string $path): ?string
{
try {
return $disk->mimeType($path);
} catch (\Exception $e) {
return null;
}
}
protected function safeFileSize($disk, string $path): ?int
{
try {
return $disk->size($path);
} catch (\Exception $e) {
return null;
}
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Filament\Resources\Products\RelationManagers;
use App\Enums\ContractStatus;
use App\Enums\ContractType;
use App\Filament\Resources\Contracts\ContractResource;
use App\Models\Feedback;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filter;
@@ -15,7 +16,10 @@ class ContractsRelationManager extends RelationManager
{
protected static string $relationship = 'contracts';
protected static ?string $title = 'app.contracts';
public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string
{
return __('app.contracts');
}
public function table(Table $table): Table
{

View File

@@ -12,7 +12,10 @@ class HandoversRelationManager extends RelationManager
{
protected static string $relationship = 'handovers';
protected static ?string $title = 'app.handovers';
public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string
{
return __('app.handovers');
}
public function table(Table $table): Table
{

View File

@@ -3,7 +3,14 @@
namespace App\Filament\Resources\Products\RelationManagers;
use App\Enums\ServiceType;
use Filament\Actions\CreateAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
@@ -12,7 +19,39 @@ class ProductServicesRelationManager extends RelationManager
{
protected static string $relationship = 'productServices';
protected static ?string $title = 'app.services';
public static function getTitle(\Illuminate\Database\Eloquent\Model $ownerRecord, string $pageClass): string
{
return __('app.services');
}
public function form(Schema $schema): Schema
{
return $schema
->components([
Select::make('service_type')
->label(__('enums.service_type'))
->options(ServiceType::options())
->required(),
Select::make('status')
->label(__('enums.status'))
->options([
'active' => __('app.status_active'),
'pending' => __('app.status_pending'),
'completed' => __('app.status_completed'),
'cancelled' => __('app.status_cancelled'),
])
->default('active')
->required(),
DatePicker::make('actual_collection_date')
->label(__('app.collection_date')),
Textarea::make('remark')
->label(__('app.remark'))
->rows(3),
]);
}
public function table(Table $table): Table
{
@@ -20,15 +59,12 @@ class ProductServicesRelationManager extends RelationManager
->recordTitleAttribute('service_type')
->columns([
TextColumn::make('service_type')
->label(__('enums.service_type'))
->badge()
->color(fn (string $state): string => match ($state) {
'management_fee' => 'info',
'gratitude' => 'success',
'self_business' => 'warning',
default => 'gray',
})
->color(fn (string $state): string => ServiceType::from($state)->color())
->formatStateUsing(fn (string $state): string => ServiceType::from($state)->label()),
TextColumn::make('status')
->label(__('enums.status'))
->badge()
->color(fn (string $state): string => match ($state) {
'active' => 'success',
@@ -52,8 +88,10 @@ class ProductServicesRelationManager extends RelationManager
])
->filters([
SelectFilter::make('service_type')
->options(ServiceType::MANAGEMENT_FEE->options()),
->label(__('enums.service_type'))
->options(ServiceType::options()),
SelectFilter::make('status')
->label(__('enums.status'))
->options([
'active' => __('app.status_active'),
'pending' => __('app.status_pending'),
@@ -62,7 +100,12 @@ class ProductServicesRelationManager extends RelationManager
]),
])
->defaultSort('created_at', 'desc')
->headerActions([])
->recordActions([]);
->headerActions([
CreateAction::make(),
])
->recordActions([
EditAction::make(),
DeleteAction::make(),
]);
}
}