Refactor SMS and Coupon resources to utilize localization for labels and messages, enhancing internationalization support. Update .env.example to change APP_LOCALE to 'tk'.
This commit is contained in:
@@ -30,9 +30,9 @@ class SendSms extends Page
|
||||
{
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedChatBubbleLeftRight;
|
||||
|
||||
protected static ?string $navigationLabel = 'Send SMS';
|
||||
protected static ?string $navigationLabel = null;
|
||||
|
||||
protected static ?string $title = 'Send SMS';
|
||||
protected static ?string $title = null;
|
||||
|
||||
protected static ?int $navigationSort = 2;
|
||||
|
||||
@@ -47,6 +47,16 @@ class SendSms extends Page
|
||||
|
||||
public bool $campaignNotificationSent = false;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament.send_sms.navigation');
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return __('filament.send_sms.title');
|
||||
}
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->form->fill([
|
||||
@@ -68,38 +78,37 @@ class SendSms extends Page
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make('Message')
|
||||
->description('Write the SMS exactly as recipients should see it.')
|
||||
Section::make(__('filament.send_sms.sections.message'))
|
||||
->schema([
|
||||
Textarea::make('message')
|
||||
->label('SMS message')
|
||||
->label(__('filament.send_sms.fields.message'))
|
||||
->required()
|
||||
->rows(8)
|
||||
->maxLength(1000)
|
||||
->live(debounce: 300)
|
||||
->helperText(fn (Get $get): string => app(SmsMessageAnalyzer::class)->summary((string) ($get('message') ?? ''))),
|
||||
]),
|
||||
Section::make('Recipients')
|
||||
->description('Send to everyone, pick specific coupon holders, or exclude numbers from a broadcast.')
|
||||
Section::make(__('filament.send_sms.sections.recipients'))
|
||||
->description(__('filament.send_sms.sections.recipients_description'))
|
||||
->schema([
|
||||
Checkbox::make('send_to_all')
|
||||
->label('Send to all coupon holders')
|
||||
->helperText('When enabled, every coupon holder is included. Manual selection is disabled.')
|
||||
->label(__('filament.send_sms.fields.send_to_all'))
|
||||
->helperText(__('filament.send_sms.fields.send_to_all_helper'))
|
||||
->live(),
|
||||
Select::make('coupon_ids')
|
||||
->label('Select recipients')
|
||||
->label(__('filament.send_sms.fields.coupon_ids'))
|
||||
->multiple()
|
||||
->searchable()
|
||||
->disabled(fn (Get $get): bool => (bool) $get('send_to_all'))
|
||||
->dehydrated(fn (Get $get): bool => ! (bool) $get('send_to_all'))
|
||||
->helperText('Search by phone, coupon code, or ID.')
|
||||
->helperText(__('filament.send_sms.fields.coupon_ids_helper'))
|
||||
->getSearchResultsUsing(fn (string $search): array => $this->searchCoupons($search))
|
||||
->getOptionLabelsUsing(fn (array $values): array => $this->couponLabels($values)),
|
||||
Select::make('excluded_coupon_ids')
|
||||
->label('Exclude recipients')
|
||||
->label(__('filament.send_sms.fields.excluded_coupon_ids'))
|
||||
->multiple()
|
||||
->searchable()
|
||||
->helperText('Excluded numbers will not receive this message, even when sending to all.')
|
||||
->helperText(__('filament.send_sms.fields.excluded_coupon_ids_helper'))
|
||||
->getSearchResultsUsing(fn (string $search): array => $this->searchCoupons($search))
|
||||
->getOptionLabelsUsing(fn (array $values): array => $this->couponLabels($values)),
|
||||
]),
|
||||
@@ -116,12 +125,12 @@ class SendSms extends Page
|
||||
->footer([
|
||||
Actions::make([
|
||||
Action::make('send')
|
||||
->label('Send SMS')
|
||||
->label(__('filament.send_sms.actions.send'))
|
||||
->icon(Heroicon::OutlinedPaperAirplane)
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Send SMS now?')
|
||||
->modalHeading(__('filament.send_sms.actions.send_confirm_heading'))
|
||||
->modalDescription(fn (): string => $this->confirmationDescription())
|
||||
->modalSubmitActionLabel('Send')
|
||||
->modalSubmitActionLabel(__('filament.send_sms.actions.send_confirm_submit'))
|
||||
->action('sendCampaign')
|
||||
->disabled(fn (): bool => $this->activeCampaignId !== null && ! $this->isCampaignComplete()),
|
||||
]),
|
||||
@@ -133,7 +142,7 @@ class SendSms extends Page
|
||||
{
|
||||
if ($this->activeCampaignId !== null && ! $this->isCampaignComplete()) {
|
||||
Notification::make()
|
||||
->title('A campaign is still sending')
|
||||
->title(__('filament.send_sms.notifications.campaign_still_sending'))
|
||||
->warning()
|
||||
->send();
|
||||
|
||||
@@ -167,8 +176,8 @@ class SendSms extends Page
|
||||
|
||||
if ($willReceive === 0) {
|
||||
Notification::make()
|
||||
->title('No valid recipients')
|
||||
->body('Adjust your selection or exclusions — no coupon holders with valid phone numbers will receive this message.')
|
||||
->title(__('filament.send_sms.notifications.no_valid_recipients'))
|
||||
->body(__('filament.send_sms.notifications.no_valid_recipients_body'))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
@@ -190,8 +199,8 @@ class SendSms extends Page
|
||||
$this->notifyCampaignFinished($campaign);
|
||||
} else {
|
||||
Notification::make()
|
||||
->title('SMS campaign queued')
|
||||
->body("Sending to {$campaign->recipient_count} recipient(s) in the background.")
|
||||
->title(__('filament.send_sms.notifications.queued'))
|
||||
->body(__('filament.send_sms.notifications.queued_body', ['count' => $campaign->recipient_count]))
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
@@ -227,20 +236,24 @@ class SendSms extends Page
|
||||
|
||||
if ($campaign->failed_count > 0 && $campaign->sent_count === 0) {
|
||||
Notification::make()
|
||||
->title('SMS campaign failed')
|
||||
->body("{$campaign->failed_count} message(s) failed to send.")
|
||||
->title(__('filament.send_sms.notifications.failed'))
|
||||
->body(__('filament.send_sms.notifications.failed_body', ['count' => $campaign->failed_count]))
|
||||
->danger()
|
||||
->send();
|
||||
} elseif ($campaign->failed_count > 0) {
|
||||
Notification::make()
|
||||
->title('SMS campaign finished with errors')
|
||||
->body("Sent {$campaign->sent_count}, failed {$campaign->failed_count}, skipped {$campaign->skipped_count}.")
|
||||
->title(__('filament.send_sms.notifications.finished_with_errors'))
|
||||
->body(__('filament.send_sms.notifications.finished_with_errors_body', [
|
||||
'sent' => $campaign->sent_count,
|
||||
'failed' => $campaign->failed_count,
|
||||
'skipped' => $campaign->skipped_count,
|
||||
]))
|
||||
->warning()
|
||||
->send();
|
||||
} else {
|
||||
Notification::make()
|
||||
->title('SMS campaign completed')
|
||||
->body("Successfully sent {$campaign->sent_count} message(s).")
|
||||
->title(__('filament.send_sms.notifications.completed'))
|
||||
->body(__('filament.send_sms.notifications.completed_body', ['count' => $campaign->sent_count]))
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
@@ -264,7 +277,10 @@ class SendSms extends Page
|
||||
$analyzer = app(SmsMessageAnalyzer::class);
|
||||
$willReceive = $this->getWillReceiveCount();
|
||||
|
||||
return "Send to {$willReceive} recipient(s)? {$analyzer->summary($message)}";
|
||||
return __('filament.send_sms.confirmation', [
|
||||
'count' => $willReceive,
|
||||
'summary' => $analyzer->summary($message),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getWillReceiveCount(): int
|
||||
@@ -304,7 +320,11 @@ class SendSms extends Page
|
||||
return '';
|
||||
}
|
||||
|
||||
return "{$campaign->processedCount()} / {$campaign->recipient_count} processed · {$campaign->sent_count} sent";
|
||||
return __('filament.send_sms.progress.processed', [
|
||||
'processed' => $campaign->processedCount(),
|
||||
'total' => $campaign->recipient_count,
|
||||
'sent' => $campaign->sent_count,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,12 +19,17 @@ class CouponResource extends Resource
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'phone';
|
||||
|
||||
protected static ?string $navigationLabel = 'Coupons';
|
||||
protected static ?string $navigationLabel = null;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedTicket;
|
||||
|
||||
protected static ?int $navigationSort = 1;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament.coupons.navigation');
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return CouponInfolist::configure($schema);
|
||||
|
||||
@@ -12,13 +12,17 @@ class CouponInfolist
|
||||
return $schema
|
||||
->components([
|
||||
TextEntry::make('phone')
|
||||
->label(__('filament.fields.phone'))
|
||||
->formatStateUsing(fn (string $state): string => format_phone($state))
|
||||
->copyable(),
|
||||
TextEntry::make('code')
|
||||
->label(__('filament.fields.code'))
|
||||
->copyable(),
|
||||
TextEntry::make('created_at')
|
||||
->label(__('filament.fields.created_at'))
|
||||
->dateTime(),
|
||||
TextEntry::make('updated_at')
|
||||
->label(__('filament.fields.updated_at'))
|
||||
->dateTime(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Filament\Resources\Coupons\Tables;
|
||||
|
||||
use App\Filament\Tables\Filters\CreatedAtDateFilter;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
@@ -16,19 +16,24 @@ class CouponsTable
|
||||
->defaultSort('created_at', 'desc')
|
||||
->columns([
|
||||
TextColumn::make('id')
|
||||
->label(__('filament.fields.id'))
|
||||
->sortable(),
|
||||
TextColumn::make('phone')
|
||||
->label(__('filament.fields.phone'))
|
||||
->formatStateUsing(fn (string $state): string => format_phone($state))
|
||||
->searchable()
|
||||
->sortable()
|
||||
->copyable(),
|
||||
TextColumn::make('code')
|
||||
->label(__('filament.fields.code'))
|
||||
->sortable()
|
||||
->copyable(),
|
||||
TextColumn::make('created_at')
|
||||
->label(__('filament.fields.created_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('updated_at')
|
||||
->label(__('filament.fields.updated_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
@@ -19,16 +19,31 @@ class PhoneVerificationResource extends Resource
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'phone';
|
||||
|
||||
protected static ?string $navigationLabel = 'OTP Verifications';
|
||||
protected static ?string $navigationLabel = null;
|
||||
|
||||
protected static ?string $modelLabel = 'OTP verification';
|
||||
protected static ?string $modelLabel = null;
|
||||
|
||||
protected static ?string $pluralModelLabel = 'OTP verifications';
|
||||
protected static ?string $pluralModelLabel = null;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedKey;
|
||||
|
||||
protected static ?int $navigationSort = 2;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament.phone_verifications.navigation');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('filament.phone_verifications.model');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('filament.phone_verifications.plural');
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return PhoneVerificationInfolist::configure($schema);
|
||||
|
||||
@@ -13,15 +13,20 @@ class PhoneVerificationInfolist
|
||||
return $schema
|
||||
->components([
|
||||
TextEntry::make('phone')
|
||||
->label(__('filament.fields.phone'))
|
||||
->formatStateUsing(fn (string $state): string => format_phone($state))
|
||||
->copyable(),
|
||||
TextEntry::make('expires_at')
|
||||
->label(__('filament.fields.expires_at'))
|
||||
->dateTime(),
|
||||
IconEntry::make('verified')
|
||||
->label(__('filament.fields.verified'))
|
||||
->boolean(),
|
||||
TextEntry::make('created_at')
|
||||
->label(__('filament.fields.created_at'))
|
||||
->dateTime(),
|
||||
TextEntry::make('updated_at')
|
||||
->label(__('filament.fields.updated_at'))
|
||||
->dateTime(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -16,22 +16,28 @@ class PhoneVerificationsTable
|
||||
->defaultSort('created_at', 'desc')
|
||||
->columns([
|
||||
TextColumn::make('id')
|
||||
->label(__('filament.fields.id'))
|
||||
->sortable(),
|
||||
TextColumn::make('phone')
|
||||
->label(__('filament.fields.phone'))
|
||||
->formatStateUsing(fn (string $state): string => format_phone($state))
|
||||
->searchable()
|
||||
->sortable()
|
||||
->copyable(),
|
||||
TextColumn::make('expires_at')
|
||||
->label(__('filament.fields.expires_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
IconColumn::make('verified')
|
||||
->label(__('filament.fields.verified'))
|
||||
->boolean()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label(__('filament.fields.created_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('updated_at')
|
||||
->label(__('filament.fields.updated_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
@@ -13,12 +13,12 @@ class CreatedAtDateFilter
|
||||
public static function make(): Filter
|
||||
{
|
||||
return Filter::make('created_at')
|
||||
->label('Date')
|
||||
->label(__('filament.filters.date'))
|
||||
->schema([
|
||||
DatePicker::make('from')
|
||||
->label('From'),
|
||||
->label(__('filament.filters.from')),
|
||||
DatePicker::make('until')
|
||||
->label('Until'),
|
||||
->label(__('filament.filters.until')),
|
||||
])
|
||||
->query(function (Builder $query, array $data): Builder {
|
||||
return $query
|
||||
@@ -35,12 +35,16 @@ class CreatedAtDateFilter
|
||||
$indicators = [];
|
||||
|
||||
if ($data['from'] ?? null) {
|
||||
$indicators[] = Indicator::make('From '.Carbon::parse($data['from'])->toFormattedDateString())
|
||||
$indicators[] = Indicator::make(__('filament.filters.from_indicator', [
|
||||
'date' => Carbon::parse($data['from'])->toFormattedDateString(),
|
||||
]))
|
||||
->removeField('from');
|
||||
}
|
||||
|
||||
if ($data['until'] ?? null) {
|
||||
$indicators[] = Indicator::make('Until '.Carbon::parse($data['until'])->toFormattedDateString())
|
||||
$indicators[] = Indicator::make(__('filament.filters.until_indicator', [
|
||||
'date' => Carbon::parse($data['until'])->toFormattedDateString(),
|
||||
]))
|
||||
->removeField('until');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user