diff --git a/app-modules/panel-admin/lang/en/events.php b/app-modules/panel-admin/lang/en/events.php
new file mode 100644
index 000000000..865227d09
--- /dev/null
+++ b/app-modules/panel-admin/lang/en/events.php
@@ -0,0 +1,113 @@
+ 'Event',
+ 'plural' => 'Events',
+
+ 'columns' => [
+ 'title' => 'Title',
+ 'slug' => 'Slug',
+ 'type' => 'Type',
+ 'tenant' => 'Tenant',
+ 'location' => 'Location',
+ 'description' => 'Description',
+ 'starts_at' => 'Starts At',
+ 'ends_at' => 'Ends At',
+ 'status' => 'Status',
+ 'created_at' => 'Created At',
+ 'date' => 'Date',
+ 'code' => 'Code',
+ 'event_date' => 'Event Date',
+ 'valid_from' => 'Valid From',
+ 'expires_at' => 'Expires At',
+ 'uses' => 'Uses',
+ 'revoked_at' => 'Revoked At',
+ ],
+
+ 'sections' => [
+ 'enrollment_policy' => 'Enrollment Policy',
+ ],
+
+ 'form' => [
+ 'enrollment_method' => 'Enrollment Method',
+ 'check_in_method' => 'Check-in Method',
+ 'capacity' => 'Capacity',
+ 'waitlist_enabled' => 'Waitlist Enabled',
+ 'attendance_requirement' => 'Attendance Requirement',
+ 'minimum_days' => 'Minimum Days',
+ 'cancellation_deadline_hours' => 'Cancellation Deadline (hours before event)',
+ 'xp_on_confirmed' => 'XP on Confirmed',
+ 'xp_on_checked_in' => 'XP on Checked-in',
+ 'xp_on_attended' => 'XP on Attended',
+ 'application_form_schema' => 'Application Form Schema',
+ 'application_schema_key' => 'Field name',
+ 'application_schema_value' => 'Field type / label',
+ 'helpers' => [
+ 'minimum_days' => 'Required when attendance requirement is "Minimum Days". Default 1, max = event days.',
+ ],
+ ],
+
+ 'relations' => [
+ 'enrollments' => 'Enrollments',
+ 'check_in_codes' => 'Check-in Codes',
+ ],
+
+ 'enrollments' => [
+ 'columns' => [
+ 'participant' => 'Participant',
+ 'waitlist' => 'Waitlist',
+ 'enrolled_at' => 'Enrolled At',
+ 'confirmed_at' => 'Confirmed At',
+ 'check_in_history' => 'Check-in History',
+ 'cancelled_at' => 'Cancelled At',
+ ],
+ 'actions' => [
+ 'check_in' => 'Check In',
+ 'check_in_selected' => 'Check In Selected',
+ 'override_status' => 'Override Status',
+ 'new_status' => 'New Status',
+ 'reason' => 'Reason',
+ ],
+ 'notifications' => [
+ 'participant_checked_in' => 'Participant checked in.',
+ 'selected_participants_checked_in' => 'Selected participants checked in.',
+ 'status_overridden' => 'Enrollment status overridden.',
+ ],
+ ],
+
+ 'check_in_codes' => [
+ 'actions' => [
+ 'generate_code' => 'Generate Code',
+ 'revoke' => 'Revoke',
+ ],
+ 'fields' => [
+ 'code_length' => 'Code Length',
+ 'generated_code' => 'Generated Code',
+ 'max_uses' => 'Max Uses (optional)',
+ ],
+ 'digits' => [
+ 'four' => '4 digits',
+ 'six' => '6 digits',
+ ],
+ 'unlimited' => 'Unlimited',
+ 'notifications' => [
+ 'code_revoked' => 'Code revoked.',
+ ],
+ ],
+
+ 'edit' => [
+ 'scan_qr' => 'Scan QR',
+ 'qr_token' => 'QR Token',
+ 'qr_token_placeholder' => 'Scan or paste the participant token',
+ 'check_in_submit' => 'Check In',
+ 'participant_fallback' => 'Participant',
+ 'notifications' => [
+ 'check_in_success_title' => 'Check-in successful',
+ 'check_in_success_body' => ':name has been checked in.',
+ 'check_in_failed_title' => 'Check-in failed',
+ 'check_in_unexpected_error' => 'An unexpected error occurred. Please try again.',
+ ],
+ ],
+];
diff --git a/app-modules/panel-admin/lang/pt_BR/events.php b/app-modules/panel-admin/lang/pt_BR/events.php
new file mode 100644
index 000000000..e4ee75ed0
--- /dev/null
+++ b/app-modules/panel-admin/lang/pt_BR/events.php
@@ -0,0 +1,113 @@
+ 'Evento',
+ 'plural' => 'Eventos',
+
+ 'columns' => [
+ 'title' => 'Título',
+ 'slug' => 'Slug',
+ 'type' => 'Tipo',
+ 'tenant' => 'Tenant',
+ 'location' => 'Local',
+ 'description' => 'Descrição',
+ 'starts_at' => 'Início',
+ 'ends_at' => 'Término',
+ 'status' => 'Status',
+ 'created_at' => 'Criado em',
+ 'date' => 'Data',
+ 'code' => 'Código',
+ 'event_date' => 'Data do evento',
+ 'valid_from' => 'Válido de',
+ 'expires_at' => 'Expira em',
+ 'uses' => 'Usos',
+ 'revoked_at' => 'Revogado em',
+ ],
+
+ 'sections' => [
+ 'enrollment_policy' => 'Política de inscrição',
+ ],
+
+ 'form' => [
+ 'enrollment_method' => 'Método de inscrição',
+ 'check_in_method' => 'Método de check-in',
+ 'capacity' => 'Capacidade',
+ 'waitlist_enabled' => 'Lista de espera habilitada',
+ 'attendance_requirement' => 'Requisito de presença',
+ 'minimum_days' => 'Dias mínimos',
+ 'cancellation_deadline_hours' => 'Prazo de cancelamento (horas antes do evento)',
+ 'xp_on_confirmed' => 'XP ao confirmar',
+ 'xp_on_checked_in' => 'XP no check-in',
+ 'xp_on_attended' => 'XP ao comparecer',
+ 'application_form_schema' => 'Schema do formulário de inscrição',
+ 'application_schema_key' => 'Nome do campo',
+ 'application_schema_value' => 'Tipo / rótulo do campo',
+ 'helpers' => [
+ 'minimum_days' => 'Obrigatório quando o requisito de presença é "Dias mínimos". Padrão 1, máximo = dias do evento.',
+ ],
+ ],
+
+ 'relations' => [
+ 'enrollments' => 'Inscrições',
+ 'check_in_codes' => 'Códigos de check-in',
+ ],
+
+ 'enrollments' => [
+ 'columns' => [
+ 'participant' => 'Participante',
+ 'waitlist' => 'Lista de espera',
+ 'enrolled_at' => 'Inscrito em',
+ 'confirmed_at' => 'Confirmado em',
+ 'check_in_history' => 'Histórico de check-in',
+ 'cancelled_at' => 'Cancelado em',
+ ],
+ 'actions' => [
+ 'check_in' => 'Fazer check-in',
+ 'check_in_selected' => 'Check-in selecionados',
+ 'override_status' => 'Alterar status',
+ 'new_status' => 'Novo status',
+ 'reason' => 'Motivo',
+ ],
+ 'notifications' => [
+ 'participant_checked_in' => 'Participante com check-in realizado.',
+ 'selected_participants_checked_in' => 'Participantes selecionados com check-in realizado.',
+ 'status_overridden' => 'Status da inscrição alterado.',
+ ],
+ ],
+
+ 'check_in_codes' => [
+ 'actions' => [
+ 'generate_code' => 'Gerar código',
+ 'revoke' => 'Revogar',
+ ],
+ 'fields' => [
+ 'code_length' => 'Tamanho do código',
+ 'generated_code' => 'Código gerado',
+ 'max_uses' => 'Máximo de usos (opcional)',
+ ],
+ 'digits' => [
+ 'four' => '4 dígitos',
+ 'six' => '6 dígitos',
+ ],
+ 'unlimited' => 'Ilimitado',
+ 'notifications' => [
+ 'code_revoked' => 'Código revogado.',
+ ],
+ ],
+
+ 'edit' => [
+ 'scan_qr' => 'Escanear QR',
+ 'qr_token' => 'Token QR',
+ 'qr_token_placeholder' => 'Escaneie ou cole o token do participante',
+ 'check_in_submit' => 'Fazer check-in',
+ 'participant_fallback' => 'Participante',
+ 'notifications' => [
+ 'check_in_success_title' => 'Check-in realizado',
+ 'check_in_success_body' => ':name fez check-in com sucesso.',
+ 'check_in_failed_title' => 'Falha no check-in',
+ 'check_in_unexpected_error' => 'Ocorreu um erro inesperado. Tente novamente.',
+ ],
+ ],
+];
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/EventResource.php b/app-modules/panel-admin/src/Filament/Resources/Events/EventResource.php
index 36d0cef76..217aa62e1 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/EventResource.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/EventResource.php
@@ -29,6 +29,21 @@ final class EventResource extends Resource
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCalendarDays;
+ public static function getNavigationLabel(): string
+ {
+ return __('panel-admin::events.plural');
+ }
+
+ public static function getModelLabel(): string
+ {
+ return __('panel-admin::events.label');
+ }
+
+ public static function getPluralModelLabel(): string
+ {
+ return __('panel-admin::events.plural');
+ }
+
public static function form(Schema $schema): Schema
{
return EventForm::configure($schema);
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/Pages/EditEvent.php b/app-modules/panel-admin/src/Filament/Resources/Events/Pages/EditEvent.php
index bc809978d..3db3059f5 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/Pages/EditEvent.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/Pages/EditEvent.php
@@ -32,17 +32,17 @@ protected function getHeaderActions(): array
{
return [
Action::make('scanQr')
- ->label('Scan QR')
+ ->label(__('panel-admin::events.edit.scan_qr'))
->icon(Heroicon::QrCode)
->color('success')
->schema([
TextInput::make('token')
- ->label('QR Token')
+ ->label(__('panel-admin::events.edit.qr_token'))
->required()
->autofocus()
- ->placeholder('Scan or paste the participant token'),
+ ->placeholder(__('panel-admin::events.edit.qr_token_placeholder')),
])
- ->modalSubmitActionLabel('Check In')
+ ->modalSubmitActionLabel(__('panel-admin::events.edit.check_in_submit'))
->action(function (array $data): void {
/** @var Event $event */
$event = $this->getRecord();
@@ -58,24 +58,26 @@ protected function getHeaderActions(): array
);
$checkIn->enrollment->loadMissing('user');
- $participantName = $checkIn->enrollment->user->name ?? 'Participant';
+ $participantName = $checkIn->enrollment->user->name ?? __('panel-admin::events.edit.participant_fallback');
Notification::make()
->success()
- ->title('Check-in successful')
- ->body($participantName.' has been checked in.')
+ ->title(__('panel-admin::events.edit.notifications.check_in_success_title'))
+ ->body(__('panel-admin::events.edit.notifications.check_in_success_body', [
+ 'name' => $participantName,
+ ]))
->send();
} catch (CheckInException $e) {
Notification::make()
->danger()
- ->title('Check-in failed')
+ ->title(__('panel-admin::events.edit.notifications.check_in_failed_title'))
->body($e->getMessage())
->send();
} catch (Throwable $e) {
Notification::make()
->danger()
- ->title('Check-in failed')
- ->body('An unexpected error occurred. Please try again.')
+ ->title(__('panel-admin::events.edit.notifications.check_in_failed_title'))
+ ->body(__('panel-admin::events.edit.notifications.check_in_unexpected_error'))
->send();
report($e);
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/GenerateCheckInCodeAction.php b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/GenerateCheckInCodeAction.php
index cd320331b..2d02bdd89 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/GenerateCheckInCodeAction.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/GenerateCheckInCodeAction.php
@@ -21,7 +21,7 @@ final class GenerateCheckInCodeAction extends Action
protected function setUp(): void
{
parent::setUp();
- $this->label('Generate Code')
+ $this->label(__('panel-admin::events.check_in_codes.actions.generate_code'))
->icon(Heroicon::OutlinedPlusCircle)
->color('success')
->schema($this->generateFormSchema(...))
@@ -70,10 +70,10 @@ private function generateFormSchema(RelationManager $livewire): array
->columns(2)
->schema([
Select::make('digits')
- ->label('Code Length')
+ ->label(__('panel-admin::events.check_in_codes.fields.code_length'))
->options([
- '4' => '4 digits',
- '6' => '6 digits',
+ '4' => __('panel-admin::events.check_in_codes.digits.four'),
+ '6' => __('panel-admin::events.check_in_codes.digits.six'),
])
->default('6')
->live()
@@ -84,35 +84,35 @@ private function generateFormSchema(RelationManager $livewire): array
->required(),
TextInput::make('code_preview')
- ->label('Generated Code')
+ ->label(__('panel-admin::events.check_in_codes.fields.generated_code'))
->readOnly()
->default(fn (): string => $this->generateNumericCode(6))
->dehydrated()
->required(),
DatePicker::make('event_date')
- ->label('Event Date')
+ ->label(__('panel-admin::events.columns.event_date'))
->default($event->starts_at->toDateString())
->minDate($event->starts_at->toDateString())
->maxDate($event->ends_at->toDateString())
->required(),
DateTimePicker::make('starts_at')
- ->label('Valid From')
+ ->label(__('panel-admin::events.columns.valid_from'))
->default(now())
->required(),
DateTimePicker::make('expires_at')
- ->label('Expires At')
- ->default(now()->addHours(2))
+ ->label(__('panel-admin::events.columns.expires_at'))
->afterOrEqual('starts_at')
+ ->default(now()->addHours(2))
->required(),
TextInput::make('max_uses')
- ->label('Max Uses (optional)')
+ ->label(__('panel-admin::events.check_in_codes.fields.max_uses'))
->numeric()
->minValue(1)
- ->placeholder('Unlimited'),
+ ->placeholder(__('panel-admin::events.check_in_codes.unlimited')),
]),
];
}
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/OverrideEnrollmentStatusAction.php b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/OverrideEnrollmentStatusAction.php
index 9d1f7938d..b7426845e 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/OverrideEnrollmentStatusAction.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/OverrideEnrollmentStatusAction.php
@@ -20,19 +20,19 @@ protected function setUp(): void
{
parent::setUp();
- $this->label('Override Status')
+ $this->label(__('panel-admin::events.enrollments.actions.override_status'))
->icon(Heroicon::OutlinedPencilSquare)
->color('warning')
->visible(fn (Enrollment $record): bool => OverrideEnrollmentStatusDomainAction::allowedTargetsFor($record->status) !== [])
->schema([
Select::make('to_status')
- ->label('New Status')
+ ->label(__('panel-admin::events.enrollments.actions.new_status'))
->options(fn (Enrollment $record): array => collect(OverrideEnrollmentStatusDomainAction::allowedTargetsFor($record->status))
->mapWithKeys(fn (EnrollmentStatus $s): array => [$s->value => $s->getLabel()])
->all())
->required(),
Textarea::make('reason')
- ->label('Reason')
+ ->label(__('panel-admin::events.enrollments.actions.reason'))
->required()
->minLength(3)
->rows(3),
@@ -50,7 +50,7 @@ protected function setUp(): void
Notification::make()
->success()
- ->title('Enrollment status overridden.')
+ ->title(__('panel-admin::events.enrollments.notifications.status_overridden'))
->send();
});
}
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/RevokeCheckInCodeAction.php b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/RevokeCheckInCodeAction.php
index 9bd424d07..6bfd3cf2b 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/RevokeCheckInCodeAction.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/Actions/RevokeCheckInCodeAction.php
@@ -15,7 +15,7 @@ protected function setUp(): void
{
parent::setUp();
- $this->label('Revoke')
+ $this->label(__('panel-admin::events.check_in_codes.actions.revoke'))
->icon(Heroicon::OutlinedNoSymbol)
->color('danger')
->visible(fn (CheckInCode $record): bool => $record->revoked_at === null)
@@ -25,7 +25,7 @@ protected function setUp(): void
Notification::make()
->success()
- ->title('Code revoked.')
+ ->title(__('panel-admin::events.check_in_codes.notifications.code_revoked'))
->send();
});
}
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/CheckInCodesRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/CheckInCodesRelationManager.php
index 82f0bb7a5..d63daffc2 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/CheckInCodesRelationManager.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/CheckInCodesRelationManager.php
@@ -18,7 +18,10 @@ final class CheckInCodesRelationManager extends RelationManager
{
protected static string $relationship = 'checkInCodes';
- protected static ?string $title = 'Check-in Codes';
+ public static function getTitle(Model $ownerRecord, string $pageClass): string
+ {
+ return __('panel-admin::events.relations.check_in_codes');
+ }
public static function getBadge(Model $ownerRecord, string $pageClass): string
{
@@ -33,32 +36,32 @@ public function table(Table $table): Table
->modifyQueryUsing(fn (Builder $query): Builder => $query->latest())
->columns([
TextColumn::make('code')
- ->label('Code')
+ ->label(__('panel-admin::events.columns.code'))
->badge()
->color(fn (CheckInCode $record): string => $record->revoked_at !== null ? 'gray' : ($record->expires_at->isPast() ? 'warning' : 'success'))
->searchable(),
TextColumn::make('event_date')
- ->label('Event Date')
+ ->label(__('panel-admin::events.columns.event_date'))
->date()
->sortable(),
TextColumn::make('starts_at')
- ->label('Valid From')
+ ->label(__('panel-admin::events.columns.valid_from'))
->dateTime()
->sortable(),
TextColumn::make('expires_at')
- ->label('Expires At')
+ ->label(__('panel-admin::events.columns.expires_at'))
->dateTime()
->sortable(),
TextColumn::make('uses_count')
- ->label('Uses')
+ ->label(__('panel-admin::events.columns.uses'))
->state(fn (CheckInCode $record): string => $record->uses_count.($record->max_uses !== null ? '/'.$record->max_uses : '')),
TextColumn::make('revoked_at')
- ->label('Revoked At')
+ ->label(__('panel-admin::events.columns.revoked_at'))
->dateTime()
->placeholder('-'),
])
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/EnrollmentsRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/EnrollmentsRelationManager.php
index 9b322d5d8..c6a462328 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/EnrollmentsRelationManager.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/EnrollmentsRelationManager.php
@@ -25,12 +25,18 @@
use He4rt\PanelAdmin\Filament\Resources\Events\RelationManagers\Actions\OverrideEnrollmentStatusAction;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Date;
final class EnrollmentsRelationManager extends RelationManager
{
protected static string $relationship = 'enrollments';
+ public static function getTitle(Model $ownerRecord, string $pageClass): string
+ {
+ return __('panel-admin::events.relations.enrollments');
+ }
+
public function table(Table $table): Table
{
return $table
@@ -38,33 +44,33 @@ public function table(Table $table): Table
->modifyQueryUsing(fn (Builder $query): Builder => $query->with(['checkIns', 'user']))
->columns([
TextColumn::make('user.name')
- ->label('Participant')
+ ->label(__('panel-admin::events.enrollments.columns.participant'))
->searchable()
->sortable(),
TextColumn::make('status')
- ->label('Status')
+ ->label(__('panel-admin::events.columns.status'))
->badge()
->sortable(),
TextColumn::make('waitlist_position')
- ->label('Waitlist')
+ ->label(__('panel-admin::events.enrollments.columns.waitlist'))
->sortable()
->placeholder('-')
->toggleable(),
TextColumn::make('enrolled_at')
- ->label('Enrolled At')
+ ->label(__('panel-admin::events.enrollments.columns.enrolled_at'))
->dateTime()
->sortable(),
TextColumn::make('confirmed_at')
- ->label('Confirmed At')
+ ->label(__('panel-admin::events.enrollments.columns.confirmed_at'))
->dateTime()
->sortable(),
TextColumn::make('check_in_history')
- ->label('Check-in History')
+ ->label(__('panel-admin::events.enrollments.columns.check_in_history'))
->state(fn (Enrollment $record): string => $record->checkIns
->sortBy('event_date')
->map(fn (CheckIn $checkIn): string => $checkIn->event_date->toDateString())
@@ -73,14 +79,14 @@ public function table(Table $table): Table
->toggleable(),
TextColumn::make('cancelled_at')
- ->label('Cancelled At')
+ ->label(__('panel-admin::events.enrollments.columns.cancelled_at'))
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('status')
- ->label('Status')
+ ->label(__('panel-admin::events.columns.status'))
->options(EnrollmentStatus::class),
])
->recordActions([
@@ -99,7 +105,7 @@ public function table(Table $table): Table
private function checkInAction(): Action
{
return Action::make('checkIn')
- ->label('Check In')
+ ->label(__('panel-admin::events.enrollments.actions.check_in'))
->icon(Heroicon::OutlinedCheckCircle)
->color('success')
->visible(fn (Enrollment $record): bool => $record->status->is(EnrollmentStatus::Confirmed, EnrollmentStatus::CheckedIn))
@@ -109,7 +115,7 @@ private function checkInAction(): Action
Notification::make()
->success()
- ->title('Participant checked in.')
+ ->title(__('panel-admin::events.enrollments.notifications.participant_checked_in'))
->send();
});
}
@@ -117,7 +123,7 @@ private function checkInAction(): Action
private function bulkCheckInAction(): BulkAction
{
return BulkAction::make('checkInSelected')
- ->label('Check In Selected')
+ ->label(__('panel-admin::events.enrollments.actions.check_in_selected'))
->icon(Heroicon::OutlinedCheckCircle)
->color('success')
->schema($this->checkInSchema())
@@ -132,7 +138,7 @@ private function bulkCheckInAction(): BulkAction
Notification::make()
->success()
- ->title('Selected participants checked in.')
+ ->title(__('panel-admin::events.enrollments.notifications.selected_participants_checked_in'))
->send();
})
->deselectRecordsAfterCompletion();
@@ -148,7 +154,7 @@ private function checkInSchema(): array
return [
DatePicker::make('event_date')
- ->label('Date')
+ ->label(__('panel-admin::events.columns.date'))
->default(now())
->minDate($event->starts_at->toDateString())
->maxDate($event->ends_at->toDateString())
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventForm.php b/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventForm.php
index c55277a3f..47ae0b0a2 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventForm.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventForm.php
@@ -30,13 +30,13 @@ public static function configure(Schema $schema): Schema
->columns(2)
->components([
TextInput::make('title')
- ->label('Title')
+ ->label(__('panel-admin::events.columns.title'))
->required()
->maxLength(200)
->columnSpanFull(),
TextInput::make('slug')
- ->label('Slug')
+ ->label(__('panel-admin::events.columns.slug'))
->required()
->maxLength(120)
->unique(
@@ -53,75 +53,75 @@ public static function configure(Schema $schema): Schema
),
Select::make('event_type')
- ->label('Type')
+ ->label(__('panel-admin::events.columns.type'))
->options(EventType::class)
->required(),
Select::make('tenant_id')
- ->label('Tenant')
+ ->label(__('panel-admin::events.columns.tenant'))
->relationship('tenant', 'name')
->searchable()
->nullable(),
TextInput::make('location')
- ->label('Location')
+ ->label(__('panel-admin::events.columns.location'))
->nullable(),
Textarea::make('description')
- ->label('Description')
+ ->label(__('panel-admin::events.columns.description'))
->nullable()
->columnSpanFull(),
DateTimePicker::make('starts_at')
- ->label('Starts At')
+ ->label(__('panel-admin::events.columns.starts_at'))
->required(),
DateTimePicker::make('ends_at')
- ->label('Ends At')
+ ->label(__('panel-admin::events.columns.ends_at'))
->required()
->after('starts_at'),
Select::make('status')
- ->label('Status')
+ ->label(__('panel-admin::events.columns.status'))
->options(EventStatus::class)
->default(EventStatus::Draft)
->required()
->columnSpanFull(),
- Section::make('Enrollment Policy')
+ Section::make(__('panel-admin::events.sections.enrollment_policy'))
->relationship('enrollmentPolicy')
->columns(2)
->schema([
Select::make('enrollment_method')
- ->label('Enrollment Method')
+ ->label(__('panel-admin::events.form.enrollment_method'))
->options(EnrollmentMethod::class)
->live()
->required(),
Select::make('check_in_method')
- ->label('Check-in Method')
+ ->label(__('panel-admin::events.form.check_in_method'))
->options(CheckInMethod::class)
->required(),
TextInput::make('capacity')
- ->label('Capacity')
+ ->label(__('panel-admin::events.form.capacity'))
->integer()
->minValue(1)
->nullable(),
Toggle::make('has_waitlist')
- ->label('Waitlist Enabled')
+ ->label(__('panel-admin::events.form.waitlist_enabled'))
->default(false),
Select::make('attendance_requirement')
- ->label('Attendance Requirement')
+ ->label(__('panel-admin::events.form.attendance_requirement'))
->options(fn (Get $get): array => self::attendanceRequirementOptions($get))
->live()
->required(),
TextInput::make('minimum_days')
- ->label('Minimum Days')
- ->helperText('Required when attendance requirement is "Minimum Days". Default 1, max = event days.')
+ ->label(__('panel-admin::events.form.minimum_days'))
+ ->helperText(__('panel-admin::events.form.helpers.minimum_days'))
->integer()
->minValue(1)
->maxValue(fn (Get $get): ?int => self::minimumDaysMaxValue($get))
@@ -130,33 +130,33 @@ public static function configure(Schema $schema): Schema
->visible(fn (Get $get): bool => $get('attendance_requirement') === AttendanceRequirement::MinimumDays->value),
TextInput::make('cancellation_deadline_hours')
- ->label('Cancellation Deadline (hours before event)')
+ ->label(__('panel-admin::events.form.cancellation_deadline_hours'))
->integer()
->minValue(0)
->nullable(),
TextInput::make('xp_on_confirmed')
- ->label('XP on Confirmed')
+ ->label(__('panel-admin::events.form.xp_on_confirmed'))
->integer()
->minValue(0)
->default(0),
TextInput::make('xp_on_checked_in')
- ->label('XP on Checked-in')
+ ->label(__('panel-admin::events.form.xp_on_checked_in'))
->integer()
->minValue(0)
->default(0),
TextInput::make('xp_on_attended')
- ->label('XP on Attended')
+ ->label(__('panel-admin::events.form.xp_on_attended'))
->integer()
->minValue(0)
->default(0),
KeyValue::make('application_schema')
- ->label('Application Form Schema')
- ->keyLabel('Field name')
- ->valueLabel('Field type / label')
+ ->label(__('panel-admin::events.form.application_form_schema'))
+ ->keyLabel(__('panel-admin::events.form.application_schema_key'))
+ ->valueLabel(__('panel-admin::events.form.application_schema_value'))
->nullable()
->columnSpanFull()
->visible(fn (Get $get): bool => $get('enrollment_method') === EnrollmentMethod::Application->value),
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventInfolist.php b/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventInfolist.php
index e138d7fba..23e7e7d21 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventInfolist.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/Schemas/EventInfolist.php
@@ -17,79 +17,79 @@ public static function configure(Schema $schema): Schema
->columns(2)
->components([
TextEntry::make('title')
- ->label('Title')
+ ->label(__('panel-admin::events.columns.title'))
->columnSpanFull(),
TextEntry::make('slug')
- ->label('Slug'),
+ ->label(__('panel-admin::events.columns.slug')),
TextEntry::make('event_type')
- ->label('Type')
+ ->label(__('panel-admin::events.columns.type'))
->badge(),
TextEntry::make('tenant.name')
- ->label('Tenant'),
+ ->label(__('panel-admin::events.columns.tenant')),
TextEntry::make('location')
- ->label('Location'),
+ ->label(__('panel-admin::events.columns.location')),
TextEntry::make('description')
- ->label('Description')
+ ->label(__('panel-admin::events.columns.description'))
->columnSpanFull(),
TextEntry::make('starts_at')
- ->label('Starts At')
+ ->label(__('panel-admin::events.columns.starts_at'))
->dateTime(),
TextEntry::make('ends_at')
- ->label('Ends At')
+ ->label(__('panel-admin::events.columns.ends_at'))
->dateTime(),
TextEntry::make('status')
- ->label('Status')
+ ->label(__('panel-admin::events.columns.status'))
->badge(),
TextEntry::make('created_at')
- ->label('Created At')
+ ->label(__('panel-admin::events.columns.created_at'))
->dateTime(),
- Section::make('Enrollment Policy')
+ Section::make(__('panel-admin::events.sections.enrollment_policy'))
->relationship('enrollmentPolicy')
->columns(2)
->schema([
TextEntry::make('enrollment_method')
- ->label('Enrollment Method')
+ ->label(__('panel-admin::events.form.enrollment_method'))
->badge(),
TextEntry::make('check_in_method')
- ->label('Check-in Method')
+ ->label(__('panel-admin::events.form.check_in_method'))
->badge(),
TextEntry::make('capacity')
- ->label('Capacity'),
+ ->label(__('panel-admin::events.form.capacity')),
IconEntry::make('has_waitlist')
- ->label('Waitlist Enabled')
+ ->label(__('panel-admin::events.form.waitlist_enabled'))
->boolean(),
TextEntry::make('attendance_requirement')
- ->label('Attendance Requirement')
+ ->label(__('panel-admin::events.form.attendance_requirement'))
->badge(),
TextEntry::make('minimum_days')
- ->label('Minimum Days'),
+ ->label(__('panel-admin::events.form.minimum_days')),
TextEntry::make('cancellation_deadline_hours')
- ->label('Cancellation Deadline (hours before event)'),
+ ->label(__('panel-admin::events.form.cancellation_deadline_hours')),
TextEntry::make('xp_on_confirmed')
- ->label('XP on Confirmed'),
+ ->label(__('panel-admin::events.form.xp_on_confirmed')),
TextEntry::make('xp_on_checked_in')
- ->label('XP on Checked-in'),
+ ->label(__('panel-admin::events.form.xp_on_checked_in')),
TextEntry::make('xp_on_attended')
- ->label('XP on Attended'),
+ ->label(__('panel-admin::events.form.xp_on_attended')),
]),
]);
}
diff --git a/app-modules/panel-admin/src/Filament/Resources/Events/Tables/EventsTable.php b/app-modules/panel-admin/src/Filament/Resources/Events/Tables/EventsTable.php
index a9cf7ec98..827ba8d06 100644
--- a/app-modules/panel-admin/src/Filament/Resources/Events/Tables/EventsTable.php
+++ b/app-modules/panel-admin/src/Filament/Resources/Events/Tables/EventsTable.php
@@ -21,48 +21,48 @@ public static function table(Table $table): Table
return $table
->columns([
TextColumn::make('title')
- ->label('Title')
+ ->label(__('panel-admin::events.columns.title'))
->searchable()
->sortable(),
TextColumn::make('event_type')
- ->label('Type')
+ ->label(__('panel-admin::events.columns.type'))
->badge()
->sortable(),
TextColumn::make('tenant.name')
- ->label('Tenant')
+ ->label(__('panel-admin::events.columns.tenant'))
->searchable()
->sortable(),
TextColumn::make('starts_at')
- ->label('Starts At')
+ ->label(__('panel-admin::events.columns.starts_at'))
->dateTime()
->sortable(),
TextColumn::make('ends_at')
- ->label('Ends At')
+ ->label(__('panel-admin::events.columns.ends_at'))
->dateTime()
->sortable(),
TextColumn::make('status')
- ->label('Status')
+ ->label(__('panel-admin::events.columns.status'))
->badge()
->sortable(),
TextColumn::make('created_at')
- ->label('Created At')
+ ->label(__('panel-admin::events.columns.created_at'))
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('event_type')
- ->label('Type')
+ ->label(__('panel-admin::events.columns.type'))
->options(EventType::class),
SelectFilter::make('status')
- ->label('Status')
+ ->label(__('panel-admin::events.columns.status'))
->options(EventStatus::class),
])
->recordActions([
diff --git a/app/Http/Controllers/SwitchLocaleController.php b/app/Http/Controllers/SwitchLocaleController.php
new file mode 100644
index 000000000..283ae17d4
--- /dev/null
+++ b/app/Http/Controllers/SwitchLocaleController.php
@@ -0,0 +1,20 @@
+ $locale]);
+
+ return back();
+ }
+}
diff --git a/app/Http/Middleware/SetApplicationLocale.php b/app/Http/Middleware/SetApplicationLocale.php
new file mode 100644
index 000000000..6f202800b
--- /dev/null
+++ b/app/Http/Middleware/SetApplicationLocale.php
@@ -0,0 +1,23 @@
+configureBuilder();
$this->configureSelectFilter();
$this->configureTable();
+ $this->configureSchema();
}
public function register(): void
@@ -123,7 +126,18 @@ private function configureTable(): void
->defaultPaginationPageOption(10)
->filtersFormWidth(Width::Medium)
->paginated([10, 25, 50])
- ->emptyStateIcon(Heroicon::OutlinedExclamationTriangle));
+ ->emptyStateIcon(Heroicon::OutlinedExclamationTriangle)
+ ->defaultDateDisplayFormat(fn (): string => ApplicationLocale::dateFormat())
+ ->defaultDateTimeDisplayFormat(fn (): string => ApplicationLocale::dateTimeFormat())
+ ->defaultTimeDisplayFormat('H:i:s'));
+ }
+
+ private function configureSchema(): void
+ {
+ Schema::configureUsing(fn (Schema $schema): Schema => $schema
+ ->defaultDateDisplayFormat(fn (): string => ApplicationLocale::dateFormat())
+ ->defaultDateTimeDisplayFormat(fn (): string => ApplicationLocale::dateTimeFormat())
+ ->defaultTimeDisplayFormat('H:i:s'));
}
private function configureSelect(): void
@@ -143,6 +157,8 @@ private function configureDateTimePicker(): void
->seconds(false)
->minDate(now()->subYears(25))
->maxDate(now()->addYears(25))
+ ->defaultDateDisplayFormat(fn (): string => ApplicationLocale::dateFormat())
+ ->defaultDateTimeDisplayFormat(fn (): string => ApplicationLocale::dateTimeFormat())
->translateLabel());
}
diff --git a/app/Support/ApplicationLocale.php b/app/Support/ApplicationLocale.php
new file mode 100644
index 000000000..e39be1a7e
--- /dev/null
+++ b/app/Support/ApplicationLocale.php
@@ -0,0 +1,70 @@
+
+ */
+ public const array SUPPORTED = [
+ self::EN,
+ self::PT_BR,
+ ];
+
+ public static function isSupported(string $locale): bool
+ {
+ return in_array($locale, self::SUPPORTED, strict: true);
+ }
+
+ public static function resolve(): string
+ {
+ $locale = session(self::SESSION_KEY, config('app.locale', self::EN));
+
+ if (!is_string($locale) || !self::isSupported($locale)) {
+ return self::EN;
+ }
+
+ return $locale;
+ }
+
+ public static function apply(string $locale): void
+ {
+ app()->setLocale($locale);
+ Date::setLocale(self::carbonLocale($locale));
+ }
+
+ public static function carbonLocale(string $locale): string
+ {
+ return match ($locale) {
+ self::PT_BR => 'pt_BR',
+ default => 'en',
+ };
+ }
+
+ public static function dateFormat(): string
+ {
+ return match (app()->getLocale()) {
+ self::PT_BR => 'd/m/Y',
+ default => 'M j, Y',
+ };
+ }
+
+ public static function dateTimeFormat(): string
+ {
+ return match (app()->getLocale()) {
+ self::PT_BR => 'd/m/Y H:i:s',
+ default => 'M j, Y H:i:s',
+ };
+ }
+}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index d31b71406..eb45bc175 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -2,6 +2,7 @@
declare(strict_types=1);
+use App\Http\Middleware\SetApplicationLocale;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
@@ -19,6 +20,10 @@
TrustProxies::class,
Monicahq\Cloudflare\Http\Middleware\TrustProxies::class
);
+
+ $middleware->web(append: [
+ SetApplicationLocale::class,
+ ]);
})
->withExceptions(function (Exceptions $exceptions): void {})
->create();
diff --git a/lang/en/app.php b/lang/en/app.php
new file mode 100644
index 000000000..8f8917c0e
--- /dev/null
+++ b/lang/en/app.php
@@ -0,0 +1,12 @@
+ [
+ 'english' => 'English',
+ 'english_short' => 'EN',
+ 'portuguese' => 'Português (Brasil)',
+ 'portuguese_short' => 'PT-BR',
+ ],
+];
diff --git a/lang/pt_BR/app.php b/lang/pt_BR/app.php
new file mode 100644
index 000000000..8f8917c0e
--- /dev/null
+++ b/lang/pt_BR/app.php
@@ -0,0 +1,12 @@
+ [
+ 'english' => 'English',
+ 'english_short' => 'EN',
+ 'portuguese' => 'Português (Brasil)',
+ 'portuguese_short' => 'PT-BR',
+ ],
+];
diff --git a/resources/css/filament/admin/theme.css b/resources/css/filament/admin/theme.css
index 6b2c0eb56..40308c886 100644
--- a/resources/css/filament/admin/theme.css
+++ b/resources/css/filament/admin/theme.css
@@ -1,5 +1,6 @@
@import '../../../../vendor/filament/filament/resources/css/theme.css';
@import '../../../../vendor/livewire/flux/dist/flux.css';
+@import '../locale-switcher.css' layer(components);
@source '../../../../app/Filament/**/*';
@source '../../../../resources/views/**/*';
diff --git a/resources/css/filament/app/theme.css b/resources/css/filament/app/theme.css
index 8bc6c0311..6f76cb5a7 100644
--- a/resources/css/filament/app/theme.css
+++ b/resources/css/filament/app/theme.css
@@ -1,4 +1,5 @@
@import '../../../../vendor/filament/filament/resources/css/theme.css';
+@import '../locale-switcher.css' layer(components);
@source '../../../../app/Filament/**/*';
@source '../../../../app/Filament/**/*';
diff --git a/resources/css/filament/locale-switcher.css b/resources/css/filament/locale-switcher.css
new file mode 100644
index 000000000..9b89b86c8
--- /dev/null
+++ b/resources/css/filament/locale-switcher.css
@@ -0,0 +1,15 @@
+.fi-locale-switcher {
+ @apply grid grid-flow-col gap-x-1;
+}
+
+.fi-locale-switcher-btn {
+ @apply flex justify-center rounded-md px-3 py-2 text-xs font-semibold tracking-wide outline-hidden transition duration-75 hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5;
+
+ &.fi-active {
+ @apply text-primary-500 dark:text-primary-400 bg-gray-50 dark:bg-white/5;
+ }
+
+ &:not(.fi-active) {
+ @apply text-gray-400 hover:text-gray-500 focus-visible:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400 dark:focus-visible:text-gray-400;
+ }
+}
diff --git a/resources/views/components/filament/locale-switcher/button.blade.php b/resources/views/components/filament/locale-switcher/button.blade.php
new file mode 100644
index 000000000..84c581405
--- /dev/null
+++ b/resources/views/components/filament/locale-switcher/button.blade.php
@@ -0,0 +1,26 @@
+@props (['label', 'locale', 'active' => false])
+
+@php
+ $ariaLabel = match ($locale) {
+ \App\Support\ApplicationLocale::EN => __('app.locale.english'),
+ \App\Support\ApplicationLocale::PT_BR => __('app.locale.portuguese'),
+ default => $label,
+ };
+@endphp
+
+ $active,
+ 'text-gray-400 hover:bg-gray-50 hover:text-gray-500 focus-visible:bg-gray-50 focus-visible:text-gray-500 dark:text-gray-500 dark:hover:bg-white/5 dark:hover:text-gray-400 dark:focus-visible:bg-white/5' => !$active
+ ])
+ x-on:click="close()"
+>
+ {{ $label }}
+
diff --git a/resources/views/components/filament/locale-switcher/dropdown.blade.php b/resources/views/components/filament/locale-switcher/dropdown.blade.php
new file mode 100644
index 000000000..e029ec636
--- /dev/null
+++ b/resources/views/components/filament/locale-switcher/dropdown.blade.php
@@ -0,0 +1,3 @@
+