Compare commits
10 Commits
275ee63ffb
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3eaf2a2a3 | ||
|
|
3f646ea951 | ||
|
|
21feb3baa6 | ||
|
|
8e66883587 | ||
|
|
7cef2543b9 | ||
|
|
6f3069f619 | ||
|
|
96eefd70ba | ||
|
|
9a14bc8254 | ||
|
|
fa3202288f | ||
|
|
c74330cfaf |
@@ -6,7 +6,7 @@ APP_URL=http://localhost
|
||||
|
||||
SMS_API_URL=https://sms.daragt.com/api/sms
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE=tk
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
@@ -69,3 +69,4 @@ VITE_APP_NAME="${APP_NAME}"
|
||||
SMS_API_URL=http://216.250.14.144:3000/api/data
|
||||
SMS_API_TIMEOUT=10
|
||||
SMS_API_CONNECT_TIMEOUT=5
|
||||
SMS_BULK_DELAY_MS=300
|
||||
|
||||
59
README.md
59
README.md
@@ -1,58 +1 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
In addition, [Laracasts](https://laracasts.com) contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
You can also watch bite-sized lessons with real-world projects on [Laravel Learn](https://laravel.com/learn), where you will be guided through building a Laravel application from scratch while learning PHP fundamentals.
|
||||
|
||||
## Agentic Development
|
||||
|
||||
Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install [Laravel Boost](https://laravel.com/docs/ai) to supercharge your AI workflow:
|
||||
|
||||
```bash
|
||||
composer require laravel/boost --dev
|
||||
|
||||
php artisan boost:install
|
||||
```
|
||||
|
||||
Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices.
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
# App
|
||||
11
app/Enums/SmsSendStatus.php
Normal file
11
app/Enums/SmsSendStatus.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SmsSendStatus: string
|
||||
{
|
||||
case Pending = 'pending';
|
||||
case Sent = 'sent';
|
||||
case Failed = 'failed';
|
||||
case SkippedInvalid = 'skipped_invalid';
|
||||
}
|
||||
15
app/Exceptions/CouponPoolExhaustedException.php
Normal file
15
app/Exceptions/CouponPoolExhaustedException.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class CouponPoolExhaustedException extends RuntimeException
|
||||
{
|
||||
public const MESSAGE = 'Aksiýa tamamlandy. Gyzyklanmagyňyz üçin sag boluň.';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::MESSAGE);
|
||||
}
|
||||
}
|
||||
273
app/Filament/Pages/SendSms.php
Normal file
273
app/Filament/Pages/SendSms.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\Coupon;
|
||||
use App\Services\SmsBroadcastService;
|
||||
use App\Services\SmsMessageAnalyzer;
|
||||
use BackedEnum;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Schemas\Components\Actions;
|
||||
use Filament\Schemas\Components\EmbeddedSchema;
|
||||
use Filament\Schemas\Components\Form;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* @property-read Schema $form
|
||||
*/
|
||||
class SendSms extends Page
|
||||
{
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedChatBubbleLeftRight;
|
||||
|
||||
protected static ?string $navigationLabel = null;
|
||||
|
||||
protected static ?string $title = null;
|
||||
|
||||
protected static ?int $navigationSort = 2;
|
||||
|
||||
protected string $view = 'filament.pages.send-sms';
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>|null
|
||||
*/
|
||||
public ?array $data = [];
|
||||
|
||||
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([
|
||||
'message' => '',
|
||||
'send_to_all' => true,
|
||||
'coupon_ids' => [],
|
||||
'excluded_coupon_ids' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function defaultForm(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->statePath('data')
|
||||
->columns(1);
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make(__('filament.send_sms.sections.message'))
|
||||
->schema([
|
||||
Textarea::make('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(__('filament.send_sms.sections.recipients'))
|
||||
->description(__('filament.send_sms.sections.recipients_description'))
|
||||
->schema([
|
||||
Checkbox::make('send_to_all')
|
||||
->label(__('filament.send_sms.fields.send_to_all'))
|
||||
->helperText(__('filament.send_sms.fields.send_to_all_helper'))
|
||||
->live(),
|
||||
Select::make('coupon_ids')
|
||||
->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(__('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(__('filament.send_sms.fields.excluded_coupon_ids'))
|
||||
->multiple()
|
||||
->searchable()
|
||||
->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)),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function content(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Form::make([EmbeddedSchema::make('form')])
|
||||
->id('send-sms-form')
|
||||
->livewireSubmitHandler('sendCampaign')
|
||||
->footer([
|
||||
Actions::make([
|
||||
Action::make('send')
|
||||
->label(__('filament.send_sms.actions.send'))
|
||||
->icon(Heroicon::OutlinedPaperAirplane)
|
||||
->requiresConfirmation()
|
||||
->modalHeading(__('filament.send_sms.actions.send_confirm_heading'))
|
||||
->modalDescription(fn (): string => $this->confirmationDescription())
|
||||
->modalSubmitActionLabel(__('filament.send_sms.actions.send_confirm_submit'))
|
||||
->action('sendCampaign'),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function sendCampaign(): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
|
||||
$validator = Validator::make($data, [
|
||||
'message' => ['required', 'string', 'max:1000'],
|
||||
'send_to_all' => ['required', 'boolean'],
|
||||
'coupon_ids' => [
|
||||
Rule::requiredIf(fn (): bool => ! ($data['send_to_all'] ?? false)),
|
||||
'array',
|
||||
],
|
||||
'coupon_ids.*' => ['integer', 'exists:coupons,id'],
|
||||
'excluded_coupon_ids' => ['nullable', 'array'],
|
||||
'excluded_coupon_ids.*' => ['integer', 'exists:coupons,id'],
|
||||
]);
|
||||
|
||||
$validator->validate();
|
||||
|
||||
$sendToAll = (bool) $data['send_to_all'];
|
||||
$selectedIds = array_map('intval', $data['coupon_ids'] ?? []);
|
||||
$excludedIds = array_map('intval', $data['excluded_coupon_ids'] ?? []);
|
||||
|
||||
$service = app(SmsBroadcastService::class);
|
||||
$recipients = $service->resolveRecipients($sendToAll, $selectedIds, $excludedIds)
|
||||
->filter(fn (Coupon $coupon): bool => is_valid_coupon_phone($coupon->phone));
|
||||
|
||||
$willReceive = $recipients->count();
|
||||
|
||||
if ($willReceive === 0) {
|
||||
Notification::make()
|
||||
->title(__('filament.send_sms.notifications.no_valid_recipients'))
|
||||
->body(__('filament.send_sms.notifications.no_valid_recipients_body'))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$messages = $recipients->map(fn (Coupon $coupon): array => [
|
||||
'phone' => '+993'.$coupon->phone,
|
||||
'message' => $data['message'],
|
||||
])->values()->all();
|
||||
|
||||
$success = sendBulkSMS($messages);
|
||||
|
||||
if ($success) {
|
||||
Notification::make()
|
||||
->title(__('filament.send_sms.notifications.submitted'))
|
||||
->body(__('filament.send_sms.notifications.submitted_body', ['count' => $willReceive]))
|
||||
->success()
|
||||
->send();
|
||||
} else {
|
||||
Notification::make()
|
||||
->title(__('filament.send_sms.notifications.failed'))
|
||||
->body(__('filament.send_sms.notifications.failed_body'))
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
}
|
||||
|
||||
protected function confirmationDescription(): string
|
||||
{
|
||||
$message = (string) ($this->data['message'] ?? '');
|
||||
$analyzer = app(SmsMessageAnalyzer::class);
|
||||
$willReceive = $this->getWillReceiveCount();
|
||||
|
||||
return __('filament.send_sms.confirmation', [
|
||||
'count' => $willReceive,
|
||||
'summary' => $analyzer->summary($message),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getWillReceiveCount(): int
|
||||
{
|
||||
$data = $this->data ?? [];
|
||||
|
||||
return app(SmsBroadcastService::class)
|
||||
->resolveRecipients(
|
||||
(bool) ($data['send_to_all'] ?? false),
|
||||
array_map('intval', $data['coupon_ids'] ?? []),
|
||||
array_map('intval', $data['excluded_coupon_ids'] ?? []),
|
||||
)
|
||||
->filter(fn (Coupon $coupon): bool => is_valid_coupon_phone($coupon->phone))
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
protected function searchCoupons(string $search): array
|
||||
{
|
||||
$search = trim($search);
|
||||
|
||||
if ($search === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Coupon::query()
|
||||
->where(function ($query) use ($search): void {
|
||||
$query->where('phone', 'like', "%{$search}%")
|
||||
->orWhere('code', 'like', "%{$search}%");
|
||||
|
||||
if (ctype_digit($search)) {
|
||||
$query->orWhere('id', (int) $search);
|
||||
}
|
||||
})
|
||||
->orderBy('id')
|
||||
->limit(50)
|
||||
->get()
|
||||
->mapWithKeys(fn (Coupon $coupon): array => [
|
||||
$coupon->id => $this->couponLabel($coupon),
|
||||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, int|string> $values
|
||||
* @return array<int, string>
|
||||
*/
|
||||
protected function couponLabels(array $values): array
|
||||
{
|
||||
if ($values === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Coupon::query()
|
||||
->whereIn('id', $values)
|
||||
->get()
|
||||
->mapWithKeys(fn (Coupon $coupon): array => [
|
||||
$coupon->id => $this->couponLabel($coupon),
|
||||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
protected function couponLabel(Coupon $coupon): string
|
||||
{
|
||||
return format_phone($coupon->phone);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,57 @@ if (! function_exists('sendSMS')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('sendBulkSMS')) {
|
||||
/**
|
||||
* Send bulk SMS
|
||||
*
|
||||
* @param array<int, array{phone: string, message: string}> $messages
|
||||
*/
|
||||
function sendBulkSMS(array $messages): bool
|
||||
{
|
||||
try {
|
||||
$response = Http::timeout(config('services.sms.timeout'))
|
||||
->connectTimeout(config('services.sms.connect_timeout'))
|
||||
->retry(
|
||||
times: 3,
|
||||
sleepMilliseconds: 50,
|
||||
throw: false,
|
||||
when: function (Throwable $exception, PendingRequest $request): bool {
|
||||
if ($exception instanceof ConnectionException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($exception instanceof RequestException) {
|
||||
return $exception->response->serverError();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
)
|
||||
->post(config('services.sms.url').'/bulk', [
|
||||
'messages' => $messages,
|
||||
]);
|
||||
|
||||
if (! $response->successful()) {
|
||||
Log::error('Bulk SMS API request failed', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable $exception) {
|
||||
Log::error('Bulk SMS API exception', [
|
||||
'message' => $exception->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('unmask_phone')) {
|
||||
/**
|
||||
* Unmask Turkmenistan phone number from TM code +993 6X XX XX XX to 6xxxxxxx
|
||||
@@ -76,6 +127,16 @@ if (! function_exists('unmask_phone')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('is_valid_coupon_phone')) {
|
||||
/**
|
||||
* Whether an 8-digit stored coupon phone can receive SMS.
|
||||
*/
|
||||
function is_valid_coupon_phone(string $phone): bool
|
||||
{
|
||||
return (bool) preg_match('/^6\d{7}$/', $phone);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('format_phone')) {
|
||||
/**
|
||||
* Format an 8-digit TM mobile number for display (+993 6X XX XX XX).
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\OtpVerificationResult;
|
||||
use App\Exceptions\CouponPoolExhaustedException;
|
||||
use App\Http\Requests\SendOtpRequest;
|
||||
use App\Http\Requests\VerifyOtpRequest;
|
||||
use App\Models\Coupon;
|
||||
use App\Services\CouponCodePool;
|
||||
use App\Services\CouponService;
|
||||
use App\Services\OtpService;
|
||||
use Illuminate\Contracts\View\View;
|
||||
@@ -20,6 +22,7 @@ class VerificationController extends Controller
|
||||
public function __construct(
|
||||
private OtpService $otpService,
|
||||
private CouponService $couponService,
|
||||
private CouponCodePool $couponCodePool,
|
||||
) {}
|
||||
|
||||
public function index(): View|RedirectResponse
|
||||
@@ -28,6 +31,10 @@ class VerificationController extends Controller
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
if (! $this->couponCodePool->hasAvailable()) {
|
||||
return view('verification.promotion-ended');
|
||||
}
|
||||
|
||||
return view('verification.index');
|
||||
}
|
||||
|
||||
@@ -44,6 +51,10 @@ class VerificationController extends Controller
|
||||
return back()->withErrors(['phone' => 'Bu telefon belgisi eýýäm ulanyldy.'])->withInput();
|
||||
}
|
||||
|
||||
if (! $this->couponCodePool->hasAvailable()) {
|
||||
return back()->withErrors(['phone' => CouponPoolExhaustedException::MESSAGE])->withInput();
|
||||
}
|
||||
|
||||
if (! $this->otpService->issue($phone)) {
|
||||
return back()->withErrors(['phone' => 'SMS iberilmedi. Soňrak synanyşyň.'])->withInput();
|
||||
}
|
||||
@@ -63,6 +74,12 @@ class VerificationController extends Controller
|
||||
return redirect()->route('verification.index');
|
||||
}
|
||||
|
||||
$phone = session()->get('verify_phone');
|
||||
|
||||
if (! $this->couponCodePool->hasAvailable() && ! Coupon::query()->where('phone', $phone)->exists()) {
|
||||
return redirect()->route('verification.index');
|
||||
}
|
||||
|
||||
return view('verification.verify', [
|
||||
'phone' => session()->get('verify_phone'),
|
||||
]);
|
||||
@@ -84,6 +101,10 @@ class VerificationController extends Controller
|
||||
return redirect()->route('verification.congratulations');
|
||||
}
|
||||
|
||||
if (! $this->couponCodePool->hasAvailable()) {
|
||||
return redirect()->route('verification.index');
|
||||
}
|
||||
|
||||
if (! $this->otpService->issue($phone)) {
|
||||
return back()->withErrors(['otp' => 'SMS iberilmedi. Soňrak synanyşyň.']);
|
||||
}
|
||||
@@ -109,7 +130,11 @@ class VerificationController extends Controller
|
||||
return back()->withErrors(['otp' => 'Nädogry kod.']);
|
||||
}
|
||||
|
||||
try {
|
||||
$coupon = $this->couponService->findOrCreateForPhone($phone);
|
||||
} catch (CouponPoolExhaustedException) {
|
||||
return back()->withErrors(['otp' => CouponPoolExhaustedException::MESSAGE]);
|
||||
}
|
||||
|
||||
session()->put('coupon_code', $coupon->code);
|
||||
|
||||
|
||||
66
app/Models/SmsCampaign.php
Normal file
66
app/Models/SmsCampaign.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\SmsCampaignFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class SmsCampaign extends Model
|
||||
{
|
||||
/** @use HasFactory<SmsCampaignFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'message',
|
||||
'mode',
|
||||
'recipient_count',
|
||||
'sent_count',
|
||||
'failed_count',
|
||||
'skipped_count',
|
||||
'laravel_batch_id',
|
||||
'created_by',
|
||||
'completed_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'completed_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function sendLogs(): HasMany
|
||||
{
|
||||
return $this->hasMany(SmsSendLog::class);
|
||||
}
|
||||
|
||||
public function isComplete(): bool
|
||||
{
|
||||
return $this->completed_at !== null;
|
||||
}
|
||||
|
||||
public function processedCount(): int
|
||||
{
|
||||
return $this->sent_count + $this->failed_count + $this->skipped_count;
|
||||
}
|
||||
|
||||
public function progressPercent(): int
|
||||
{
|
||||
if ($this->recipient_count === 0) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return (int) min(100, round(($this->processedCount() / $this->recipient_count) * 100));
|
||||
}
|
||||
}
|
||||
46
app/Models/SmsSendLog.php
Normal file
46
app/Models/SmsSendLog.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SmsSendStatus;
|
||||
use Database\Factories\SmsSendLogFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class SmsSendLog extends Model
|
||||
{
|
||||
/** @use HasFactory<SmsSendLogFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'sms_campaign_id',
|
||||
'coupon_id',
|
||||
'phone',
|
||||
'message',
|
||||
'status',
|
||||
'error_message',
|
||||
'attempted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => SmsSendStatus::class,
|
||||
'attempted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function campaign(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SmsCampaign::class, 'sms_campaign_id');
|
||||
}
|
||||
|
||||
public function coupon(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Coupon::class);
|
||||
}
|
||||
}
|
||||
77
app/Services/CouponCodePool.php
Normal file
77
app/Services/CouponCodePool.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Coupon;
|
||||
|
||||
class CouponCodePool
|
||||
{
|
||||
/** @var list<string>|null */
|
||||
private ?array $allCodesCache = null;
|
||||
|
||||
public function __construct(
|
||||
private ?string $codesPath = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function allCodes(): array
|
||||
{
|
||||
if ($this->allCodesCache !== null) {
|
||||
return $this->allCodesCache;
|
||||
}
|
||||
|
||||
$path = $this->codesPath ?? resource_path('codes/codes.txt');
|
||||
|
||||
if (! is_readable($path)) {
|
||||
throw new \RuntimeException("Coupon codes file is not readable: {$path}");
|
||||
}
|
||||
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
if ($lines === false) {
|
||||
throw new \RuntimeException("Unable to read coupon codes file: {$path}");
|
||||
}
|
||||
|
||||
$codes = array_values(array_filter(array_map(trim(...), $lines)));
|
||||
|
||||
if ($codes === []) {
|
||||
throw new \RuntimeException("Coupon codes file is empty: {$path}");
|
||||
}
|
||||
|
||||
return $this->allCodesCache = $codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function usedCodes(): array
|
||||
{
|
||||
return Coupon::query()->pluck('code')->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function availableCodes(): array
|
||||
{
|
||||
return array_values(array_diff($this->allCodes(), $this->usedCodes()));
|
||||
}
|
||||
|
||||
public function hasAvailable(): bool
|
||||
{
|
||||
return $this->availableCodes() !== [];
|
||||
}
|
||||
|
||||
public function pickRandom(): ?string
|
||||
{
|
||||
$available = $this->availableCodes();
|
||||
|
||||
if ($available === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $available[array_rand($available)];
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Exceptions\CouponPoolExhaustedException;
|
||||
use App\Models\Coupon;
|
||||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CouponService
|
||||
{
|
||||
public function __construct(
|
||||
private CouponCodePool $couponCodePool,
|
||||
) {}
|
||||
|
||||
public function findOrCreateForPhone(string $phone): Coupon
|
||||
{
|
||||
$existing = Coupon::query()->where('phone', $phone)->first();
|
||||
@@ -22,24 +26,22 @@ class CouponService
|
||||
private function createWithUniqueCode(string $phone): Coupon
|
||||
{
|
||||
for ($attempt = 0; $attempt < 10; $attempt++) {
|
||||
$code = $this->couponCodePool->pickRandom();
|
||||
|
||||
if ($code === null) {
|
||||
throw new CouponPoolExhaustedException;
|
||||
}
|
||||
|
||||
try {
|
||||
return Coupon::query()->create([
|
||||
'phone' => $phone,
|
||||
'code' => $this->generateCouponCode(),
|
||||
'code' => $code,
|
||||
]);
|
||||
} catch (UniqueConstraintViolationException) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Unable to generate a unique coupon code.');
|
||||
}
|
||||
|
||||
private function generateCouponCode(): string
|
||||
{
|
||||
$x = random_int(1, 9);
|
||||
$yyyy = Str::upper(Str::random(4));
|
||||
|
||||
return "{$x}_{$yyyy}";
|
||||
throw new CouponPoolExhaustedException;
|
||||
}
|
||||
}
|
||||
|
||||
32
app/Services/SmsBroadcastService.php
Normal file
32
app/Services/SmsBroadcastService.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Coupon;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class SmsBroadcastService
|
||||
{
|
||||
/**
|
||||
* @param array<int, int|string> $selectedIds
|
||||
* @param array<int, int|string> $excludedIds
|
||||
* @return Collection<int, Coupon>
|
||||
*/
|
||||
public function resolveRecipients(bool $sendToAll, array $selectedIds = [], array $excludedIds = []): Collection
|
||||
{
|
||||
$excludedIds = array_values(array_unique(array_map('intval', $excludedIds)));
|
||||
|
||||
if ($sendToAll) {
|
||||
$query = Coupon::query();
|
||||
} else {
|
||||
$selectedIds = array_values(array_unique(array_map('intval', $selectedIds)));
|
||||
$query = Coupon::query()->whereIn('id', $selectedIds);
|
||||
}
|
||||
|
||||
if ($excludedIds !== []) {
|
||||
$query->whereNotIn('id', $excludedIds);
|
||||
}
|
||||
|
||||
return $query->orderBy('id')->get();
|
||||
}
|
||||
}
|
||||
70
app/Services/SmsMessageAnalyzer.php
Normal file
70
app/Services/SmsMessageAnalyzer.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class SmsMessageAnalyzer
|
||||
{
|
||||
private const GSM_SINGLE_LIMIT = 160;
|
||||
|
||||
private const GSM_MULTI_LIMIT = 153;
|
||||
|
||||
private const UCS2_SINGLE_LIMIT = 70;
|
||||
|
||||
private const UCS2_MULTI_LIMIT = 67;
|
||||
|
||||
public function characterCount(string $message): int
|
||||
{
|
||||
return mb_strlen($message);
|
||||
}
|
||||
|
||||
public function isGsm7(string $message): bool
|
||||
{
|
||||
if ($message === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! preg_match('/[^\x00-\x7F]/', $message);
|
||||
}
|
||||
|
||||
public function estimatedParts(string $message): int
|
||||
{
|
||||
$length = $this->characterCount($message);
|
||||
|
||||
if ($length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->isGsm7($message)) {
|
||||
if ($length <= self::GSM_SINGLE_LIMIT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (int) ceil($length / self::GSM_MULTI_LIMIT);
|
||||
}
|
||||
|
||||
if ($length <= self::UCS2_SINGLE_LIMIT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (int) ceil($length / self::UCS2_MULTI_LIMIT);
|
||||
}
|
||||
|
||||
public function summary(string $message): string
|
||||
{
|
||||
$characters = $this->characterCount($message);
|
||||
$parts = $this->estimatedParts($message);
|
||||
|
||||
if ($parts === 0) {
|
||||
return __('filament.sms_analyzer.zero_characters');
|
||||
}
|
||||
|
||||
$smsLabel = $parts === 1
|
||||
? __('filament.sms_analyzer.one_sms')
|
||||
: __('filament.sms_analyzer.multiple_sms', ['count' => $parts]);
|
||||
|
||||
return __('filament.sms_analyzer.characters_sms', [
|
||||
'characters' => $characters,
|
||||
'sms' => $smsLabel,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ return [
|
||||
'url' => env('SMS_API_URL', 'https://sms.daragt.com/api/sms'),
|
||||
'timeout' => (int) env('SMS_API_TIMEOUT', 10),
|
||||
'connect_timeout' => (int) env('SMS_API_CONNECT_TIMEOUT', 5),
|
||||
'bulk_delay_ms' => (int) env('SMS_BULK_DELAY_MS', 300),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -20,7 +20,7 @@ class CouponFactory extends Factory
|
||||
{
|
||||
return [
|
||||
'phone' => '6'.fake()->unique()->numerify('#######'),
|
||||
'code' => random_int(1, 9).'_'.Str::upper(Str::random(4)),
|
||||
'code' => 'TEST_'.Str::upper(Str::random(8)),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
39
database/factories/SmsCampaignFactory.php
Normal file
39
database/factories/SmsCampaignFactory.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\SmsCampaign;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<SmsCampaign>
|
||||
*/
|
||||
class SmsCampaignFactory extends Factory
|
||||
{
|
||||
protected $model = SmsCampaign::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'message' => fake()->sentence(),
|
||||
'mode' => 'all',
|
||||
'recipient_count' => 0,
|
||||
'sent_count' => 0,
|
||||
'failed_count' => 0,
|
||||
'skipped_count' => 0,
|
||||
'created_by' => User::factory(),
|
||||
'completed_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
public function completed(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes): array => [
|
||||
'completed_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
36
database/factories/SmsSendLogFactory.php
Normal file
36
database/factories/SmsSendLogFactory.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\SmsSendStatus;
|
||||
use App\Models\Coupon;
|
||||
use App\Models\SmsCampaign;
|
||||
use App\Models\SmsSendLog;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<SmsSendLog>
|
||||
*/
|
||||
class SmsSendLogFactory extends Factory
|
||||
{
|
||||
protected $model = SmsSendLog::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$campaign = SmsCampaign::factory()->create();
|
||||
$coupon = Coupon::factory()->create();
|
||||
|
||||
return [
|
||||
'sms_campaign_id' => $campaign->id,
|
||||
'coupon_id' => $coupon->id,
|
||||
'phone' => $coupon->phone,
|
||||
'message' => $campaign->message,
|
||||
'status' => SmsSendStatus::Pending,
|
||||
'error_message' => null,
|
||||
'attempted_at' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sms_campaigns', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('message');
|
||||
$table->string('mode');
|
||||
$table->unsignedInteger('recipient_count')->default(0);
|
||||
$table->unsignedInteger('sent_count')->default(0);
|
||||
$table->unsignedInteger('failed_count')->default(0);
|
||||
$table->unsignedInteger('skipped_count')->default(0);
|
||||
$table->string('laravel_batch_id')->nullable();
|
||||
$table->foreignId('created_by')->constrained('users')->cascadeOnDelete();
|
||||
$table->timestamp('completed_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sms_campaigns');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sms_send_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('sms_campaign_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('coupon_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('phone');
|
||||
$table->text('message');
|
||||
$table->string('status');
|
||||
$table->text('error_message')->nullable();
|
||||
$table->timestamp('attempted_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['sms_campaign_id', 'status']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sms_send_logs');
|
||||
}
|
||||
};
|
||||
9
lang/tk/auth.php
Normal file
9
lang/tk/auth.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'failed' => 'Bu maglumatlar biziň ýazgylarymyza gabat gelenok.',
|
||||
'password' => 'Berlen parol nädogry.',
|
||||
'throttle' => 'Giriş synanyşyklary köp. :seconds sekuntdan soň gaýtadan synanyşyň.',
|
||||
|
||||
];
|
||||
74
lang/tk/filament.php
Normal file
74
lang/tk/filament.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'coupons' => [
|
||||
'navigation' => 'Kuponlar',
|
||||
],
|
||||
|
||||
'phone_verifications' => [
|
||||
'navigation' => 'OTP tassyklamalary',
|
||||
'model' => 'OTP tassyklama',
|
||||
'plural' => 'OTP tassyklamalary',
|
||||
],
|
||||
|
||||
'send_sms' => [
|
||||
'navigation' => 'SMS ibermek',
|
||||
'title' => 'SMS ibermek',
|
||||
'sections' => [
|
||||
'message' => 'Hat',
|
||||
'message_description' => 'SMS-ni alýanlar görmeli bolşy ýaly takyk ýazyň.',
|
||||
'recipients' => 'Alyjylar',
|
||||
'recipients_description' => 'Hemmä ibermek, belli kupon eýelerini saýlamak ýa-da sanlary aýyrmak.',
|
||||
],
|
||||
'fields' => [
|
||||
'message' => 'SMS habary',
|
||||
'send_to_all' => 'Ähli kupon eýelerine ibermek',
|
||||
'send_to_all_helper' => 'Açyk bolsa, ähli kupon eýeleri girýär. El bilen saýlaw öçürilýär.',
|
||||
'coupon_ids' => 'Alyjylary saýlaň',
|
||||
'coupon_ids_helper' => 'Telefon, kupon kody ýa-da ID boýunça gözleg.',
|
||||
'excluded_coupon_ids' => 'Alyjylary aýyrmak',
|
||||
'excluded_coupon_ids_helper' => 'Aýrylan sanlar bu habary almaz, hatda ähläne iberilende hem.',
|
||||
],
|
||||
'actions' => [
|
||||
'send' => 'SMS ibermek',
|
||||
'send_confirm_heading' => 'SMS häzir ibermeli?',
|
||||
'send_confirm_submit' => 'Iber',
|
||||
],
|
||||
'notifications' => [
|
||||
'no_valid_recipients' => 'Dogry alyjy ýok',
|
||||
'no_valid_recipients_body' => 'Saýlawy ýa-da aýyrmalary üýtgediň — dogry telefon belgili kupon eýesi bu habary almaz.',
|
||||
'submitted' => 'SMS kampaniýasy iberildi',
|
||||
'submitted_body' => ':count alyja iberildi.',
|
||||
'failed' => 'SMS ibermek başa barmady',
|
||||
'failed_body' => 'Näsazlyk ýüze çykdy. Täzeden synanyşyň.',
|
||||
],
|
||||
'confirmation' => ':count alyja ibermeli? :summary',
|
||||
],
|
||||
|
||||
'filters' => [
|
||||
'date' => 'Sene',
|
||||
'from' => 'Başlanýan',
|
||||
'until' => 'Gutarýan',
|
||||
'from_indicator' => 'Başlanýan :date',
|
||||
'until_indicator' => 'Gutarýan :date',
|
||||
],
|
||||
|
||||
'sms_analyzer' => [
|
||||
'zero_characters' => '0 harp',
|
||||
'characters_sms' => ':characters harp · :sms',
|
||||
'one_sms' => '1 SMS',
|
||||
'multiple_sms' => ':count SMS',
|
||||
],
|
||||
|
||||
'fields' => [
|
||||
'id' => 'ID',
|
||||
'phone' => 'Telefon',
|
||||
'code' => 'Kupon kody',
|
||||
'created_at' => 'Döredilen',
|
||||
'updated_at' => 'Üýtgedilen',
|
||||
'expires_at' => 'Gutarýan wagty',
|
||||
'verified' => 'Tassyklanan',
|
||||
],
|
||||
|
||||
];
|
||||
34
lang/tk/validation.php
Normal file
34
lang/tk/validation.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'accepted' => ':attribute kabul edilmeli.',
|
||||
'array' => ':attribute massiw bolmaly.',
|
||||
'boolean' => ':attribute dogry ýa-da ýalňyş bolmaly.',
|
||||
'confirmed' => ':attribute tassyklamasy gabat gelenok.',
|
||||
'email' => ':attribute dogry e-poçta salgysy bolmaly.',
|
||||
'exists' => 'Saýlanan :attribute nädogry.',
|
||||
'integer' => ':attribute san bolmaly.',
|
||||
'max' => [
|
||||
'array' => ':attribute iň köp :max elementden ybarat bolmaly.',
|
||||
'numeric' => ':attribute iň köp :max bolmaly.',
|
||||
'string' => ':attribute iň köp :max harp bolmaly.',
|
||||
],
|
||||
'min' => [
|
||||
'array' => ':attribute iň az :min elementden ybarat bolmaly.',
|
||||
'numeric' => ':attribute iň az :min bolmaly.',
|
||||
'string' => ':attribute iň az :min harp bolmaly.',
|
||||
],
|
||||
'required' => ':attribute meýdany hökmany.',
|
||||
'string' => ':attribute setir bolmaly.',
|
||||
|
||||
'attributes' => [
|
||||
'email' => 'e-poçta salgysy',
|
||||
'password' => 'parol',
|
||||
'message' => 'SMS habary',
|
||||
'send_to_all' => 'ähli kupon eýelerine ibermek',
|
||||
'coupon_ids' => 'alyjylar',
|
||||
'excluded_coupon_ids' => 'aýrylan alyjylar',
|
||||
],
|
||||
|
||||
];
|
||||
28
lang/vendor/filament-actions/tk/associate.php
vendored
Normal file
28
lang/vendor/filament-actions/tk/associate.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'İlişkilendir',
|
||||
'modal' => [
|
||||
'heading' => ':label İlişkilendir',
|
||||
'fields' => [
|
||||
'record_id' => [
|
||||
'label' => 'Kayıt',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'associate' => [
|
||||
'label' => 'İlişkilendir',
|
||||
],
|
||||
'associate_another' => [
|
||||
'label' => 'İlişkilendir ve başka bir taneye başla',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'associated' => [
|
||||
'title' => 'İlişkilendirildi',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
28
lang/vendor/filament-actions/tk/attach.php
vendored
Normal file
28
lang/vendor/filament-actions/tk/attach.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'İliştir',
|
||||
'modal' => [
|
||||
'heading' => ':label iliştir',
|
||||
'fields' => [
|
||||
'record_id' => [
|
||||
'label' => 'Kayıt',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'attach' => [
|
||||
'label' => 'İliştir',
|
||||
],
|
||||
'attach_another' => [
|
||||
'label' => 'İliştir ve başka bir taneye başla',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'attached' => [
|
||||
'title' => 'İliştirildi',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
23
lang/vendor/filament-actions/tk/create.php
vendored
Normal file
23
lang/vendor/filament-actions/tk/create.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => ':label Oluştur',
|
||||
'modal' => [
|
||||
'heading' => ':label oluştur',
|
||||
'actions' => [
|
||||
'create' => [
|
||||
'label' => 'Döret',
|
||||
],
|
||||
'create_another' => [
|
||||
'label' => 'Oluştur & yeni oluştur',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'created' => [
|
||||
'title' => 'Oluşturuldu',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
73
lang/vendor/filament-actions/tk/delete.php
vendored
Normal file
73
lang/vendor/filament-actions/tk/delete.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'single' => [
|
||||
|
||||
'label' => 'Poz',
|
||||
|
||||
'modal' => [
|
||||
|
||||
'heading' => ':label poz',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'delete' => [
|
||||
'label' => 'Poz',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'notifications' => [
|
||||
|
||||
'deleted' => [
|
||||
'title' => 'Pozuldy',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'multiple' => [
|
||||
|
||||
'label' => 'Saýlananlary poz',
|
||||
|
||||
'modal' => [
|
||||
|
||||
'heading' => 'Saýlananlary poz',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'delete' => [
|
||||
'label' => 'Poz',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'notifications' => [
|
||||
|
||||
'deleted' => [
|
||||
'title' => 'Pozuldy',
|
||||
],
|
||||
|
||||
'deleted_partial' => [
|
||||
'title' => ':total ýazgydan :count pozuldy',
|
||||
'missing_authorization_failure_message' => ':count ýazgy pozmak üçin ygtyýaryňyz ýok.',
|
||||
'missing_processing_failure_message' => ':count ýazgy pozulmady.',
|
||||
],
|
||||
|
||||
'deleted_none' => [
|
||||
'title' => 'Ýazgylar pozulmady',
|
||||
'missing_authorization_failure_message' => ':count ýazgy pozmak üçin ygtyýaryňyz ýok.',
|
||||
'missing_processing_failure_message' => ':count ýazgy pozulmady.',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
36
lang/vendor/filament-actions/tk/detach.php
vendored
Normal file
36
lang/vendor/filament-actions/tk/detach.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Ayır',
|
||||
'modal' => [
|
||||
'heading' => ':label ayır',
|
||||
'actions' => [
|
||||
'detach' => [
|
||||
'label' => 'Ayır',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'detached' => [
|
||||
'title' => 'Ayrıldı',
|
||||
],
|
||||
],
|
||||
],
|
||||
'multiple' => [
|
||||
'label' => 'Seçiliyi ayır',
|
||||
'modal' => [
|
||||
'heading' => ':label seçiliyi ayır ',
|
||||
'actions' => [
|
||||
'detach' => [
|
||||
'label' => 'Seçiliyi ayır',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'detached' => [
|
||||
'title' => 'Ayrıldı',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
36
lang/vendor/filament-actions/tk/dissociate.php
vendored
Normal file
36
lang/vendor/filament-actions/tk/dissociate.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Ayrıştır',
|
||||
'modal' => [
|
||||
'heading' => ':label ayrıştır',
|
||||
'actions' => [
|
||||
'dissociate' => [
|
||||
'label' => 'Ayrıştır',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'dissociated' => [
|
||||
'title' => 'Ayrıştırıldı',
|
||||
],
|
||||
],
|
||||
],
|
||||
'multiple' => [
|
||||
'label' => 'Seçiliyi ayrıştır',
|
||||
'modal' => [
|
||||
'heading' => ':label seçiliyi ayrıştır',
|
||||
'actions' => [
|
||||
'dissociate' => [
|
||||
'label' => 'Seçiliyi ayrıştır',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'dissociated' => [
|
||||
'title' => 'Ayrıştırıldı',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
20
lang/vendor/filament-actions/tk/edit.php
vendored
Normal file
20
lang/vendor/filament-actions/tk/edit.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Üýtget',
|
||||
'modal' => [
|
||||
'heading' => ':label üýtget',
|
||||
'actions' => [
|
||||
'save' => [
|
||||
'label' => 'Sakla',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'saved' => [
|
||||
'title' => 'Kaydedildi',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
48
lang/vendor/filament-actions/tk/export.php
vendored
Normal file
48
lang/vendor/filament-actions/tk/export.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Dışa Aktar :label',
|
||||
'modal' => [
|
||||
'heading' => 'Dışa Aktar :label',
|
||||
'form' => [
|
||||
'columns' => [
|
||||
'label' => 'Sütünler',
|
||||
'form' => [
|
||||
'is_enabled' => [
|
||||
'label' => ':column etkin',
|
||||
],
|
||||
'label' => [
|
||||
'label' => ':column etiketi',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'export' => [
|
||||
'label' => 'Dışa Aktar',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'completed' => [
|
||||
'title' => 'Dışa Aktarım Tamamlandı',
|
||||
'actions' => [
|
||||
'download_csv' => [
|
||||
'label' => '.csv Olarak İndir',
|
||||
],
|
||||
'download_xlsx' => [
|
||||
'label' => '.xlsx Olarak İndir',
|
||||
],
|
||||
],
|
||||
],
|
||||
'max_rows' => [
|
||||
'title' => 'Maksimum Satır Sayısı Aşıldı',
|
||||
'body' => 'Birden fazla satırı dışa aktaramazsınız.|:count satırı dışa aktaramazsınız.',
|
||||
],
|
||||
'started' => [
|
||||
'title' => 'Dışa Aktarım Başladı',
|
||||
'body' => 'Dışa aktarım başladı ve 1 satır arka planda işlenecek.|Dışa aktarım başladı ve :count satır arka planda işlenecek.',
|
||||
],
|
||||
],
|
||||
'file_name' => 'export-:export_id-:model',
|
||||
];
|
||||
46
lang/vendor/filament-actions/tk/force-delete.php
vendored
Normal file
46
lang/vendor/filament-actions/tk/force-delete.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Kalıcı olarak sil',
|
||||
'modal' => [
|
||||
'heading' => ':label kalıcı olarak sil',
|
||||
'actions' => [
|
||||
'delete' => [
|
||||
'label' => 'Kalıcı olarak sil',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'deleted' => [
|
||||
'title' => 'Kayıt kalıcı olarak silindi',
|
||||
],
|
||||
],
|
||||
],
|
||||
'multiple' => [
|
||||
'label' => 'Seçiliyi kalıcı olarak sil',
|
||||
'modal' => [
|
||||
'heading' => ':label seçiliyi kalıcı olarak sil',
|
||||
'actions' => [
|
||||
'delete' => [
|
||||
'label' => 'Kalıcı olarak sil',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'deleted' => [
|
||||
'title' => 'Kayıtlar kalıcı olarak silindi',
|
||||
],
|
||||
'deleted_partial' => [
|
||||
'title' => ':total kayıttan :count kayıt kalıcı olarak silindi',
|
||||
'missing_authorization_failure_message' => ':count kayıtı kalıcı olarak silmek için gereken izniniz yok.',
|
||||
'missing_processing_failure_message' => ':count kayıt kalıcı olarak silinemedi.',
|
||||
],
|
||||
'deleted_none' => [
|
||||
'title' => 'Kayıtlar kalıcı olarak silinemedi',
|
||||
'missing_authorization_failure_message' => ':count kayıtı kalıcı olarak silmek için gereken izniniz yok.',
|
||||
'missing_processing_failure_message' => ':count kayıt kalıcı olarak silinemedi.',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
7
lang/vendor/filament-actions/tk/group.php
vendored
Normal file
7
lang/vendor/filament-actions/tk/group.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'trigger' => [
|
||||
'label' => 'Eylemler',
|
||||
],
|
||||
];
|
||||
56
lang/vendor/filament-actions/tk/import.php
vendored
Normal file
56
lang/vendor/filament-actions/tk/import.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'İçe Aktar: :label',
|
||||
'modal' => [
|
||||
'heading' => 'İçe Aktar: :label',
|
||||
'form' => [
|
||||
'file' => [
|
||||
'label' => 'Dosya',
|
||||
'placeholder' => 'Bir CSV dosyası seçin',
|
||||
'rules' => [
|
||||
'duplicate_columns' => '{0} Dosya birden fazla boş sütun başlığı içeremez.|{1,*} Dosya tekrar eden sütun başlığı içeremez: :columns.',
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'label' => 'Sütünler',
|
||||
'placeholder' => 'Sütunları eşleştirin',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'download_example' => [
|
||||
'label' => 'Örnek CSV Dosyasını İndir',
|
||||
],
|
||||
'import' => [
|
||||
'label' => 'İçe Aktar',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'completed' => [
|
||||
'title' => 'İçe Aktarım Tamamlandı',
|
||||
'actions' => [
|
||||
'download_failed_rows_csv' => [
|
||||
'label' => 'Başarısız satır hakkında bilgileri indir|Başarısız satırlar hakkında bilgileri indir',
|
||||
],
|
||||
],
|
||||
],
|
||||
'max_rows' => [
|
||||
'title' => 'Yüklenen Dosya Çok Büyük',
|
||||
'body' => 'Aynı anda 1\'den fazla satır içeren dosyaları içe aktaramazsınız.|Aynı anda :count\'den fazla satır içeren dosyaları içe aktaramazsınız.',
|
||||
],
|
||||
'started' => [
|
||||
'title' => 'İçe Aktarım Başladı',
|
||||
'body' => 'İçe aktarım başladı ve 1 satır arka planda işlenecek.|İçe aktarım başladı ve :count satır arka planda işlenecek.',
|
||||
],
|
||||
],
|
||||
'example_csv' => [
|
||||
'file_name' => ':importer-example',
|
||||
],
|
||||
'failure_csv' => [
|
||||
'file_name' => 'import-:import_id-:csv_name-failed-rows',
|
||||
'error_header' => 'error',
|
||||
'system_error' => 'Sistem Hatası',
|
||||
'column_mapping_required_for_new_record' => ':attribute sütunu dosyadaki bir sütun ile eşleştirilmedi, fakat bu sütun yeni kayıt oluşturmak için gerekli bir sütun.',
|
||||
],
|
||||
];
|
||||
16
lang/vendor/filament-actions/tk/modal.php
vendored
Normal file
16
lang/vendor/filament-actions/tk/modal.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'confirmation' => 'Bunu yapmak istediğinizden emin misiniz?',
|
||||
'actions' => [
|
||||
'cancel' => [
|
||||
'label' => 'Ýatyr',
|
||||
],
|
||||
'confirm' => [
|
||||
'label' => 'Tassykla',
|
||||
],
|
||||
'submit' => [
|
||||
'label' => 'Iber',
|
||||
],
|
||||
],
|
||||
];
|
||||
8
lang/vendor/filament-actions/tk/notifications.php
vendored
Normal file
8
lang/vendor/filament-actions/tk/notifications.php
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'throttled' => [
|
||||
'title' => 'Çok Fazla Deneme Yapıldı',
|
||||
'body' => 'Çok fazla deneme yapıldı, lütfen :seconds saniye sonra tekrar deneyin.',
|
||||
],
|
||||
];
|
||||
20
lang/vendor/filament-actions/tk/replicate.php
vendored
Normal file
20
lang/vendor/filament-actions/tk/replicate.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Çoğalt',
|
||||
'modal' => [
|
||||
'heading' => ':label çoğalt',
|
||||
'actions' => [
|
||||
'replicate' => [
|
||||
'label' => 'Çoğalt',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'replicated' => [
|
||||
'title' => 'Kayıt çoğaltıldı',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
46
lang/vendor/filament-actions/tk/restore.php
vendored
Normal file
46
lang/vendor/filament-actions/tk/restore.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Geri yükle',
|
||||
'modal' => [
|
||||
'heading' => ':label geri yükle',
|
||||
'actions' => [
|
||||
'restore' => [
|
||||
'label' => 'Geri yükle',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'restored' => [
|
||||
'title' => 'Kayıt geri yüklendi',
|
||||
],
|
||||
],
|
||||
],
|
||||
'multiple' => [
|
||||
'label' => 'Seçileni geri yükle',
|
||||
'modal' => [
|
||||
'heading' => ':label seçileni geri yükle',
|
||||
'actions' => [
|
||||
'restore' => [
|
||||
'label' => 'Geri yükle',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'restored' => [
|
||||
'title' => 'Kayıtlar geri yüklendi',
|
||||
],
|
||||
'restored_partial' => [
|
||||
'title' => ':total kayıttan :count kayıt geri yüklendi',
|
||||
'missing_authorization_failure_message' => ':count kayıt geri yüklemek için gereken izniniz yok.',
|
||||
'missing_processing_failure_message' => ':count geri yüklenemedi.',
|
||||
],
|
||||
'restored_none' => [
|
||||
'title' => 'Kayıtlar geri yüklenemedi',
|
||||
'missing_authorization_failure_message' => ':count kayıt geri yüklemek için gereken izniniz yok.',
|
||||
'missing_processing_failure_message' => ':count geri yüklenemedi.',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
15
lang/vendor/filament-actions/tk/view.php
vendored
Normal file
15
lang/vendor/filament-actions/tk/view.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'single' => [
|
||||
'label' => 'Gör',
|
||||
'modal' => [
|
||||
'heading' => ':label gör',
|
||||
'actions' => [
|
||||
'close' => [
|
||||
'label' => 'Ýap',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
498
lang/vendor/filament-forms/tk/components.php
vendored
Normal file
498
lang/vendor/filament-forms/tk/components.php
vendored
Normal file
@@ -0,0 +1,498 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'builder' => [
|
||||
'actions' => [
|
||||
'clone' => [
|
||||
'label' => 'Klonla',
|
||||
],
|
||||
'add' => [
|
||||
'label' => ':label\'e Ekle',
|
||||
'modal' => [
|
||||
'heading' => ':label\'e Ekle',
|
||||
'actions' => [
|
||||
'add' => [
|
||||
'label' => 'Goş',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'add_between' => [
|
||||
'label' => 'Bloklar arasına ekle',
|
||||
'modal' => [
|
||||
'heading' => ':label\'e Ekle',
|
||||
'actions' => [
|
||||
'add' => [
|
||||
'label' => 'Goş',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'delete' => [
|
||||
'label' => 'Poz',
|
||||
],
|
||||
'edit' => [
|
||||
'label' => 'Üýtget',
|
||||
'modal' => [
|
||||
'heading' => 'Bloğu Düzenle',
|
||||
'actions' => [
|
||||
'save' => [
|
||||
'label' => 'Değişiklikleri Kaydet',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'reorder' => [
|
||||
'label' => 'Taşı',
|
||||
],
|
||||
'move_down' => [
|
||||
'label' => 'Aşağı taşı',
|
||||
],
|
||||
'move_up' => [
|
||||
'label' => 'Yukarı taşı',
|
||||
],
|
||||
'collapse' => [
|
||||
'label' => 'Daralt',
|
||||
],
|
||||
'expand' => [
|
||||
'label' => 'Genişlet',
|
||||
],
|
||||
'collapse_all' => [
|
||||
'label' => 'Tümünü daralt',
|
||||
],
|
||||
'expand_all' => [
|
||||
'label' => 'Tümünü genişlet',
|
||||
],
|
||||
],
|
||||
],
|
||||
'checkbox_list' => [
|
||||
'actions' => [
|
||||
'deselect_all' => [
|
||||
'label' => 'Tüm seçimi kaldır',
|
||||
],
|
||||
'select_all' => [
|
||||
'label' => 'Hemmesini saýla',
|
||||
],
|
||||
],
|
||||
],
|
||||
'file_upload' => [
|
||||
'editor' => [
|
||||
'actions' => [
|
||||
'cancel' => [
|
||||
'label' => 'Ýatyr',
|
||||
],
|
||||
'drag_crop' => [
|
||||
'label' => 'Sürükleme modu "kırpma"',
|
||||
],
|
||||
'drag_move' => [
|
||||
'label' => 'Sürükleme modu "taşıma"',
|
||||
],
|
||||
'flip_horizontal' => [
|
||||
'label' => 'Görüntüyü yatay olarak çevir',
|
||||
],
|
||||
'flip_vertical' => [
|
||||
'label' => 'Görüntüyü dikey olarak çevir',
|
||||
],
|
||||
'move_down' => [
|
||||
'label' => 'Görüntüyü aşağı taşı',
|
||||
],
|
||||
'move_left' => [
|
||||
'label' => 'Görüntüyü sola taşı',
|
||||
],
|
||||
'move_right' => [
|
||||
'label' => 'Görüntüyü sağa taşı',
|
||||
],
|
||||
'move_up' => [
|
||||
'label' => 'Görüntüyü yukarı taşı',
|
||||
],
|
||||
'reset' => [
|
||||
'label' => 'Täzeden',
|
||||
],
|
||||
'rotate_left' => [
|
||||
'label' => 'Görüntüyü sola döndür',
|
||||
],
|
||||
'rotate_right' => [
|
||||
'label' => 'Görüntüyü sağa döndür',
|
||||
],
|
||||
'set_aspect_ratio' => [
|
||||
'label' => 'En boy oranını :ratio olarak ayarla',
|
||||
],
|
||||
'save' => [
|
||||
'label' => 'Sakla',
|
||||
],
|
||||
'zoom_100' => [
|
||||
'label' => 'Görüntüyü %100 yakınlaştır',
|
||||
],
|
||||
'zoom_in' => [
|
||||
'label' => 'Yakınlaştır',
|
||||
],
|
||||
'zoom_out' => [
|
||||
'label' => 'Uzaklaştır',
|
||||
],
|
||||
],
|
||||
'fields' => [
|
||||
'height' => [
|
||||
'label' => 'Yükseklik',
|
||||
'unit' => 'px',
|
||||
],
|
||||
'rotation' => [
|
||||
'label' => 'Döndürme',
|
||||
'unit' => '°',
|
||||
],
|
||||
'width' => [
|
||||
'label' => 'Genişlik',
|
||||
'unit' => 'px',
|
||||
],
|
||||
'x_position' => [
|
||||
'label' => 'X',
|
||||
'unit' => 'px',
|
||||
],
|
||||
'y_position' => [
|
||||
'label' => 'Y',
|
||||
'unit' => 'px',
|
||||
],
|
||||
],
|
||||
'aspect_ratios' => [
|
||||
'label' => 'En boy oranı',
|
||||
'no_fixed' => [
|
||||
'label' => 'Serbest',
|
||||
],
|
||||
],
|
||||
'svg' => [
|
||||
'messages' => [
|
||||
'confirmation' => 'SVG dosyalarını düzenleme, ölçeklendirme yaptığınızda kalite kaybına neden olabileceği için tavsiye edilmez.\\n Devam etmek istediğinize emin misiniz?',
|
||||
'disabled' => 'SVG dosyalarını düzenleme, ölçeklendirme yaptığınızda kalite kaybına neden olduğu için engellendi.',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'key_value' => [
|
||||
'actions' => [
|
||||
'add' => [
|
||||
'label' => 'Satır ekle',
|
||||
],
|
||||
'delete' => [
|
||||
'label' => 'Satır sil',
|
||||
],
|
||||
'reorder' => [
|
||||
'label' => 'Satır sırala',
|
||||
],
|
||||
],
|
||||
'fields' => [
|
||||
'key' => [
|
||||
'label' => 'Anahtar',
|
||||
],
|
||||
'value' => [
|
||||
'label' => 'Değer',
|
||||
],
|
||||
],
|
||||
],
|
||||
'markdown_editor' => [
|
||||
'file_attachments_accepted_file_types_message' => 'Yüklenen dosyalar şu türlerden olmalıdır: :values.',
|
||||
'file_attachments_max_size_message' => 'Yüklenen dosyalar :max kilobayttan büyük olmamalıdır.',
|
||||
'tools' => [
|
||||
'attach_files' => 'Dosya ekle',
|
||||
'blockquote' => 'Alıntı',
|
||||
'bold' => 'Kalın',
|
||||
'bullet_list' => 'Sanaw',
|
||||
'code_block' => 'Kod bloğu',
|
||||
'heading' => 'Başlık',
|
||||
'italic' => 'Eğik',
|
||||
'link' => 'Bağlantı',
|
||||
'ordered_list' => 'Numaralı liste',
|
||||
'redo' => 'Yinele',
|
||||
'strike' => 'Üstü çizili',
|
||||
'table' => 'Tablo',
|
||||
'undo' => 'Geri al',
|
||||
],
|
||||
],
|
||||
'modal_table_select' => [
|
||||
'actions' => [
|
||||
'select' => [
|
||||
'label' => 'Saýla',
|
||||
'actions' => [
|
||||
'select' => [
|
||||
'label' => 'Saýla',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'radio' => [
|
||||
'boolean' => [
|
||||
'true' => 'Hawa',
|
||||
'false' => 'Ýok',
|
||||
],
|
||||
],
|
||||
'repeater' => [
|
||||
'actions' => [
|
||||
'add' => [
|
||||
'label' => ':label\'e ekle',
|
||||
],
|
||||
'add_between' => [
|
||||
'label' => 'Arasına yerleştir',
|
||||
],
|
||||
'delete' => [
|
||||
'label' => 'Poz',
|
||||
],
|
||||
'clone' => [
|
||||
'label' => 'Klonla',
|
||||
],
|
||||
'reorder' => [
|
||||
'label' => 'Taşı',
|
||||
],
|
||||
'move_down' => [
|
||||
'label' => 'Aşağı taşı',
|
||||
],
|
||||
'move_up' => [
|
||||
'label' => 'Yukarı taşı',
|
||||
],
|
||||
'collapse' => [
|
||||
'label' => 'Daralt',
|
||||
],
|
||||
'expand' => [
|
||||
'label' => 'Genişlet',
|
||||
],
|
||||
'collapse_all' => [
|
||||
'label' => 'Tümünü daralt',
|
||||
],
|
||||
'expand_all' => [
|
||||
'label' => 'Tümünü genişlet',
|
||||
],
|
||||
],
|
||||
],
|
||||
'rich_editor' => [
|
||||
'actions' => [
|
||||
'attach_files' => [
|
||||
'label' => 'Dosya yükle',
|
||||
'modal' => [
|
||||
'heading' => 'Dosya yükle',
|
||||
'form' => [
|
||||
'file' => [
|
||||
'label' => [
|
||||
'new' => 'Dosya',
|
||||
'existing' => 'Dosyayı değiştir',
|
||||
],
|
||||
],
|
||||
'alt' => [
|
||||
'label' => [
|
||||
'new' => 'Açıklama metni',
|
||||
'existing' => 'Açıklama metnini değiştir',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'custom_block' => [
|
||||
'modal' => [
|
||||
'actions' => [
|
||||
'insert' => [
|
||||
'label' => 'Goş',
|
||||
],
|
||||
'save' => [
|
||||
'label' => 'Sakla',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'grid' => [
|
||||
'label' => 'Grid',
|
||||
'modal' => [
|
||||
'heading' => 'Grid',
|
||||
'form' => [
|
||||
'preset' => [
|
||||
'label' => 'Ön ayar',
|
||||
'placeholder' => 'Yok',
|
||||
'options' => [
|
||||
'two' => 'İki',
|
||||
'three' => 'Üç',
|
||||
'four' => 'Dört',
|
||||
'five' => 'Beş',
|
||||
'two_start_third' => 'İki (Başta Üçte Bir)',
|
||||
'two_end_third' => 'İki (Sonda Üçte Bir)',
|
||||
'two_start_fourth' => 'İki (Başta Dörtte Bir)',
|
||||
'two_end_fourth' => 'İki (Sonda Dörtte Bir)',
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'label' => 'Sütünler',
|
||||
],
|
||||
'from_breakpoint' => [
|
||||
'label' => 'Kesme noktasından',
|
||||
'options' => [
|
||||
'default' => 'Hemmesi',
|
||||
'sm' => 'Küçük (sm)',
|
||||
'md' => 'Orta (md)',
|
||||
'lg' => 'Büyük (lg)',
|
||||
'xl' => 'Çok büyük (xl)',
|
||||
'2xl' => 'İki kat büyük (2xl)',
|
||||
],
|
||||
],
|
||||
'is_asymmetric' => [
|
||||
'label' => 'İki asimetrik sütun',
|
||||
],
|
||||
'start_span' => [
|
||||
'label' => 'Başlangıç aralığı',
|
||||
],
|
||||
'end_span' => [
|
||||
'label' => 'Bitiş aralığı',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'link' => [
|
||||
'label' => 'Üýtget',
|
||||
'modal' => [
|
||||
'heading' => 'Bağlantı',
|
||||
'form' => [
|
||||
'url' => [
|
||||
'label' => 'URL',
|
||||
],
|
||||
'should_open_in_new_tab' => [
|
||||
'label' => 'Yeni sekmede aç',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'text_color' => [
|
||||
'label' => 'Yazı rengi',
|
||||
'modal' => [
|
||||
'heading' => 'Yazı rengi',
|
||||
'form' => [
|
||||
'color' => [
|
||||
'label' => 'Renk',
|
||||
],
|
||||
'custom_color' => [
|
||||
'label' => 'Özel renk',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'file_attachments_accepted_file_types_message' => 'Yüklenen dosyalar şu türlerden olmalıdır: :values.',
|
||||
'file_attachments_max_size_message' => 'Yüklenen dosyalar :max kilobayttan büyük olmamalıdır.',
|
||||
'no_merge_tag_search_results_message' => 'Uygun birleşme etiketi bulunamadı.',
|
||||
'mentions' => [
|
||||
'no_options_message' => 'Seçenek bulunamadı.',
|
||||
'no_search_results_message' => 'Aramanızla eşleşen sonuç bulunamadı.',
|
||||
'search_prompt' => 'Aramak için yazmaya başlayın...',
|
||||
'searching_message' => 'Aranıyor...',
|
||||
],
|
||||
'tools' => [
|
||||
'align_center' => 'Ortaya hizala',
|
||||
'align_end' => 'Sona hizala',
|
||||
'align_justify' => 'İki yana yasla',
|
||||
'align_start' => 'Başa hizala',
|
||||
'attach_files' => 'Dosya ekle',
|
||||
'blockquote' => 'Alıntı',
|
||||
'bold' => 'Kalın',
|
||||
'bullet_list' => 'Sırasız liste',
|
||||
'clear_formatting' => 'Biçimlendirmeyi temizle',
|
||||
'code' => 'Kod',
|
||||
'code_block' => 'Kod bloğu',
|
||||
'custom_blocks' => 'Bloklar',
|
||||
'details' => 'Jikme-jik',
|
||||
'h1' => 'Başlık',
|
||||
'h2' => 'Başlık 2',
|
||||
'h3' => 'Alt başlık',
|
||||
'grid' => 'Grid',
|
||||
'grid_delete' => 'Grid\'i sil',
|
||||
'highlight' => 'Vurgula',
|
||||
'horizontal_rule' => 'Yatay çizgi',
|
||||
'italic' => 'Eğik',
|
||||
'lead' => 'Öne çıkan metin',
|
||||
'link' => 'Bağlantı',
|
||||
'merge_tags' => 'Birleşme etiketleri',
|
||||
'ordered_list' => 'Sıralı liste',
|
||||
'redo' => 'Yinele',
|
||||
'small' => 'Küçük metin',
|
||||
'strike' => 'Üstü çizili',
|
||||
'subscript' => 'Alt simge',
|
||||
'superscript' => 'Üst simge',
|
||||
'table' => 'Tablo',
|
||||
'table_delete' => 'Tabloyu sil',
|
||||
'table_add_column_before' => 'Öncesine sütun ekle',
|
||||
'table_add_column_after' => 'Sonrasına sütun ekle',
|
||||
'table_delete_column' => 'Sütunu sil',
|
||||
'table_add_row_before' => 'Üstüne satır ekle',
|
||||
'table_add_row_after' => 'Altına satır ekle',
|
||||
'table_delete_row' => 'Satırı sil',
|
||||
'table_merge_cells' => 'Hücreleri birleştir',
|
||||
'table_split_cell' => 'Hücreyi böl',
|
||||
'table_toggle_header_row' => 'Başlık satırını aç/kapat',
|
||||
'table_toggle_header_cell' => 'Başlık hücresini aç/kapat',
|
||||
'text_color' => 'Yazı rengi',
|
||||
'underline' => 'Altı çizili',
|
||||
'undo' => 'Geri al',
|
||||
],
|
||||
'uploading_file_message' => 'Dosya yükleniyor...',
|
||||
],
|
||||
'select' => [
|
||||
'actions' => [
|
||||
'create_option' => [
|
||||
'label' => 'Döret',
|
||||
'modal' => [
|
||||
'heading' => 'Döret',
|
||||
'actions' => [
|
||||
'create' => [
|
||||
'label' => 'Döret',
|
||||
],
|
||||
'create_another' => [
|
||||
'label' => 'Oluştur & Yeni oluştur',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'edit_option' => [
|
||||
'label' => 'Üýtget',
|
||||
'modal' => [
|
||||
'heading' => 'Üýtget',
|
||||
'actions' => [
|
||||
'save' => [
|
||||
'label' => 'Sakla',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'boolean' => [
|
||||
'true' => 'Hawa',
|
||||
'false' => 'Ýok',
|
||||
],
|
||||
'loading_message' => 'Ýüklenýär...',
|
||||
'max_items_message' => 'Sadece :count adet seçilebilir.',
|
||||
'no_options_message' => 'Seçenek bulunamadı.',
|
||||
'no_search_results_message' => 'Arama kriterlerinize uyan seçenek yok.',
|
||||
'placeholder' => 'Bir seçenek seçin',
|
||||
'searching_message' => 'Aranıyor...',
|
||||
'search_prompt' => 'Aramak için yazmaya başlayın...',
|
||||
],
|
||||
'tags_input' => [
|
||||
'actions' => [
|
||||
'delete' => [
|
||||
'label' => 'Poz',
|
||||
],
|
||||
],
|
||||
'placeholder' => 'Yeni etiket',
|
||||
],
|
||||
'text_input' => [
|
||||
'actions' => [
|
||||
'copy' => [
|
||||
'label' => 'Göçür',
|
||||
'message' => 'Göçürildi',
|
||||
],
|
||||
'hide_password' => [
|
||||
'label' => 'Şifreyi gizle',
|
||||
],
|
||||
'show_password' => [
|
||||
'label' => 'Şifreyi göster',
|
||||
],
|
||||
],
|
||||
],
|
||||
'toggle_buttons' => [
|
||||
'boolean' => [
|
||||
'true' => 'Hawa',
|
||||
'false' => 'Ýok',
|
||||
],
|
||||
],
|
||||
];
|
||||
8
lang/vendor/filament-forms/tk/validation.php
vendored
Normal file
8
lang/vendor/filament-forms/tk/validation.php
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'distinct' => [
|
||||
'must_be_selected' => 'En az 1 adet :attribute alanı seçmelisiniz.',
|
||||
'only_one_must_be_selected' => 'Sadece 1 adet :attribute alanı seçilmelidir.',
|
||||
],
|
||||
];
|
||||
24
lang/vendor/filament-infolists/tk/components.php
vendored
Normal file
24
lang/vendor/filament-infolists/tk/components.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'entries' => [
|
||||
'text' => [
|
||||
'actions' => [
|
||||
'collapse_list' => ':count kayıt az göster',
|
||||
'expand_list' => ':count kayıt daha göster',
|
||||
],
|
||||
'more_list_items' => 've :count kayıt daha',
|
||||
],
|
||||
'key_value' => [
|
||||
'columns' => [
|
||||
'key' => [
|
||||
'label' => 'Anahtar',
|
||||
],
|
||||
'value' => [
|
||||
'label' => 'Değer',
|
||||
],
|
||||
],
|
||||
'placeholder' => 'Kayıt yok',
|
||||
],
|
||||
],
|
||||
];
|
||||
19
lang/vendor/filament-notifications/tk/database.php
vendored
Normal file
19
lang/vendor/filament-notifications/tk/database.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'modal' => [
|
||||
'heading' => 'Habarnamalar',
|
||||
'actions' => [
|
||||
'clear' => [
|
||||
'label' => 'Arassala',
|
||||
],
|
||||
'mark_all_as_read' => [
|
||||
'label' => 'Tümünü okundu işaretle',
|
||||
],
|
||||
],
|
||||
'empty' => [
|
||||
'heading' => 'Bildirim yok',
|
||||
'description' => 'Lütfen sonra kontrol ediniz',
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'notifications' => [
|
||||
'blocked' => [
|
||||
'title' => 'E-posta adresi güncelleme isteği engellendi',
|
||||
'body' => 'Başarılı bir şekilde E-posta adresinizin :email olarak güncellenme isteğini engellediniz. Eğer bu isteği siz yapmadıysanız lütfen bizimle iletişime geçin.',
|
||||
],
|
||||
'failed' => [
|
||||
'title' => 'E-posta adresi güncelleme isteği engellenirken bir hata oluştu',
|
||||
'body' => 'Ne yazık ki, E-posta adresinizin :email olarak güncellenme isteğini engelleme işleminiz başarısız oldu, siz engelleyemeden E-posta adresi onaylandı. Eğer bu isteği siz yapmadıysanız lütfen bizimle iletişime geçin.',
|
||||
],
|
||||
],
|
||||
];
|
||||
10
lang/vendor/filament-panels/tk/auth/http/controllers/email-change-verification-controller.php
vendored
Normal file
10
lang/vendor/filament-panels/tk/auth/http/controllers/email-change-verification-controller.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'notifications' => [
|
||||
'verified' => [
|
||||
'title' => 'E-posta adresi güncellendi',
|
||||
'body' => 'E-posta adresiniz başarıyla :email olarak güncellendi.',
|
||||
],
|
||||
],
|
||||
];
|
||||
40
lang/vendor/filament-panels/tk/auth/multi-factor/app/actions/disable.php
vendored
Normal file
40
lang/vendor/filament-panels/tk/auth/multi-factor/app/actions/disable.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Ýap',
|
||||
'modal' => [
|
||||
'heading' => 'Doğrulama uygulamasını devre dışı bırak',
|
||||
'description' => 'Doğrulama uygulamasını devre dışı bırakmak istediğinize emin misiniz? Bunu devre dışı bırakmak hesabınızda bulunan ekstra koruma katmanını kaldıracaktır.',
|
||||
'form' => [
|
||||
'code' => [
|
||||
'label' => 'Doğrulama uygulamanızdaki 6 haneli kodu girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'actions' => [
|
||||
'use_recovery_code' => [
|
||||
'label' => 'Bunun yerine kurtarma kodu girin',
|
||||
],
|
||||
],
|
||||
'messages' => [
|
||||
'invalid' => 'Girmiş olduğunuz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
'recovery_code' => [
|
||||
'label' => 'Veya, bir kurtarma kodu girin',
|
||||
'validation_attribute' => 'kurtarma kodu',
|
||||
'messages' => [
|
||||
'invalid' => 'Girmiş olduğunuz kurtarma kodu geçersiz.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'submit' => [
|
||||
'label' => 'Uygulamayı devre dışı bırak',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'disabled' => [
|
||||
'title' => 'Doğrulama uygulaması devre dışı bırakıldı',
|
||||
],
|
||||
],
|
||||
];
|
||||
43
lang/vendor/filament-panels/tk/auth/multi-factor/app/actions/regenerate-recovery-codes.php
vendored
Normal file
43
lang/vendor/filament-panels/tk/auth/multi-factor/app/actions/regenerate-recovery-codes.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Kurtarma kodlarını yeniden oluştur',
|
||||
'modal' => [
|
||||
'heading' => 'Kurtarma kodlarını yeniden oluştur',
|
||||
'description' => 'Eğer kurtarma kodlarınızı kaybederseniz buradan yeniden oluşturabilirsiniz. Eski kodlarınız devre dışı kalacaktır.',
|
||||
'form' => [
|
||||
'code' => [
|
||||
'label' => 'Doğrulama uygulamanızdaki 6 haneli kodu girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'messages' => [
|
||||
'invalid' => 'Girmiş olduğunuz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
'password' => [
|
||||
'label' => 'Veya, geçerli şifrenizi girin',
|
||||
'validation_attribute' => 'şifre',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'submit' => [
|
||||
'label' => 'Kodları yeniden oluştur',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'regenerated' => [
|
||||
'title' => 'Yeni kurtarma kodları oluşturuldu',
|
||||
],
|
||||
],
|
||||
'show_new_recovery_codes' => [
|
||||
'modal' => [
|
||||
'heading' => 'Yeni kodlar',
|
||||
'description' => 'Lütfen bu kodları güvenli bir şekilde saklayın. Bu kodlar size sadece bir kere gösterilecek ve eğer doğrulama uygulamanıza erişiminizi kaybederseniz bu kodları kullanmanız gerekecek:',
|
||||
'actions' => [
|
||||
'submit' => [
|
||||
'label' => 'Ýap',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
44
lang/vendor/filament-panels/tk/auth/multi-factor/app/actions/set-up.php
vendored
Normal file
44
lang/vendor/filament-panels/tk/auth/multi-factor/app/actions/set-up.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Kur',
|
||||
'modal' => [
|
||||
'heading' => 'Doğrulama uygulaması kur',
|
||||
'description' => 'Devam etmek için Google Authenticator gibi (<x-filament::link href="https://itunes.apple.com/us/app/google-authenticator/id388497605" target="_blank">iOS</x-filament::link>, <x-filament::link href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank">Android</x-filament::link>) uygulamalardan birine ihtiyacınız olacak.',
|
||||
'content' => [
|
||||
'qr_code' => [
|
||||
'instruction' => 'Doğrulama uygulamanızla aşağıdaki QR kodunu taratın:',
|
||||
'alt' => 'QR kodunu taratın',
|
||||
],
|
||||
'text_code' => [
|
||||
'instruction' => 'Veya aşağıdaki kodu elle girin:',
|
||||
'messages' => [
|
||||
'copied' => 'Göçürildi',
|
||||
],
|
||||
],
|
||||
'recovery_codes' => [
|
||||
'instruction' => 'Lütfen bu kodları güvenli bir şekilde saklayın. Bu kodlar size sadece bir kere gösterilecek ve eğer doğrulama uygulamanıza erişiminizi kaybederseniz bu kodları kullanmanız gerekecek:',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'code' => [
|
||||
'label' => 'Doğrulama uygulamanızdaki 6 haneli kodu girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'below_content' => 'Giriş yaparken veya hassas bir işlem gerçekleştirirken doğrulama uygulamanız tarafından oluşturulan 6 haneli kodu girmeniz gerekecek.',
|
||||
'messages' => [
|
||||
'invalid' => 'Girmiş olduğunuz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'submit' => [
|
||||
'label' => 'Doğrulama uygulamasını etkinleştir',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'enabled' => [
|
||||
'title' => 'Doğrulama uygulaması etkinleştirildi',
|
||||
],
|
||||
],
|
||||
];
|
||||
36
lang/vendor/filament-panels/tk/auth/multi-factor/app/provider.php
vendored
Normal file
36
lang/vendor/filament-panels/tk/auth/multi-factor/app/provider.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'management_schema' => [
|
||||
'actions' => [
|
||||
'label' => 'Doğrulama uygulaması',
|
||||
'below_content' => 'Girişinizi doğrulamak için doğrulama uygulamanız tarafından oluşturulan kodları kullanın',
|
||||
'messages' => [
|
||||
'enabled' => 'Etkin',
|
||||
'disabled' => 'Devre Dışı',
|
||||
],
|
||||
],
|
||||
],
|
||||
'login_form' => [
|
||||
'label' => 'Use a code from your authenticator app',
|
||||
'code' => [
|
||||
'label' => 'Girişinizi doğrulamak için doğrulama uygulamanız tarafından oluşturulan bir kod girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'actions' => [
|
||||
'use_recovery_code' => [
|
||||
'label' => 'Kurtarma kodu kullan',
|
||||
],
|
||||
],
|
||||
'messages' => [
|
||||
'invalid' => 'Girmiş olduğunuz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
'recovery_code' => [
|
||||
'label' => 'Veya kurtarma kodu girin',
|
||||
'validation_attribute' => 'kurtarma kodu',
|
||||
'messages' => [
|
||||
'invalid' => 'Girmiş olduğunuz kurtarma kodu geçersiz.',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
38
lang/vendor/filament-panels/tk/auth/multi-factor/email/actions/disable.php
vendored
Normal file
38
lang/vendor/filament-panels/tk/auth/multi-factor/email/actions/disable.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Ýap',
|
||||
'modal' => [
|
||||
'heading' => 'E-posta doğrulama kodlarını devre dışı bırak',
|
||||
'description' => 'E-posta doğrulama kodları almayı durdurmak istediğinizden emin misiniz? Bu özelliği devre dışı bırakmak hesabınızdan ek bir güvenlik katmanını kaldıracaktır.',
|
||||
'form' => [
|
||||
'code' => [
|
||||
'label' => 'Size e-posta ile gönderdiğimiz 6 haneli kodu girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'actions' => [
|
||||
'resend' => [
|
||||
'label' => 'E-posta ile yeni kod gönder',
|
||||
'notifications' => [
|
||||
'resent' => [
|
||||
'title' => 'Size e-posta ile yeni bir kod gönderdik',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'messages' => [
|
||||
'invalid' => 'Girdiğiniz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'submit' => [
|
||||
'label' => 'E-posta doğrulama kodlarını devre dışı bırak',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'disabled' => [
|
||||
'title' => 'E-posta doğrulama kodları devre dışı bırakıldı',
|
||||
],
|
||||
],
|
||||
];
|
||||
38
lang/vendor/filament-panels/tk/auth/multi-factor/email/actions/set-up.php
vendored
Normal file
38
lang/vendor/filament-panels/tk/auth/multi-factor/email/actions/set-up.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Kur',
|
||||
'modal' => [
|
||||
'heading' => 'E-posta doğrulama kodlarını kur',
|
||||
'description' => 'Her giriş yaptığınızda veya hassas işlemler gerçekleştirdiğinizde size e-posta ile gönderdiğimiz 6 haneli kodu girmeniz gerekecek. Kurulumu tamamlamak için e-postanızı kontrol edin ve 6 haneli kodu girin.',
|
||||
'form' => [
|
||||
'code' => [
|
||||
'label' => 'Size e-posta ile gönderdiğimiz 6 haneli kodu girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'actions' => [
|
||||
'resend' => [
|
||||
'label' => 'E-posta ile yeni kod gönder',
|
||||
'notifications' => [
|
||||
'resent' => [
|
||||
'title' => 'Size e-posta ile yeni bir kod gönderdik',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'messages' => [
|
||||
'invalid' => 'Girdiğiniz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'submit' => [
|
||||
'label' => 'E-posta doğrulama kodlarını etkinleştir',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'enabled' => [
|
||||
'title' => 'E-posta doğrulama kodları etkinleştirildi',
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'subject' => 'Giriş kodunuz',
|
||||
'lines' => [
|
||||
0 => 'Giriş kodunuz: :code',
|
||||
1 => 'Bu kod 1 dakika içinde geçersiz olacak.|Bu kod :minutes dakika içinde geçersiz olacak.',
|
||||
],
|
||||
];
|
||||
34
lang/vendor/filament-panels/tk/auth/multi-factor/email/provider.php
vendored
Normal file
34
lang/vendor/filament-panels/tk/auth/multi-factor/email/provider.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'management_schema' => [
|
||||
'actions' => [
|
||||
'label' => 'E-posta doğrulama kodları',
|
||||
'below_content' => 'Giriş sırasında kimliğinizi doğrulamak için e-posta adresinize geçici bir kod alın.',
|
||||
'messages' => [
|
||||
'enabled' => 'Etkin',
|
||||
'disabled' => 'Devre dışı',
|
||||
],
|
||||
],
|
||||
],
|
||||
'login_form' => [
|
||||
'label' => 'E-postanıza kod gönder',
|
||||
'code' => [
|
||||
'label' => 'Size e-posta ile gönderdiğimiz 6 haneli kodu girin',
|
||||
'validation_attribute' => 'kod',
|
||||
'actions' => [
|
||||
'resend' => [
|
||||
'label' => 'E-posta ile yeni kod gönder',
|
||||
'notifications' => [
|
||||
'resent' => [
|
||||
'title' => 'Size e-posta ile yeni bir kod gönderdik',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'messages' => [
|
||||
'invalid' => 'Girdiğiniz kod geçersiz.',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'İki faktörlü kimlik doğrulamayı (2FA) kur',
|
||||
'heading' => 'İki faktörlü kimlik doğrulamayı kur',
|
||||
'subheading' => '2FA, giriş yaparken ikinci bir doğrulama formu gerektirerek hesabınıza ek bir güvenlik katmanı ekler.',
|
||||
'actions' => [
|
||||
'continue' => [
|
||||
'label' => 'Devam et',
|
||||
],
|
||||
],
|
||||
];
|
||||
18
lang/vendor/filament-panels/tk/auth/multi-factor/recovery-codes-modal-content.php
vendored
Normal file
18
lang/vendor/filament-panels/tk/auth/multi-factor/recovery-codes-modal-content.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'actions' => [
|
||||
0 => 'Tüm kodları',
|
||||
'copy' => [
|
||||
'label' => 'kopyalamak',
|
||||
],
|
||||
1 => 'or',
|
||||
'download' => [
|
||||
'label' => 'indirmek',
|
||||
],
|
||||
2 => 'için tıklayın.',
|
||||
],
|
||||
'messages' => [
|
||||
'copied' => 'Göçürildi',
|
||||
],
|
||||
];
|
||||
12
lang/vendor/filament-panels/tk/auth/notifications/notice-of-email-change-request.php
vendored
Normal file
12
lang/vendor/filament-panels/tk/auth/notifications/notice-of-email-change-request.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'subject' => 'E-posta adresiniz güncelleniyor',
|
||||
'lines' => [
|
||||
0 => 'E-posta adresinizin güncellenmesi isteğini aldık. Bu güncelleme işlemi şifreniz kullanılarak doğrulanmıştır.',
|
||||
1 => 'Doğrulanınca hesabınızın E-posta adresi belirtilen adres olarak güncellenecektir: :email.',
|
||||
2 => 'Aşağıdaki butonu kullanarak E-posta adresi doğrulanmadan bu güncelleme isteğini iptal edebilirsiniz.',
|
||||
3 => 'Eğer bu isteği siz yapmadıysanız lütfen bizimle iletişime geçin.',
|
||||
],
|
||||
'action' => 'Güncelleme İsteğini İptal Et',
|
||||
];
|
||||
48
lang/vendor/filament-panels/tk/auth/pages/edit-profile.php
vendored
Normal file
48
lang/vendor/filament-panels/tk/auth/pages/edit-profile.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Profil',
|
||||
'form' => [
|
||||
'email' => [
|
||||
'label' => 'E-poçta salgysy',
|
||||
],
|
||||
'name' => [
|
||||
'label' => 'Ad',
|
||||
],
|
||||
'password' => [
|
||||
'label' => 'Yeni şifre',
|
||||
'validation_attribute' => 'şifre',
|
||||
],
|
||||
'password_confirmation' => [
|
||||
'label' => 'Yeni şifreyi onayla',
|
||||
'validation_attribute' => 'şifre onayı',
|
||||
],
|
||||
'current_password' => [
|
||||
'label' => 'Güncel şifre',
|
||||
'below_content' => 'Güvenliğiniz için lütfen güncel şifrenizi girin.',
|
||||
'validation_attribute' => 'güncel şifre',
|
||||
],
|
||||
'actions' => [
|
||||
'save' => [
|
||||
'label' => 'Değişiklikleri Kaydet',
|
||||
],
|
||||
],
|
||||
],
|
||||
'multi_factor_authentication' => [
|
||||
'label' => 'İki Faktörlü Doğrulama (2FA)',
|
||||
],
|
||||
'notifications' => [
|
||||
'email_change_verification_sent' => [
|
||||
'title' => 'E-posta adresi güncelleme isteği gönderildi',
|
||||
'body' => 'E-posta adresi güncelleme isteği :email adresine gönderildi. Lütfen güncellemeyi tamamlamak için E-posta adresinizi doğrulayın.',
|
||||
],
|
||||
'saved' => [
|
||||
'title' => 'Kaydedildi',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'cancel' => [
|
||||
'label' => 'Ýatyr',
|
||||
],
|
||||
],
|
||||
];
|
||||
24
lang/vendor/filament-panels/tk/auth/pages/email-verification/email-verification-prompt.php
vendored
Normal file
24
lang/vendor/filament-panels/tk/auth/pages/email-verification/email-verification-prompt.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'E-posta adresinizi doğrulayın',
|
||||
'heading' => 'E-posta adresinizi doğrulayın',
|
||||
'actions' => [
|
||||
'resend_notification' => [
|
||||
'label' => 'Yeniden Gönder',
|
||||
],
|
||||
],
|
||||
'messages' => [
|
||||
'notification_not_received' => 'Gönderdiğimiz e-postayı almadınız mı?',
|
||||
'notification_sent' => ':email adresine, e-posta adresinizi nasıl doğrulayacağınıza ilişkin talimatları içeren bir e-posta gönderdik.',
|
||||
],
|
||||
'notifications' => [
|
||||
'notification_resent' => [
|
||||
'title' => 'E-posta yeniden gönderildi.',
|
||||
],
|
||||
'notification_resend_throttled' => [
|
||||
'title' => 'Çok fazla yeniden gönderme denemesi',
|
||||
'body' => ':seconds sekuntdan soň gaýtadan synanyşyň.',
|
||||
],
|
||||
],
|
||||
];
|
||||
85
lang/vendor/filament-panels/tk/auth/pages/login.php
vendored
Normal file
85
lang/vendor/filament-panels/tk/auth/pages/login.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'title' => 'Giriş',
|
||||
|
||||
'heading' => 'Girmek',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'register' => [
|
||||
'before' => 'ýa-da',
|
||||
'label' => 'hasap açmak',
|
||||
],
|
||||
|
||||
'request_password_reset' => [
|
||||
'label' => 'Paroly ýatdan çykardyňyzmy?',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'form' => [
|
||||
|
||||
'email' => [
|
||||
'label' => 'E-poçta salgysy',
|
||||
],
|
||||
|
||||
'password' => [
|
||||
'label' => 'Parol',
|
||||
],
|
||||
|
||||
'remember' => [
|
||||
'label' => 'Meni ýatda sakla',
|
||||
],
|
||||
|
||||
'actions' => [
|
||||
|
||||
'authenticate' => [
|
||||
'label' => 'Girmek',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'multi_factor' => [
|
||||
|
||||
'heading' => 'Şahsyýetiňizi tassyklaň',
|
||||
|
||||
'subheading' => 'Girmegi dowam etdirmek üçin şahsyýetiňizi tassyklaň.',
|
||||
|
||||
'form' => [
|
||||
|
||||
'provider' => [
|
||||
'label' => 'Nähili tassyklamak isleýärsiňiz?',
|
||||
],
|
||||
|
||||
'actions' => [
|
||||
|
||||
'authenticate' => [
|
||||
'label' => 'Girişi tassykla',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'messages' => [
|
||||
|
||||
'failed' => 'Bu maglumatlar biziň ýazgylarymyza gabat gelenok.',
|
||||
|
||||
],
|
||||
|
||||
'notifications' => [
|
||||
|
||||
'throttled' => [
|
||||
'title' => 'Giriş synanyşyklary köp',
|
||||
'body' => ':seconds sekuntdan soň gaýtadan synanyşyň.',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
30
lang/vendor/filament-panels/tk/auth/pages/password-reset/request-password-reset.php
vendored
Normal file
30
lang/vendor/filament-panels/tk/auth/pages/password-reset/request-password-reset.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Şifrenizi sıfırlayın',
|
||||
'heading' => 'Şifrenizi mi unuttunuz?',
|
||||
'actions' => [
|
||||
'login' => [
|
||||
'label' => 'girişe geri dön',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'email' => [
|
||||
'label' => 'E-poçta salgysy',
|
||||
],
|
||||
'actions' => [
|
||||
'request' => [
|
||||
'label' => 'E-posta gönder',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'sent' => [
|
||||
'body' => 'Eğer hesabınız yoksa bir e-posta almayacaksınız.',
|
||||
],
|
||||
'throttled' => [
|
||||
'title' => 'Çok fazla istek',
|
||||
'body' => ':seconds sekuntdan soň gaýtadan synanyşyň.',
|
||||
],
|
||||
],
|
||||
];
|
||||
29
lang/vendor/filament-panels/tk/auth/pages/password-reset/reset-password.php
vendored
Normal file
29
lang/vendor/filament-panels/tk/auth/pages/password-reset/reset-password.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Şifrenizi sıfırlayın',
|
||||
'heading' => 'Şifrenizi sıfırlayın',
|
||||
'form' => [
|
||||
'email' => [
|
||||
'label' => 'E-poçta salgysy',
|
||||
],
|
||||
'password' => [
|
||||
'label' => 'Parol',
|
||||
'validation_attribute' => 'password',
|
||||
],
|
||||
'password_confirmation' => [
|
||||
'label' => 'Şifreyi onayla',
|
||||
],
|
||||
'actions' => [
|
||||
'reset' => [
|
||||
'label' => 'Şifreyi sıfırla',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'throttled' => [
|
||||
'title' => 'Çok fazla sıfırlama denemesi',
|
||||
'body' => ':seconds sekuntdan soň gaýtadan synanyşyň.',
|
||||
],
|
||||
],
|
||||
];
|
||||
38
lang/vendor/filament-panels/tk/auth/pages/register.php
vendored
Normal file
38
lang/vendor/filament-panels/tk/auth/pages/register.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Kayıt Ol',
|
||||
'heading' => 'Üye Ol',
|
||||
'actions' => [
|
||||
'login' => [
|
||||
'before' => 'ýa-da',
|
||||
'label' => 'hesabınıza giriş yapın',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'email' => [
|
||||
'label' => 'E-poçta salgysy',
|
||||
],
|
||||
'name' => [
|
||||
'label' => 'Ad',
|
||||
],
|
||||
'password' => [
|
||||
'label' => 'Parol',
|
||||
'validation_attribute' => 'password',
|
||||
],
|
||||
'password_confirmation' => [
|
||||
'label' => 'Şifreyi onayla',
|
||||
],
|
||||
'actions' => [
|
||||
'register' => [
|
||||
'label' => 'Üye ol',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'throttled' => [
|
||||
'title' => 'Çok fazla kayıt denemesi',
|
||||
'body' => ':seconds sekuntdan soň gaýtadan synanyşyň.',
|
||||
],
|
||||
],
|
||||
];
|
||||
6
lang/vendor/filament-panels/tk/error-notifications.php
vendored
Normal file
6
lang/vendor/filament-panels/tk/error-notifications.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Sayfa yüklenirken hata oluştu',
|
||||
'body' => 'Sayfa yüklenirken bir hata oluştu. Lütfen daha sonra tekrar deneyin.',
|
||||
];
|
||||
9
lang/vendor/filament-panels/tk/global-search.php
vendored
Normal file
9
lang/vendor/filament-panels/tk/global-search.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'field' => [
|
||||
'label' => 'Genel arama',
|
||||
'placeholder' => 'Gözleg',
|
||||
],
|
||||
'no_results_message' => 'Sonuç bulunamadı.',
|
||||
];
|
||||
72
lang/vendor/filament-panels/tk/layout.php
vendored
Normal file
72
lang/vendor/filament-panels/tk/layout.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'direction' => 'ltr',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'billing' => [
|
||||
'label' => 'Abunalygy dolandyr',
|
||||
],
|
||||
|
||||
'logout' => [
|
||||
'label' => 'Çykmak',
|
||||
],
|
||||
|
||||
'open_database_notifications' => [
|
||||
'label' => 'Habarnamalar',
|
||||
],
|
||||
|
||||
'open_user_menu' => [
|
||||
'label' => 'Ulanyjy menýusy',
|
||||
],
|
||||
|
||||
'sidebar' => [
|
||||
|
||||
'collapse' => [
|
||||
'label' => 'Gapdal paneli ýygnamak',
|
||||
],
|
||||
|
||||
'expand' => [
|
||||
'label' => 'Gapdal paneli giňeltmek',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'theme_switcher' => [
|
||||
|
||||
'dark' => [
|
||||
'label' => 'Gara tema açmak',
|
||||
],
|
||||
|
||||
'light' => [
|
||||
'label' => 'Açyk tema açmak',
|
||||
],
|
||||
|
||||
'system' => [
|
||||
'label' => 'Ulgam temasyny açmak',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'avatar' => [
|
||||
'alt' => ':name awatary',
|
||||
],
|
||||
|
||||
'logo' => [
|
||||
'alt' => ':name logotipi',
|
||||
],
|
||||
|
||||
'tenant_menu' => [
|
||||
|
||||
'search_field' => [
|
||||
'label' => 'Kirdeçi gözlegi',
|
||||
'placeholder' => 'Gözleg',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
33
lang/vendor/filament-panels/tk/pages/dashboard.php
vendored
Normal file
33
lang/vendor/filament-panels/tk/pages/dashboard.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'title' => 'Dolandyryş paneli',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'filter' => [
|
||||
|
||||
'label' => 'Süzgüç',
|
||||
|
||||
'modal' => [
|
||||
|
||||
'heading' => 'Süzgüç',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'apply' => [
|
||||
|
||||
'label' => 'Ulan',
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
16
lang/vendor/filament-panels/tk/pages/tenancy/edit-tenant-profile.php
vendored
Normal file
16
lang/vendor/filament-panels/tk/pages/tenancy/edit-tenant-profile.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'form' => [
|
||||
'actions' => [
|
||||
'save' => [
|
||||
'label' => 'Değişiklikleri kaydet',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'saved' => [
|
||||
'title' => 'Kaydedildi',
|
||||
],
|
||||
],
|
||||
];
|
||||
24
lang/vendor/filament-panels/tk/resources/pages/create-record.php
vendored
Normal file
24
lang/vendor/filament-panels/tk/resources/pages/create-record.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => ':label oluştur',
|
||||
'breadcrumb' => 'Döret',
|
||||
'form' => [
|
||||
'actions' => [
|
||||
'cancel' => [
|
||||
'label' => 'Ýatyr',
|
||||
],
|
||||
'create' => [
|
||||
'label' => 'Döret',
|
||||
],
|
||||
'create_another' => [
|
||||
'label' => 'Oluştur & yeni oluştur',
|
||||
],
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'created' => [
|
||||
'title' => 'Oluşturuldu',
|
||||
],
|
||||
],
|
||||
];
|
||||
27
lang/vendor/filament-panels/tk/resources/pages/edit-record.php
vendored
Normal file
27
lang/vendor/filament-panels/tk/resources/pages/edit-record.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => ':label üýtget',
|
||||
'breadcrumb' => 'Üýtget',
|
||||
'navigation_label' => 'Üýtget',
|
||||
'form' => [
|
||||
'actions' => [
|
||||
'cancel' => [
|
||||
'label' => 'Ýatyr',
|
||||
],
|
||||
'save' => [
|
||||
'label' => 'Değişiklikleri kaydet',
|
||||
],
|
||||
],
|
||||
],
|
||||
'content' => [
|
||||
'tab' => [
|
||||
'label' => 'Üýtget',
|
||||
],
|
||||
],
|
||||
'notifications' => [
|
||||
'saved' => [
|
||||
'title' => 'Kaydedildi',
|
||||
],
|
||||
],
|
||||
];
|
||||
5
lang/vendor/filament-panels/tk/resources/pages/list-records.php
vendored
Normal file
5
lang/vendor/filament-panels/tk/resources/pages/list-records.php
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'breadcrumb' => 'Sanaw',
|
||||
];
|
||||
5
lang/vendor/filament-panels/tk/resources/pages/manage-related-records.php
vendored
Normal file
5
lang/vendor/filament-panels/tk/resources/pages/manage-related-records.php
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => ':label :relationship yönet',
|
||||
];
|
||||
12
lang/vendor/filament-panels/tk/resources/pages/view-record.php
vendored
Normal file
12
lang/vendor/filament-panels/tk/resources/pages/view-record.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => ':label gör',
|
||||
'breadcrumb' => 'Gör',
|
||||
'navigation_label' => 'Gör',
|
||||
'content' => [
|
||||
'tab' => [
|
||||
'label' => 'Gör',
|
||||
],
|
||||
],
|
||||
];
|
||||
5
lang/vendor/filament-panels/tk/unsaved-changes-alert.php
vendored
Normal file
5
lang/vendor/filament-panels/tk/unsaved-changes-alert.php
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'body' => 'Kayıt edilmemiş değişiklikleriniz mevcut. Bu sayfayı terk etmek istediğinize emin misiniz?',
|
||||
];
|
||||
10
lang/vendor/filament-panels/tk/widgets/account-widget.php
vendored
Normal file
10
lang/vendor/filament-panels/tk/widgets/account-widget.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'actions' => [
|
||||
'logout' => [
|
||||
'label' => 'Çykmak',
|
||||
],
|
||||
],
|
||||
'welcome' => 'Hoş geldin',
|
||||
];
|
||||
12
lang/vendor/filament-panels/tk/widgets/filament-info-widget.php
vendored
Normal file
12
lang/vendor/filament-panels/tk/widgets/filament-info-widget.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'actions' => [
|
||||
'open_documentation' => [
|
||||
'label' => 'Dokümantasyon',
|
||||
],
|
||||
'open_github' => [
|
||||
'label' => 'GitHub',
|
||||
],
|
||||
],
|
||||
];
|
||||
333
lang/vendor/filament-query-builder/tk/query-builder.php
vendored
Normal file
333
lang/vendor/filament-query-builder/tk/query-builder.php
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'label' => 'Sorgu oluşturucu',
|
||||
'form' => [
|
||||
'operator' => [
|
||||
'label' => 'Operatör',
|
||||
],
|
||||
'or_groups' => [
|
||||
'label' => 'Gruplar',
|
||||
'block' => [
|
||||
'label' => 'Veya (OR)',
|
||||
'or' => 'VEYA',
|
||||
],
|
||||
],
|
||||
'rules' => [
|
||||
'label' => 'Kurallar',
|
||||
'item' => [
|
||||
'and' => 'VE',
|
||||
],
|
||||
],
|
||||
],
|
||||
'no_rules' => '(Kural yok)',
|
||||
'item_separators' => [
|
||||
'and' => 'VE',
|
||||
'or' => 'VEYA',
|
||||
],
|
||||
'operators' => [
|
||||
'is_filled' => [
|
||||
'label' => [
|
||||
'direct' => 'Dolu',
|
||||
'inverse' => 'Boş',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute dolu',
|
||||
'inverse' => ':attribute boş',
|
||||
],
|
||||
],
|
||||
'boolean' => [
|
||||
'is_true' => [
|
||||
'label' => [
|
||||
'direct' => 'Doğru',
|
||||
'inverse' => 'Yanlış',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute doğru',
|
||||
'inverse' => ':attribute yanlış',
|
||||
],
|
||||
],
|
||||
],
|
||||
'date' => [
|
||||
'is_after' => [
|
||||
'label' => [
|
||||
'direct' => 'Sonra',
|
||||
'inverse' => 'Sonra değil',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :date tarihinden sonra',
|
||||
'inverse' => ':attribute :date tarihinden sonra değil',
|
||||
],
|
||||
],
|
||||
'is_before' => [
|
||||
'label' => [
|
||||
'direct' => 'Önce',
|
||||
'inverse' => 'Önce değil',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :date tarihinden önce',
|
||||
'inverse' => ':attribute :date tarihinden önce değil',
|
||||
],
|
||||
],
|
||||
'is_date' => [
|
||||
'label' => [
|
||||
'direct' => 'Tarihtir',
|
||||
'inverse' => 'Tarih değildir',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :date',
|
||||
'inverse' => ':attribute :date değil',
|
||||
],
|
||||
],
|
||||
'is_month' => [
|
||||
'label' => [
|
||||
'direct' => 'Aydır',
|
||||
'inverse' => 'Ay değildir',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :month',
|
||||
'inverse' => ':attribute :month değil',
|
||||
],
|
||||
],
|
||||
'is_year' => [
|
||||
'label' => [
|
||||
'direct' => 'Yıldır',
|
||||
'inverse' => 'Yıl değildir',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :year',
|
||||
'inverse' => ':attribute :year değil',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'date' => [
|
||||
'label' => 'Tarih',
|
||||
],
|
||||
'month' => [
|
||||
'label' => 'Ay',
|
||||
],
|
||||
'year' => [
|
||||
'label' => 'Yıl',
|
||||
],
|
||||
],
|
||||
],
|
||||
'number' => [
|
||||
'equals' => [
|
||||
'label' => [
|
||||
'direct' => 'Eşittir',
|
||||
'inverse' => 'Eşit değildir',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :number\'a eşittir',
|
||||
'inverse' => ':attribute :number\'a eşit değildir',
|
||||
],
|
||||
],
|
||||
'is_max' => [
|
||||
'label' => [
|
||||
'direct' => 'Maksimum',
|
||||
'inverse' => 'Büyüktür',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute maksimum :number',
|
||||
'inverse' => ':attribute :number\'dan büyük',
|
||||
],
|
||||
],
|
||||
'is_min' => [
|
||||
'label' => [
|
||||
'direct' => 'Minimum',
|
||||
'inverse' => 'Küçüktür',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute minimum :number',
|
||||
'inverse' => ':attribute :number\'dan küçük',
|
||||
],
|
||||
],
|
||||
'aggregates' => [
|
||||
'average' => [
|
||||
'label' => 'Ortalama',
|
||||
'summary' => 'Ortalama :attribute',
|
||||
],
|
||||
'max' => [
|
||||
'label' => 'Maksimum',
|
||||
'summary' => 'Maksimum :attribute',
|
||||
],
|
||||
'min' => [
|
||||
'label' => 'Min',
|
||||
'summary' => 'Min :attribute',
|
||||
],
|
||||
'sum' => [
|
||||
'label' => 'Toplam',
|
||||
'summary' => ':attribute toplamı',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'aggregate' => [
|
||||
'label' => 'Toplam',
|
||||
],
|
||||
'number' => [
|
||||
'label' => 'Sayı',
|
||||
],
|
||||
],
|
||||
],
|
||||
'relationship' => [
|
||||
'equals' => [
|
||||
'label' => [
|
||||
'direct' => 'Sahip',
|
||||
'inverse' => 'Sahip değil',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':count :relationship mevcut',
|
||||
'inverse' => ':count :relationship mevcut değil',
|
||||
],
|
||||
],
|
||||
'has_max' => [
|
||||
'label' => [
|
||||
'direct' => 'En fazla',
|
||||
'inverse' => 'Has köp',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => 'En fazla :count :relationship',
|
||||
'inverse' => ':count adetten fazla :relationship',
|
||||
],
|
||||
],
|
||||
'has_min' => [
|
||||
'label' => [
|
||||
'direct' => 'En az',
|
||||
'inverse' => 'Has az',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => 'En az :count :relationship',
|
||||
'inverse' => ':count adetten az :relationship',
|
||||
],
|
||||
],
|
||||
'is_empty' => [
|
||||
'label' => [
|
||||
'direct' => 'Boş',
|
||||
'inverse' => 'Boş değil',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':relationship boş',
|
||||
'inverse' => ':relationship boş değil',
|
||||
],
|
||||
],
|
||||
'is_related_to' => [
|
||||
'label' => [
|
||||
'single' => [
|
||||
'direct' => 'Eşittir',
|
||||
'inverse' => 'Eşit değildir',
|
||||
],
|
||||
'multiple' => [
|
||||
'direct' => 'İçerir',
|
||||
'inverse' => 'İçermez',
|
||||
],
|
||||
],
|
||||
'summary' => [
|
||||
'single' => [
|
||||
'direct' => ':relationship :values',
|
||||
'inverse' => ':relationship :values değil',
|
||||
],
|
||||
'multiple' => [
|
||||
'direct' => ':relationship :values içerir',
|
||||
'inverse' => ':relationship :values içermez',
|
||||
],
|
||||
'values_glue' => [
|
||||
0 => ', ',
|
||||
'final' => ' veya ',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'value' => [
|
||||
'label' => 'Değer',
|
||||
],
|
||||
'values' => [
|
||||
'label' => 'Değerler',
|
||||
],
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'count' => [
|
||||
'label' => 'Sayı',
|
||||
],
|
||||
],
|
||||
],
|
||||
'select' => [
|
||||
'is' => [
|
||||
'label' => [
|
||||
'direct' => 'Eşittir',
|
||||
'inverse' => 'Eşit değildir',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :values',
|
||||
'inverse' => ':attribute :values değil',
|
||||
'values_glue' => [
|
||||
0 => ', ',
|
||||
'final' => ' veya ',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'value' => [
|
||||
'label' => 'Değer',
|
||||
],
|
||||
'values' => [
|
||||
'label' => 'Değerler',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'text' => [
|
||||
'contains' => [
|
||||
'label' => [
|
||||
'direct' => 'İçerir',
|
||||
'inverse' => 'İçermez',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :text içerir',
|
||||
'inverse' => ':attribute :text içermez',
|
||||
],
|
||||
],
|
||||
'ends_with' => [
|
||||
'label' => [
|
||||
'direct' => 'Şununla biter',
|
||||
'inverse' => 'Şununla bitmez',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :text ile biter',
|
||||
'inverse' => ':attribute :text ile bitmez',
|
||||
],
|
||||
],
|
||||
'equals' => [
|
||||
'label' => [
|
||||
'direct' => 'Eşittir',
|
||||
'inverse' => 'Eşit değildir',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :text\'e eşittir',
|
||||
'inverse' => ':attribute :text\'e eşit değildir',
|
||||
],
|
||||
],
|
||||
'starts_with' => [
|
||||
'label' => [
|
||||
'direct' => 'Şununla başlar',
|
||||
'inverse' => 'Şununla başlamaz',
|
||||
],
|
||||
'summary' => [
|
||||
'direct' => ':attribute :text ile başlar',
|
||||
'inverse' => ':attribute :text ile başlamaz',
|
||||
],
|
||||
],
|
||||
'form' => [
|
||||
'text' => [
|
||||
'label' => 'Metin',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
'add_rule' => [
|
||||
'label' => 'Kural ekle',
|
||||
],
|
||||
'add_rule_group' => [
|
||||
'label' => 'Kural grubu ekle',
|
||||
],
|
||||
],
|
||||
];
|
||||
14
lang/vendor/filament-schemas/tk/components.php
vendored
Normal file
14
lang/vendor/filament-schemas/tk/components.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'wizard' => [
|
||||
'actions' => [
|
||||
'previous_step' => [
|
||||
'label' => 'Yza',
|
||||
],
|
||||
'next_step' => [
|
||||
'label' => 'İleri',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
9
lang/vendor/filament-support/tk/components/button.php
vendored
Normal file
9
lang/vendor/filament-support/tk/components/button.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'messages' => [
|
||||
'uploading_file' => 'Faýl ýüklenýär...',
|
||||
],
|
||||
|
||||
];
|
||||
7
lang/vendor/filament-support/tk/components/copyable.php
vendored
Normal file
7
lang/vendor/filament-support/tk/components/copyable.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'messages' => [
|
||||
'copied' => 'Göçürildi',
|
||||
],
|
||||
];
|
||||
9
lang/vendor/filament-support/tk/components/modal.php
vendored
Normal file
9
lang/vendor/filament-support/tk/components/modal.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'actions' => [
|
||||
'close' => [
|
||||
'label' => 'Ýap',
|
||||
],
|
||||
],
|
||||
];
|
||||
47
lang/vendor/filament-support/tk/components/pagination.php
vendored
Normal file
47
lang/vendor/filament-support/tk/components/pagination.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'label' => 'Sahypalar navigasiýasy',
|
||||
|
||||
'overview' => '{1} 1 netije görkezilýär|[2,*] :total netijeden :first–:last görkezilýär',
|
||||
|
||||
'fields' => [
|
||||
|
||||
'records_per_page' => [
|
||||
|
||||
'label' => 'Sahypada',
|
||||
|
||||
'options' => [
|
||||
'all' => 'Hemmesi',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'actions' => [
|
||||
|
||||
'first' => [
|
||||
'label' => 'Ilkinji',
|
||||
],
|
||||
|
||||
'go_to_page' => [
|
||||
'label' => ':page sahypa geç',
|
||||
],
|
||||
|
||||
'last' => [
|
||||
'label' => 'Soňky',
|
||||
],
|
||||
|
||||
'next' => [
|
||||
'label' => 'Indiki',
|
||||
],
|
||||
|
||||
'previous' => [
|
||||
'label' => 'Öňki',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
267
lang/vendor/filament-tables/tk/table.php
vendored
Normal file
267
lang/vendor/filament-tables/tk/table.php
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'column_manager' => [
|
||||
|
||||
'heading' => 'Sütünler',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'apply' => [
|
||||
'label' => 'Sütünleri ulan',
|
||||
],
|
||||
|
||||
'reset' => [
|
||||
'label' => 'Täzeden',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'columns' => [
|
||||
|
||||
'actions' => [
|
||||
'label' => 'Hereket|Hereketler',
|
||||
],
|
||||
|
||||
'select' => [
|
||||
|
||||
'loading_message' => 'Ýüklenýär...',
|
||||
|
||||
'no_options_message' => 'Saýlaw ýok.',
|
||||
|
||||
'no_search_results_message' => 'Gözlege laýyk saýlaw tapylmady.',
|
||||
|
||||
'placeholder' => 'Saýlaw saýlaň',
|
||||
|
||||
'searching_message' => 'Gözlenýär...',
|
||||
|
||||
'search_prompt' => 'Gözlemek üçin ýazyň...',
|
||||
|
||||
],
|
||||
|
||||
'text' => [
|
||||
|
||||
'actions' => [
|
||||
'collapse_list' => ':count az görkez',
|
||||
'expand_list' => ':count köp görkez',
|
||||
],
|
||||
|
||||
'more_list_items' => 'we ýene :count',
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'fields' => [
|
||||
|
||||
'bulk_select_page' => [
|
||||
'label' => 'Köp hereketler üçin ähli elementleri saýla/saýlawy aýyr.',
|
||||
],
|
||||
|
||||
'bulk_select_record' => [
|
||||
'label' => 'Köp hereketler üçin :key elementini saýla/saýlawy aýyr.',
|
||||
],
|
||||
|
||||
'bulk_select_group' => [
|
||||
'label' => 'Köp hereketler üçin :title toparyny saýla/saýlawy aýyr.',
|
||||
],
|
||||
|
||||
'search' => [
|
||||
'label' => 'Gözleg',
|
||||
'placeholder' => 'Gözleg',
|
||||
'indicator' => 'Gözleg',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'summary' => [
|
||||
|
||||
'heading' => 'Jemi',
|
||||
|
||||
'subheadings' => [
|
||||
'all' => 'Ähli :label',
|
||||
'group' => ':group jemi',
|
||||
'page' => 'Bu sahypa',
|
||||
],
|
||||
|
||||
'summarizers' => [
|
||||
|
||||
'average' => [
|
||||
'label' => 'Ortaça',
|
||||
],
|
||||
|
||||
'count' => [
|
||||
'label' => 'Sany',
|
||||
],
|
||||
|
||||
'sum' => [
|
||||
'label' => 'Jemi',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'actions' => [
|
||||
|
||||
'disable_reordering' => [
|
||||
'label' => 'Tertiplemegi tamamla',
|
||||
],
|
||||
|
||||
'enable_reordering' => [
|
||||
'label' => 'Ýazgylary tertiple',
|
||||
],
|
||||
|
||||
'filter' => [
|
||||
'label' => 'Süzgüç',
|
||||
],
|
||||
|
||||
'group' => [
|
||||
'label' => 'Toparla',
|
||||
],
|
||||
|
||||
'open_bulk_actions' => [
|
||||
'label' => 'Köp hereketler',
|
||||
],
|
||||
|
||||
'column_manager' => [
|
||||
'label' => 'Sütün dolandyryşy',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'empty' => [
|
||||
|
||||
'heading' => ':model ýok',
|
||||
|
||||
'description' => 'Başlamak üçin :model dörediň.',
|
||||
|
||||
],
|
||||
|
||||
'filters' => [
|
||||
|
||||
'actions' => [
|
||||
|
||||
'apply' => [
|
||||
'label' => 'Süzgüçleri ulan',
|
||||
],
|
||||
|
||||
'remove' => [
|
||||
'label' => 'Süzgüçi aýyr',
|
||||
],
|
||||
|
||||
'remove_all' => [
|
||||
'label' => 'Ähli süzgüçleri aýyr',
|
||||
'tooltip' => 'Ähli süzgüçleri aýyr',
|
||||
],
|
||||
|
||||
'reset' => [
|
||||
'label' => 'Täzeden',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'heading' => 'Süzgüçler',
|
||||
|
||||
'indicator' => 'Işjeň süzgüçler',
|
||||
|
||||
'multi_select' => [
|
||||
'placeholder' => 'Hemmesi',
|
||||
],
|
||||
|
||||
'select' => [
|
||||
|
||||
'placeholder' => 'Hemmesi',
|
||||
|
||||
'relationship' => [
|
||||
'empty_option_label' => 'Ýok',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'trashed' => [
|
||||
|
||||
'label' => 'Pozulan ýazgylar',
|
||||
|
||||
'only_trashed' => 'Diňe pozulan ýazgylar',
|
||||
|
||||
'with_trashed' => 'Pozulanlar bilen',
|
||||
|
||||
'without_trashed' => 'Pozulmadyklar',
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'grouping' => [
|
||||
|
||||
'fields' => [
|
||||
|
||||
'group' => [
|
||||
'label' => 'Toparla',
|
||||
],
|
||||
|
||||
'direction' => [
|
||||
|
||||
'label' => 'Topar ugry',
|
||||
|
||||
'options' => [
|
||||
'asc' => 'Artýan',
|
||||
'desc' => 'Pesýän',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'reorder_indicator' => 'Ýazgylary tertiplemek üçin süýşüriň.',
|
||||
|
||||
'selection_indicator' => [
|
||||
|
||||
'selected_count' => '1 ýazgy saýlandy|:count ýazgy saýlandy',
|
||||
|
||||
'actions' => [
|
||||
|
||||
'select_all' => [
|
||||
'label' => 'Ähli :count saýla',
|
||||
],
|
||||
|
||||
'deselect_all' => [
|
||||
'label' => 'Saýlawy aýyr',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'sorting' => [
|
||||
|
||||
'fields' => [
|
||||
|
||||
'column' => [
|
||||
'label' => 'Tertiple',
|
||||
],
|
||||
|
||||
'direction' => [
|
||||
|
||||
'label' => 'Tertip ugry',
|
||||
|
||||
'options' => [
|
||||
'asc' => 'Artýan',
|
||||
'desc' => 'Pesýän',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'default_model_label' => 'ýazgy',
|
||||
|
||||
];
|
||||
9
lang/vendor/filament-widgets/tk/chart.php
vendored
Normal file
9
lang/vendor/filament-widgets/tk/chart.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'actions' => [
|
||||
'filter' => [
|
||||
'label' => 'Süzgüç',
|
||||
],
|
||||
],
|
||||
];
|
||||
500
resources/codes/codes.txt
Normal file
500
resources/codes/codes.txt
Normal file
@@ -0,0 +1,500 @@
|
||||
1_3VKL
|
||||
2_MCPW
|
||||
3_7PMX
|
||||
4_TZT9
|
||||
5_0C7Y
|
||||
6_WIVJ
|
||||
7_SGAJ
|
||||
8_H4U8
|
||||
9_NS9X
|
||||
10_812A
|
||||
11_EK7K
|
||||
12_TE33
|
||||
13_XWNL
|
||||
14_1NTQ
|
||||
15_CDLW
|
||||
16_8JUV
|
||||
17_JPDJ
|
||||
18_U21E
|
||||
19_EBRZ
|
||||
20_UPF8
|
||||
21_PKLI
|
||||
22_21DT
|
||||
23_BGR4
|
||||
24_USZQ
|
||||
25_8GQ3
|
||||
26_CVRV
|
||||
27_XNJG
|
||||
28_THB6
|
||||
29_PMCG
|
||||
30_MFT5
|
||||
31_WDP9
|
||||
32_Q30T
|
||||
33_VOOK
|
||||
34_0BTS
|
||||
35_TULN
|
||||
36_4VV9
|
||||
37_K2F4
|
||||
38_LMBA
|
||||
39_85X7
|
||||
40_4PUS
|
||||
41_9OLE
|
||||
42_11YM
|
||||
43_WRI9
|
||||
44_F2M0
|
||||
45_VW7L
|
||||
46_LZ79
|
||||
47_7PY5
|
||||
48_R80B
|
||||
49_M2QS
|
||||
50_MNC6
|
||||
51_91MD
|
||||
52_TE3C
|
||||
53_AF5L
|
||||
54_EF1S
|
||||
55_8EKV
|
||||
56_KPYO
|
||||
57_HSW6
|
||||
58_PBEN
|
||||
59_F3NM
|
||||
60_GA7C
|
||||
61_BBFM
|
||||
62_BP3J
|
||||
63_FC7K
|
||||
64_K7YK
|
||||
65_WIPQ
|
||||
66_ZJ6A
|
||||
67_U0GY
|
||||
68_FSMM
|
||||
69_YDEG
|
||||
70_98JY
|
||||
71_3UF4
|
||||
72_WCLV
|
||||
73_89K6
|
||||
74_HZFK
|
||||
75_UC84
|
||||
76_M55S
|
||||
77_LAG2
|
||||
78_NZ5P
|
||||
79_DKIG
|
||||
80_DP4T
|
||||
81_UYOC
|
||||
82_ILY0
|
||||
83_GR2J
|
||||
84_KM6B
|
||||
85_KVI3
|
||||
86_1SSF
|
||||
87_NNPW
|
||||
88_I1OD
|
||||
89_GDMS
|
||||
90_8QUH
|
||||
91_8JMX
|
||||
92_KHVE
|
||||
93_WM7B
|
||||
94_L4O6
|
||||
95_QQ49
|
||||
96_4JC1
|
||||
97_TDI2
|
||||
98_1TM0
|
||||
99_5E3R
|
||||
100_KP4E
|
||||
101_9O8Y
|
||||
102_TXAO
|
||||
103_ITQ4
|
||||
104_NZAZ
|
||||
105_RIWG
|
||||
106_OTA9
|
||||
107_1CW5
|
||||
108_0IF4
|
||||
109_GURI
|
||||
110_JUM2
|
||||
111_WDMH
|
||||
112_93G4
|
||||
113_HWVP
|
||||
114_OZTR
|
||||
115_9C6X
|
||||
116_HC8N
|
||||
117_293T
|
||||
118_GL0T
|
||||
119_N0DT
|
||||
120_QZ4D
|
||||
121_EG3S
|
||||
122_W4TI
|
||||
123_3RW6
|
||||
124_KT1X
|
||||
125_RKPO
|
||||
126_R8ZS
|
||||
127_XZS1
|
||||
128_U41S
|
||||
129_H8VI
|
||||
130_IZD7
|
||||
131_3FJA
|
||||
132_EWCU
|
||||
133_DIEM
|
||||
134_TO2S
|
||||
135_49NX
|
||||
136_CEEL
|
||||
137_Y9MG
|
||||
138_YO87
|
||||
139_3081
|
||||
140_ZECX
|
||||
141_NMH9
|
||||
142_8W3Z
|
||||
143_KNV9
|
||||
144_LZU9
|
||||
145_36HY
|
||||
146_SF8N
|
||||
147_IZGI
|
||||
148_Q8HH
|
||||
149_5PQK
|
||||
150_BJM8
|
||||
151_6LFK
|
||||
152_XTBN
|
||||
153_4D37
|
||||
154_2DAI
|
||||
155_5AZ7
|
||||
156_UQ0A
|
||||
157_XPRJ
|
||||
158_EHRC
|
||||
159_JC06
|
||||
160_LA9J
|
||||
161_B5SZ
|
||||
162_W5W3
|
||||
163_OF1U
|
||||
164_GC5D
|
||||
165_8WK8
|
||||
166_XH10
|
||||
167_R7H0
|
||||
168_GWOT
|
||||
169_8M0N
|
||||
170_BRAA
|
||||
171_PL05
|
||||
172_7DCY
|
||||
173_Q8E7
|
||||
174_J7QE
|
||||
175_RPKN
|
||||
176_7UKB
|
||||
177_927Y
|
||||
178_M4OZ
|
||||
179_VF3C
|
||||
180_0LCS
|
||||
181_CU8W
|
||||
182_HCVL
|
||||
183_EF1H
|
||||
184_AOG1
|
||||
185_K3QX
|
||||
186_DY9P
|
||||
187_X658
|
||||
188_LZZW
|
||||
189_959S
|
||||
190_O577
|
||||
191_X1NU
|
||||
192_KU9L
|
||||
193_QFUC
|
||||
194_CB48
|
||||
195_ZR2G
|
||||
196_UIFK
|
||||
197_BL2X
|
||||
198_L896
|
||||
199_QIU6
|
||||
200_N1XM
|
||||
201_IIZW
|
||||
202_H1W8
|
||||
203_KUAT
|
||||
204_HM4E
|
||||
205_BWLL
|
||||
206_RP4T
|
||||
207_LSU6
|
||||
208_EXBW
|
||||
209_I74Q
|
||||
210_QUKQ
|
||||
211_66SO
|
||||
212_PSOP
|
||||
213_4HJU
|
||||
214_IPYE
|
||||
215_I1W7
|
||||
216_X8ZV
|
||||
217_YEZB
|
||||
218_MPMJ
|
||||
219_A5GT
|
||||
220_1HJT
|
||||
221_8RWP
|
||||
222_URNI
|
||||
223_G90D
|
||||
224_BXEN
|
||||
225_QYEV
|
||||
226_K20E
|
||||
227_6X2P
|
||||
228_JI0S
|
||||
229_PTDE
|
||||
230_LLLG
|
||||
231_3IWX
|
||||
232_RHNR
|
||||
233_F8XQ
|
||||
234_SXRC
|
||||
235_Y4LK
|
||||
236_1E5V
|
||||
237_CO7W
|
||||
238_U7Y1
|
||||
239_8QXP
|
||||
240_0WO7
|
||||
241_BS3V
|
||||
242_E5T2
|
||||
243_0ETP
|
||||
244_7MIU
|
||||
245_25DW
|
||||
246_OCPS
|
||||
247_XBP9
|
||||
248_QDA1
|
||||
249_2CXJ
|
||||
250_STYT
|
||||
251_ZNLZ
|
||||
252_Z0NJ
|
||||
253_PE45
|
||||
254_8K30
|
||||
255_WK9E
|
||||
256_N12C
|
||||
257_NHU8
|
||||
258_JM5W
|
||||
259_QAP6
|
||||
260_O73L
|
||||
261_3TFN
|
||||
262_23I2
|
||||
263_6ZT4
|
||||
264_AV12
|
||||
265_9S0I
|
||||
266_0WIB
|
||||
267_MW1C
|
||||
268_HL55
|
||||
269_0L3E
|
||||
270_JMKI
|
||||
271_U4EJ
|
||||
272_YLN2
|
||||
273_MR75
|
||||
274_8K7S
|
||||
275_JPVV
|
||||
276_SMQ8
|
||||
277_935M
|
||||
278_H7SA
|
||||
279_Z0WG
|
||||
280_P58E
|
||||
281_RUQL
|
||||
282_Q84V
|
||||
283_YH6B
|
||||
284_F5YL
|
||||
285_S8FP
|
||||
286_FW42
|
||||
287_LACT
|
||||
288_UG5P
|
||||
289_82KV
|
||||
290_MGJN
|
||||
291_3BBA
|
||||
292_W62R
|
||||
293_2D61
|
||||
294_E6Q8
|
||||
295_X43E
|
||||
296_OWL9
|
||||
297_H79G
|
||||
298_S1DH
|
||||
299_JRKD
|
||||
300_KG68
|
||||
301_9ER6
|
||||
302_1MVY
|
||||
303_3ST4
|
||||
304_LMTD
|
||||
305_SB8C
|
||||
306_J42I
|
||||
307_EIUX
|
||||
308_U5BL
|
||||
309_RZ2E
|
||||
310_20L2
|
||||
311_Y2EY
|
||||
312_YEUU
|
||||
313_HBY3
|
||||
314_SNPT
|
||||
315_7W31
|
||||
316_03QM
|
||||
317_WTA6
|
||||
318_2Z9V
|
||||
319_4C2F
|
||||
320_5HX6
|
||||
321_X2H7
|
||||
322_OHYV
|
||||
323_ORN8
|
||||
324_CA5O
|
||||
325_AJFS
|
||||
326_EGN1
|
||||
327_VBR0
|
||||
328_VO3N
|
||||
329_8QP5
|
||||
330_0A19
|
||||
331_R1BO
|
||||
332_FKH9
|
||||
333_KKUO
|
||||
334_L7UR
|
||||
335_LSKK
|
||||
336_ML7S
|
||||
337_MA9Y
|
||||
338_GGCE
|
||||
339_0NL9
|
||||
340_DXMF
|
||||
341_8I2Z
|
||||
342_KWO0
|
||||
343_K2XY
|
||||
344_Z8FN
|
||||
345_R04V
|
||||
346_IGGL
|
||||
347_3YL3
|
||||
348_E3W3
|
||||
349_FU44
|
||||
350_0X7B
|
||||
351_GI6E
|
||||
352_O3LC
|
||||
353_WER4
|
||||
354_E1WM
|
||||
355_7HPZ
|
||||
356_SRL0
|
||||
357_C0IG
|
||||
358_T47P
|
||||
359_3ADP
|
||||
360_MWPI
|
||||
361_XKW0
|
||||
362_KR6N
|
||||
363_U53I
|
||||
364_64LP
|
||||
365_N2BN
|
||||
366_16M0
|
||||
367_FDS4
|
||||
368_RY2M
|
||||
369_PPCM
|
||||
370_X7IX
|
||||
371_6XUN
|
||||
372_6IDF
|
||||
373_YL7O
|
||||
374_OTPM
|
||||
375_B086
|
||||
376_RLMV
|
||||
377_UNQN
|
||||
378_NX6Q
|
||||
379_335Q
|
||||
380_OB1Z
|
||||
381_ULKQ
|
||||
382_QHL2
|
||||
383_ZFFS
|
||||
384_NU7Z
|
||||
385_EM9Z
|
||||
386_GOM7
|
||||
387_R2WL
|
||||
388_J350
|
||||
389_KMA3
|
||||
390_8MPR
|
||||
391_PFZB
|
||||
392_P8W4
|
||||
393_E36O
|
||||
394_FA0T
|
||||
395_VLC4
|
||||
396_49FF
|
||||
397_0RMD
|
||||
398_FHCI
|
||||
399_ZT6A
|
||||
400_EE45
|
||||
401_4ULP
|
||||
402_2HHJ
|
||||
403_33D8
|
||||
404_QQBW
|
||||
405_KM9U
|
||||
406_5YQ2
|
||||
407_KAN2
|
||||
408_81RU
|
||||
409_8H5W
|
||||
410_R2FH
|
||||
411_YHG1
|
||||
412_W15M
|
||||
413_9MGE
|
||||
414_BL23
|
||||
415_44MX
|
||||
416_0XN1
|
||||
417_N1SE
|
||||
418_EV2G
|
||||
419_FDMW
|
||||
420_L599
|
||||
421_O8I8
|
||||
422_SDRS
|
||||
423_5FLL
|
||||
424_IKYD
|
||||
425_ZGU2
|
||||
426_AJM6
|
||||
427_PL0Q
|
||||
428_BQN7
|
||||
429_AFGG
|
||||
430_8XWJ
|
||||
431_916E
|
||||
432_QG5T
|
||||
433_PU29
|
||||
434_SKS9
|
||||
435_BVS6
|
||||
436_ROUE
|
||||
437_Z772
|
||||
438_KYU5
|
||||
439_4KY9
|
||||
440_8CMS
|
||||
441_QIYG
|
||||
442_ZTBY
|
||||
443_PPS1
|
||||
444_T5EX
|
||||
445_2FH9
|
||||
446_ULQX
|
||||
447_BI9M
|
||||
448_KSL5
|
||||
449_852B
|
||||
450_399J
|
||||
451_0PQS
|
||||
452_S75P
|
||||
453_N4FI
|
||||
454_KZ81
|
||||
455_4LAG
|
||||
456_O33N
|
||||
457_C9HS
|
||||
458_J28X
|
||||
459_0M05
|
||||
460_KIHC
|
||||
461_NX86
|
||||
462_F4IB
|
||||
463_KGXK
|
||||
464_C9LJ
|
||||
465_L2VB
|
||||
466_HU09
|
||||
467_GAFU
|
||||
468_B4AA
|
||||
469_6O5G
|
||||
470_8FB1
|
||||
471_I9KU
|
||||
472_I7MX
|
||||
473_L5OM
|
||||
474_OBTX
|
||||
475_T6YS
|
||||
476_6KSS
|
||||
477_6H6A
|
||||
478_W6IU
|
||||
479_PR66
|
||||
480_6D8V
|
||||
481_8MSK
|
||||
482_VV5S
|
||||
483_VO57
|
||||
484_EZJL
|
||||
485_8H2G
|
||||
486_RKBS
|
||||
487_NSJ9
|
||||
488_J316
|
||||
489_JQCW
|
||||
490_9IGH
|
||||
491_5HPW
|
||||
492_37JS
|
||||
493_Y4M6
|
||||
494_Z9UH
|
||||
495_8Q2V
|
||||
496_M3L4
|
||||
497_LVE9
|
||||
498_8FZ5
|
||||
499_2NK3
|
||||
500_BW2M
|
||||
3
resources/views/filament/pages/send-sms.blade.php
Normal file
3
resources/views/filament/pages/send-sms.blade.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<x-filament-panels::page>
|
||||
{{ $this->content }}
|
||||
</x-filament-panels::page>
|
||||
@@ -78,9 +78,13 @@
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="w-full max-w-sm mt-2xl flex flex-col gap-md">
|
||||
<button class="w-full bg-primary text-on-primary py-md rounded-full font-label-md text-label-md shadow-[0_4px_12px_rgba(0,105,72,0.3)] hover:opacity-90 active:scale-95 transition-all">
|
||||
Häzir ulan
|
||||
</button>
|
||||
<a
|
||||
href="https://daragt.com"
|
||||
target="_blank"
|
||||
class="w-full bg-primary text-on-primary py-md rounded-full font-label-md text-label-md shadow-[0_4px_12px_rgba(0,105,72,0.3)] hover:opacity-90 active:scale-95 transition-all text-center"
|
||||
>
|
||||
Daragt harytlary görmek
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Info Card -->
|
||||
|
||||
29
resources/views/verification/promotion-ended.blade.php
Normal file
29
resources/views/verification/promotion-ended.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header-back')
|
||||
<div class="w-10"></div>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<main class="flex-grow pt-2xl px-container-padding pb-xl flex flex-col mt-16 max-w-md mx-auto w-full">
|
||||
<div class="flex gap-xs mb-2xl justify-center">
|
||||
<div class="h-1.5 w-8 rounded-full bg-outline-variant"></div>
|
||||
<div class="h-1.5 w-8 rounded-full bg-outline-variant"></div>
|
||||
<div class="h-1.5 w-8 rounded-full bg-outline-variant"></div>
|
||||
</div>
|
||||
|
||||
<section class="space-y-base mb-2xl text-center">
|
||||
<h2 class="font-headline-xl text-headline-xl text-on-background">Aksiýa tamamlandy</h2>
|
||||
<p class="font-body-md text-body-md text-on-surface-variant">
|
||||
{{ \App\Exceptions\CouponPoolExhaustedException::MESSAGE }}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="flex items-start gap-sm bg-secondary-container/30 p-md rounded border border-secondary-container">
|
||||
<x-app-icon name="info" class="w-6 h-6 text-on-secondary-container mt-0.5 shrink-0" />
|
||||
<p class="font-body-sm text-body-sm text-on-secondary-container text-left">
|
||||
Ähli kuponlar paýlandy. Öň agza bolan ulanyjylar öz kodlaryny «Gutlaýarys» sahypasyndan görüp bilýärler.
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
@endsection
|
||||
154
tests/Feature/SmsBroadcastServiceTest.php
Normal file
154
tests/Feature/SmsBroadcastServiceTest.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\SmsSendStatus;
|
||||
use App\Jobs\SendSmsToCouponJob;
|
||||
use App\Models\Coupon;
|
||||
use App\Models\SmsCampaign;
|
||||
use App\Models\SmsSendLog;
|
||||
use App\Models\User;
|
||||
use App\Services\SmsBroadcastService;
|
||||
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SmsBroadcastServiceTest extends TestCase
|
||||
{
|
||||
use LazilyRefreshDatabase;
|
||||
|
||||
private SmsBroadcastService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Http::fake([
|
||||
config('services.sms.url') => Http::response('ok', 200),
|
||||
]);
|
||||
|
||||
$this->service = app(SmsBroadcastService::class);
|
||||
}
|
||||
|
||||
public function test_resolve_recipients_for_all_excluding_ids(): void
|
||||
{
|
||||
$included = Coupon::factory()->count(2)->create();
|
||||
$excluded = Coupon::factory()->create();
|
||||
|
||||
$recipients = $this->service->resolveRecipients(
|
||||
sendToAll: true,
|
||||
excludedIds: [$excluded->id],
|
||||
);
|
||||
|
||||
$this->assertCount(2, $recipients);
|
||||
$this->assertTrue($recipients->pluck('id')->contains($included->first()->id));
|
||||
$this->assertFalse($recipients->pluck('id')->contains($excluded->id));
|
||||
}
|
||||
|
||||
public function test_resolve_recipients_for_selected_only(): void
|
||||
{
|
||||
$selected = Coupon::factory()->create();
|
||||
Coupon::factory()->create();
|
||||
|
||||
$recipients = $this->service->resolveRecipients(
|
||||
sendToAll: false,
|
||||
selectedIds: [$selected->id],
|
||||
);
|
||||
|
||||
$this->assertCount(1, $recipients);
|
||||
$this->assertSame($selected->id, $recipients->first()->id);
|
||||
}
|
||||
|
||||
public function test_start_campaign_queues_chain_and_creates_logs(): void
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$admin = User::factory()->create();
|
||||
$coupons = Coupon::factory()->count(2)->create();
|
||||
|
||||
$campaign = $this->service->startCampaign(
|
||||
admin: $admin,
|
||||
message: 'Test broadcast',
|
||||
sendToAll: false,
|
||||
selectedIds: $coupons->pluck('id')->all(),
|
||||
);
|
||||
|
||||
$this->assertSame(2, $campaign->recipient_count);
|
||||
$this->assertDatabaseCount(SmsSendLog::class, 2);
|
||||
$this->assertDatabaseHas(SmsSendLog::class, [
|
||||
'sms_campaign_id' => $campaign->id,
|
||||
'status' => SmsSendStatus::Pending->value,
|
||||
]);
|
||||
|
||||
Bus::assertChained([
|
||||
SendSmsToCouponJob::class,
|
||||
SendSmsToCouponJob::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_invalid_phone_is_skipped_without_queue_job(): void
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$admin = User::factory()->create();
|
||||
$invalid = Coupon::factory()->create(['phone' => '81234567']);
|
||||
$valid = Coupon::factory()->create();
|
||||
|
||||
$campaign = $this->service->startCampaign(
|
||||
admin: $admin,
|
||||
message: 'Test',
|
||||
sendToAll: false,
|
||||
selectedIds: [$invalid->id, $valid->id],
|
||||
);
|
||||
|
||||
$this->assertSame(2, $campaign->recipient_count);
|
||||
$this->assertSame(1, $campaign->skipped_count);
|
||||
|
||||
$this->assertDatabaseHas(SmsSendLog::class, [
|
||||
'coupon_id' => $invalid->id,
|
||||
'status' => SmsSendStatus::SkippedInvalid->value,
|
||||
]);
|
||||
|
||||
Bus::assertChained([
|
||||
SendSmsToCouponJob::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_send_job_marks_success_and_completes_campaign(): void
|
||||
{
|
||||
Queue::fake();
|
||||
|
||||
$admin = User::factory()->create();
|
||||
$coupon = Coupon::factory()->create(['phone' => '61929248']);
|
||||
|
||||
$campaign = SmsCampaign::factory()->create([
|
||||
'message' => 'Queued hello',
|
||||
'recipient_count' => 1,
|
||||
'created_by' => $admin->id,
|
||||
]);
|
||||
|
||||
SmsSendLog::factory()->create([
|
||||
'sms_campaign_id' => $campaign->id,
|
||||
'coupon_id' => $coupon->id,
|
||||
'phone' => $coupon->phone,
|
||||
'message' => $campaign->message,
|
||||
'status' => SmsSendStatus::Pending,
|
||||
]);
|
||||
|
||||
$job = new SendSmsToCouponJob($campaign, $coupon);
|
||||
$job->handle();
|
||||
|
||||
Http::assertSentCount(1);
|
||||
|
||||
$campaign->refresh();
|
||||
|
||||
$this->assertSame(1, $campaign->sent_count);
|
||||
$this->assertNotNull($campaign->completed_at);
|
||||
$this->assertDatabaseHas(SmsSendLog::class, [
|
||||
'sms_campaign_id' => $campaign->id,
|
||||
'status' => SmsSendStatus::Sent->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
64
tests/Feature/StartSmsCampaignJobTest.php
Normal file
64
tests/Feature/StartSmsCampaignJobTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Jobs\SendSmsToCouponJob;
|
||||
use App\Jobs\StartSmsCampaignJob;
|
||||
use App\Models\Coupon;
|
||||
use App\Models\User;
|
||||
use App\Services\SmsBroadcastService;
|
||||
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Tests\TestCase;
|
||||
|
||||
class StartSmsCampaignJobTest extends TestCase
|
||||
{
|
||||
use LazilyRefreshDatabase;
|
||||
|
||||
public function test_job_is_dispatched_with_expected_payload(): void
|
||||
{
|
||||
Queue::fake();
|
||||
|
||||
$admin = User::factory()->create();
|
||||
$coupon = Coupon::factory()->create();
|
||||
|
||||
StartSmsCampaignJob::dispatch(
|
||||
adminId: $admin->id,
|
||||
message: 'Hello',
|
||||
sendToAll: false,
|
||||
selectedIds: [$coupon->id],
|
||||
excludedIds: [],
|
||||
);
|
||||
|
||||
Queue::assertPushed(StartSmsCampaignJob::class, function (StartSmsCampaignJob $job) use ($admin, $coupon): bool {
|
||||
return $job->adminId === $admin->id
|
||||
&& $job->message === 'Hello'
|
||||
&& $job->sendToAll === false
|
||||
&& $job->selectedIds === [$coupon->id]
|
||||
&& $job->excludedIds === [];
|
||||
});
|
||||
}
|
||||
|
||||
public function test_handle_starts_campaign_and_chains_send_jobs(): void
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$admin = User::factory()->create();
|
||||
$coupons = Coupon::factory()->count(2)->create();
|
||||
|
||||
$job = new StartSmsCampaignJob(
|
||||
adminId: $admin->id,
|
||||
message: 'Test broadcast',
|
||||
sendToAll: false,
|
||||
selectedIds: $coupons->pluck('id')->all(),
|
||||
);
|
||||
|
||||
$job->handle(app(SmsBroadcastService::class));
|
||||
|
||||
Bus::assertChained([
|
||||
SendSmsToCouponJob::class,
|
||||
SendSmsToCouponJob::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Exceptions\CouponPoolExhaustedException;
|
||||
use App\Models\Coupon;
|
||||
use App\Models\PhoneVerification;
|
||||
use App\Services\CouponCodePool;
|
||||
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Mockery;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class VerificationFlowTest extends TestCase
|
||||
@@ -82,15 +86,43 @@ class VerificationFlowTest extends TestCase
|
||||
->assertRedirect(route('verification.congratulations'))
|
||||
->assertCookie('device_registered');
|
||||
|
||||
$this->assertDatabaseHas(Coupon::class, [
|
||||
'phone' => self::UNMASKED_PHONE,
|
||||
]);
|
||||
$coupon = Coupon::query()->where('phone', self::UNMASKED_PHONE)->first();
|
||||
|
||||
$this->assertNotNull($coupon);
|
||||
$this->assertContains(
|
||||
$coupon->code,
|
||||
(new CouponCodePool)->allCodes(),
|
||||
);
|
||||
|
||||
$this->get(route('verification.congratulations'))
|
||||
->assertOk()
|
||||
->assertViewHas('code');
|
||||
}
|
||||
|
||||
public function test_index_shows_promotion_ended_when_pool_exhausted(): void
|
||||
{
|
||||
$this->mock(CouponCodePool::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('hasAvailable')->andReturn(false);
|
||||
});
|
||||
|
||||
$this->get(route('verification.index'))
|
||||
->assertOk()
|
||||
->assertViewIs('verification.promotion-ended')
|
||||
->assertSee(CouponPoolExhaustedException::MESSAGE);
|
||||
}
|
||||
|
||||
public function test_send_otp_rejects_when_pool_exhausted(): void
|
||||
{
|
||||
$this->mock(CouponCodePool::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('hasAvailable')->andReturn(false);
|
||||
});
|
||||
|
||||
$this->from(route('verification.index'))
|
||||
->post(route('verification.send'), ['phone' => self::PHONE])
|
||||
->assertRedirect(route('verification.index'))
|
||||
->assertSessionHasErrors(['phone' => CouponPoolExhaustedException::MESSAGE]);
|
||||
}
|
||||
|
||||
public function test_verify_otp_rejects_wrong_code(): void
|
||||
{
|
||||
PhoneVerification::factory()
|
||||
@@ -164,6 +196,13 @@ class VerificationFlowTest extends TestCase
|
||||
->assertStatus(429);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function extractOtpFromLastSms(): string
|
||||
{
|
||||
$recorded = Http::recorded()->all();
|
||||
@@ -172,7 +211,7 @@ class VerificationFlowTest extends TestCase
|
||||
|
||||
[$request] = $recorded[array_key_last($recorded)];
|
||||
|
||||
preg_match('/(\d{4})/', (string) $request->data()['code'], $matches);
|
||||
preg_match('/(\d{4})/', (string) $request->data()['message'], $matches);
|
||||
|
||||
$this->assertNotEmpty($matches[1]);
|
||||
|
||||
|
||||
73
tests/Unit/CouponCodePoolTest.php
Normal file
73
tests/Unit/CouponCodePoolTest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Coupon;
|
||||
use App\Services\CouponCodePool;
|
||||
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CouponCodePoolTest extends TestCase
|
||||
{
|
||||
use LazilyRefreshDatabase;
|
||||
|
||||
private string $codesPath;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->codesPath = tempnam(sys_get_temp_dir(), 'codes_');
|
||||
file_put_contents($this->codesPath, "1_AAAA\n2_BBBB\n3_CCCC\n");
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (is_file($this->codesPath)) {
|
||||
unlink($this->codesPath);
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_all_codes_loads_from_file(): void
|
||||
{
|
||||
$pool = new CouponCodePool($this->codesPath);
|
||||
|
||||
$this->assertSame(['1_AAAA', '2_BBBB', '3_CCCC'], $pool->allCodes());
|
||||
}
|
||||
|
||||
public function test_available_codes_excludes_used_coupons(): void
|
||||
{
|
||||
Coupon::factory()->create(['code' => '1_AAAA', 'phone' => '61111111']);
|
||||
|
||||
$pool = new CouponCodePool($this->codesPath);
|
||||
|
||||
$this->assertEqualsCanonicalizing(['2_BBBB', '3_CCCC'], $pool->availableCodes());
|
||||
}
|
||||
|
||||
public function test_pick_random_returns_only_unused_code(): void
|
||||
{
|
||||
Coupon::factory()->create(['code' => '2_BBBB', 'phone' => '61111111']);
|
||||
|
||||
$pool = new CouponCodePool($this->codesPath);
|
||||
|
||||
for ($i = 0; $i < 20; $i++) {
|
||||
$picked = $pool->pickRandom();
|
||||
$this->assertContains($picked, ['1_AAAA', '3_CCCC']);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_has_available_is_false_when_all_codes_used(): void
|
||||
{
|
||||
Coupon::factory()->create(['code' => '1_AAAA', 'phone' => '61111111']);
|
||||
Coupon::factory()->create(['code' => '2_BBBB', 'phone' => '62222222']);
|
||||
Coupon::factory()->create(['code' => '3_CCCC', 'phone' => '63333333']);
|
||||
|
||||
$pool = new CouponCodePool($this->codesPath);
|
||||
|
||||
$this->assertFalse($pool->hasAvailable());
|
||||
$this->assertNull($pool->pickRandom());
|
||||
$this->assertSame([], $pool->availableCodes());
|
||||
}
|
||||
}
|
||||
64
tests/Unit/CouponServiceTest.php
Normal file
64
tests/Unit/CouponServiceTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Exceptions\CouponPoolExhaustedException;
|
||||
use App\Models\Coupon;
|
||||
use App\Services\CouponCodePool;
|
||||
use App\Services\CouponService;
|
||||
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||
use Mockery;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CouponServiceTest extends TestCase
|
||||
{
|
||||
use LazilyRefreshDatabase;
|
||||
|
||||
public function test_find_or_create_assigns_code_from_pool(): void
|
||||
{
|
||||
$this->mock(CouponCodePool::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('pickRandom')->once()->andReturn('1_AAAA');
|
||||
});
|
||||
|
||||
$coupon = app(CouponService::class)->findOrCreateForPhone('61929248');
|
||||
|
||||
$this->assertSame('61929248', $coupon->phone);
|
||||
$this->assertSame('1_AAAA', $coupon->code);
|
||||
$this->assertDatabaseHas(Coupon::class, [
|
||||
'phone' => '61929248',
|
||||
'code' => '1_AAAA',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_find_or_create_returns_existing_coupon_without_picking(): void
|
||||
{
|
||||
$existing = Coupon::factory()->create(['phone' => '61929248', 'code' => '2_BBBB']);
|
||||
|
||||
$this->mock(CouponCodePool::class, function (MockInterface $mock): void {
|
||||
$mock->shouldNotReceive('pickRandom');
|
||||
});
|
||||
|
||||
$coupon = app(CouponService::class)->findOrCreateForPhone('61929248');
|
||||
|
||||
$this->assertTrue($existing->is($coupon));
|
||||
}
|
||||
|
||||
public function test_find_or_create_throws_when_pool_exhausted(): void
|
||||
{
|
||||
$this->mock(CouponCodePool::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('pickRandom')->andReturn(null);
|
||||
});
|
||||
|
||||
$this->expectException(CouponPoolExhaustedException::class);
|
||||
|
||||
app(CouponService::class)->findOrCreateForPhone('61929248');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
72
tests/Unit/SmsMessageAnalyzerTest.php
Normal file
72
tests/Unit/SmsMessageAnalyzerTest.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\SmsMessageAnalyzer;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SmsMessageAnalyzerTest extends TestCase
|
||||
{
|
||||
private SmsMessageAnalyzer $analyzer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->analyzer = new SmsMessageAnalyzer;
|
||||
}
|
||||
|
||||
public function test_empty_message_has_zero_parts(): void
|
||||
{
|
||||
$this->assertSame(0, $this->analyzer->estimatedParts(''));
|
||||
}
|
||||
|
||||
public function test_gsm_single_part_message(): void
|
||||
{
|
||||
$message = str_repeat('A', 160);
|
||||
|
||||
$this->assertTrue($this->analyzer->isGsm7($message));
|
||||
$this->assertSame(1, $this->analyzer->estimatedParts($message));
|
||||
}
|
||||
|
||||
public function test_gsm_multi_part_message(): void
|
||||
{
|
||||
$message = str_repeat('A', 161);
|
||||
|
||||
$this->assertSame(2, $this->analyzer->estimatedParts($message));
|
||||
}
|
||||
|
||||
public function test_unicode_single_part_message(): void
|
||||
{
|
||||
$message = 'Привет';
|
||||
|
||||
$this->assertFalse($this->analyzer->isGsm7($message));
|
||||
$this->assertSame(1, $this->analyzer->estimatedParts($message));
|
||||
}
|
||||
|
||||
public function test_unicode_multi_part_message(): void
|
||||
{
|
||||
$message = str_repeat('Я', 71);
|
||||
|
||||
$this->assertSame(2, $this->analyzer->estimatedParts($message));
|
||||
}
|
||||
|
||||
#[DataProvider('summaryProvider')]
|
||||
public function test_summary(string $message, string $expectedSubstring): void
|
||||
{
|
||||
$this->assertStringContainsString($expectedSubstring, $this->analyzer->summary($message));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{0: string, 1: string}>
|
||||
*/
|
||||
public static function summaryProvider(): array
|
||||
{
|
||||
return [
|
||||
'empty' => ['', '0 harp'],
|
||||
'gsm' => ['Hello', '5 harp · 1 SMS'],
|
||||
'unicode' => ['你好', '2 harp · 1 SMS'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user