Compare commits

...

124 Commits

Author SHA1 Message Date
Mekan1206
a5bc322170 Enhance MigrationController and ShieldSeeder for improved data migration and role management
- Added new migrators: ModelHasRolesMigrator and OnlinePaymentsMigrator to MigrationController.
- Introduced a test method in MigrationController to facilitate online payments migration.
- Updated ShieldSeeder to include role IDs for better role management and added new roles with specific IDs.
2025-12-21 20:58:10 +05:00
Mekan1206
749208ac97 Add new migrators to MigrationController for enhanced data handling
- Included CardOrdersMigrator, CardPinOrdersMigrator, and VisaMasterSettingsMigrator to the MigrationController.
- Updated the index method to incorporate new migrators for improved migration processes.
2025-12-21 20:00:14 +05:00
Mekan1206
1870583441 Add Laravel Sanctum for API authentication and update routes
- Included `laravel/sanctum` in `composer.json` for lightweight API authentication.
- Updated `User` model to use `HasApiTokens` trait for token management.
- Configured API routing in `bootstrap/app.php`.
- Modified `DatabaseSeeder` to include `ShieldSeeder` and adjusted `FillJsonData` seeder method.
- Changed JSON data path in `ProvincesMigrator` for testing purposes.
- Updated web routes to utilize `MigrationController` for better organization.
2025-12-21 19:50:38 +05:00
Mekan1206
76c05ebe7c Refactor ActivityLogModule and clean up seeders
- Adjusted indentation in ActivityLogModule methods for consistency.
- Simplified ActivityLogRepository class by removing unnecessary lines.
- Updated FillJsonData seeder to streamline the run method.
- Modified various Migrator classes to change JSON data paths for testing and added sequence reset statements for better database integrity.
2025-12-21 19:16:26 +05:00
Mekan1206
515731003c Remove activity log migration files and update FillJsonData seeder to include ActionEventsMigrator. Clean up LoanOrdersMigrator comments for clarity. 2025-12-21 18:57:21 +05:00
Mekan1206
a9c7ec6b80 Remove pxlrbt/filament-activity-log dependency from composer.json and update composer.lock. Enhance LoginController validation by adding a rule to exclude a specific username. Minor adjustments in migration files for activity log to improve code organization. 2025-12-21 17:51:38 +05:00
Mekan1206
4038d47582 add composer require spatie/laravel-activitylog 2025-12-21 06:42:41 +05:00
Mekan1206
326987d6de Add phone or username placeholder in login view and update Turkish translations
- Updated the login view to include a placeholder for phone or username input.
- Added a new Turkish translation for the phone or username placeholder to enhance user guidance.
2025-12-21 06:20:41 +05:00
Mekan1206
f433fc3e41 Remove unused Blade components and templates from the Filament package to streamline the codebase. This includes deleting various components related to forms, notifications, pagination, and widgets, enhancing maintainability and reducing clutter. 2025-12-21 06:15:33 +05:00
Mekan1206
5361c00e7b images better 2025-12-21 05:31:08 +05:00
Mekan1206
5e37bd1a76 Enhance ListRecords pages with tab functionality and default sorting
- Added getTabs method to ListCardPinOrders, ListLoanOrders, and ListLoanOrderMobiles pages to display order statuses for system users.
- Implemented default sorting by 'created_at' in LoanOrdersTable and LoanOrderMobilesTable for improved data organization.
- Updated ManageCards page to ensure proper type declaration for getTitle method.
2025-12-21 05:21:53 +05:00
Mekan1206
5a53b90272 Add default sorting to CardPinOrdersTable by creation date
- Updated CardPinOrdersTable to default sort by 'created_at' in descending order for improved data visibility.
2025-12-21 05:11:21 +05:00
Mekan1206
59548a486f Enhance CardOrder model and SMS functionality
- Added unique ID generation and SMS notification upon CardOrder creation.
- Updated CardOrdersTable to default sort by creation date.
- Implemented logging for SMS sending in SmsRepository for better debugging.
2025-12-21 05:05:28 +05:00
Mekan1206
c0bfe974ad Add avatar functionality to User model
- Implemented HasAvatar contract in User model to support avatar management.
- Added getFilamentAvatarUrl method to return the default avatar URL.
2025-12-21 04:20:45 +05:00
Mekan1206
895c5f6226 Update ManageCards page and localization for card creation
- Added a label for the 'Add card' action in the ManageCards page.
- Updated Turkish translations for card creation actions and notifications to improve clarity and consistency.
2025-12-21 04:13:47 +05:00
Mekan1206
88d84ac457 Add title method to ManageCards page for improved user experience 2025-12-21 04:01:05 +05:00
Mekan1206
94ad59ce24 Enhance user role management and update Filament panel middleware
- Added new user roles: operator, operator_card, operator_loan, client, and currency_maintainer in ShieldSeeder.
- Included EnsureUserHasRole middleware in the Filament panel for improved access control.
2025-12-21 03:49:55 +05:00
Mekan1206
b1630ea623 Add policy mappings for VisaMasterPaymentOrder and OnlinePayment models 2025-12-21 03:44:14 +05:00
Mekan1206
6a700fbd4b Enhance user role checks and update card order tabs visibility
- Updated the isSystemUser method to include currency maintainers in role checks.
- Modified getTabs method in ListCardOrders to return an empty array for non-system users.
- Added a dehydrate state function for Turkmen phone numbers in UserForm schema.
2025-12-21 03:38:52 +05:00
Mekan1206
8e548126b2 Refactor VisaMasterPaymentOrdersMigrator to enhance data migration process
- Updated the migration logic to insert records into the 'visa_master_payment_orders' table.
- Improved handling of sender and receiver data by decoding JSON fields and providing fallback values.
- Added a test route in web.php to trigger the migration for debugging purposes.
2025-12-21 03:18:50 +05:00
Mekan1206
2ec9b28a60 Refactor VisaMasterPaymentOrder components and enhance seeding process
- Updated record title attribute in VisaMasterPaymentOrderResource to 'unique_id'.
- Improved schema layout in VisaMasterPaymentOrderForm and VisaMasterPaymentOrderInfolist for better user experience.
- Added 'unique_id' field to VisaMasterPaymentOrderInfolist with styling adjustments.
- Implemented BelongsToBranch interface in VisaMasterPaymentOrder model and added a boot method for loan order creation.
- Expanded FillJsonData seeder to include migration for VisaMasterPaymentOrders.
2025-12-21 02:58:15 +05:00
Mekan1206
4fc242bc7d Update dependencies and enhance components
- Added "halaxa/json-machine" dependency to composer.json.
- Updated content hash in composer.lock to reflect changes.
- Improved SpatieMediaLibraryFileEntry component documentation.
- Expanded FillJsonData seeder to include new migrators for card types, orders, and pin orders.
- Updated Tailwind CSS version in app.css.
- Refactored various JavaScript components for better performance and readability.
- Added a test route in web.php for debugging purpose
2025-12-21 02:41:27 +05:00
Mekan1206
6df3a27383 ignore database/data folder 2025-12-20 02:38:06 +05:00
Mekan1206
7111b1db09 Enhance SpatieMediaLibraryFileEntry component and update PHPStan configuration
- Added a new method to retrieve media for the entry in SpatieMediaLibraryFileEntry component.
- Updated PHPStan configuration to exclude the SpatieMediaLibraryFileEntry.php file from analysis.
2025-12-20 02:34:19 +05:00
Mekan1206
c4ee279d91 Refactor VisaMasterPaymentOrderInfolist and SpatieMediaLibraryFileEntry components
- Cleaned up imports and improved code formatting for better readability.
- Adjusted layout in VisaMasterPaymentOrderInfolist schema for consistent column spans.
- Enhanced SpatieMediaLibraryFileEntry component by streamlining property definitions and ensuring consistent formatting.
2025-12-20 02:30:36 +05:00
Mekan1206
14c4ea5791 Update subNavigationPosition in CardsCluster to Top 2025-12-20 02:28:48 +05:00
Mekan1206
01a0aa8ca1 dont ignore public/build 2025-12-20 00:56:53 +05:00
Mekan1206
97fea41b62 WIP 2025-12-20 00:40:22 +05:00
Mekan1206
5568d32a31 Update project name and enable seeders in DatabaseSeeder
- Changed project name in package-lock.json from 'backend_tbbank' to 'online'.
- Enabled UsersTableSeeder and ShieldSeeder in DatabaseSeeder for data seeding.
2025-12-20 00:29:34 +05:00
4e78ff77cc wip 2025-12-19 17:18:36 +05:00
4c0917e6ba Refactor media display logic in SpatieMediaLibraryFileEntry component
- Simplified media handling by removing the check for images only.
- Updated grid layout for media items to use a consistent column size.
- Enhanced modal functionality to open specific image previews based on item ID.
- Improved code readability by renaming variables and streamlining the foreach loop.
2025-12-09 01:40:07 +05:00
c56e3383d1 wip 2025-12-09 01:26:57 +05:00
967501d58f wip 2025-12-09 00:54:13 +05:00
88fdc3aa7f Update dependencies and refine Filament configuration
- Upgraded @tailwindcss/vite and tailwindcss to version 4.1.17 in package.json.
- Modified Vite configuration to include a new input file for Filament theme.
- Added AdvancedFileUpload component to VisaMasterPaymentOrderForm.
- Removed unused SpatieMediaLibraryFileUpload from VisaMasterPaymentOrderInfolist.
- Enhanced VisaMasterPaymentOrderInfolist schema for better layout and organization.
- Set Vite theme path in WorkPanelProvider for improved styling.
2025-12-08 21:47:06 +05:00
183916a62a add filament-upload-pro 2025-11-18 01:50:54 +05:00
a9490e132d wip 2025-11-18 01:46:06 +05:00
438e5a3f43 Add View Page for VisaMasterPaymentOrder and Update Schema
- Introduced ViewVisaMasterPaymentOrder page for displaying payment order details.
- Updated VisaMasterPaymentOrderResource to include a route for the new view page.
- Enhanced VisaMasterPaymentOrderInfolist schema to include location details and conditional visibility for notes.
- Enabled ViewAction in VisaMasterPaymentOrdersTable for accessing the new view page.
- Modified VisaMasterPaymentOrder model to allow nullable notes property.
2025-11-18 01:09:35 +05:00
cbd469c97f wip 2025-11-18 00:37:21 +05:00
bc45f2756e some stan 2025-11-18 00:34:40 +05:00
73448548e7 wip 2025-11-18 00:30:35 +05:00
f0e9767e78 wip 2025-11-18 00:23:04 +05:00
0e39450849 wip 2025-11-18 00:21:26 +05:00
4c83d41eb0 wip 2025-11-18 00:19:28 +05:00
bccf52a8fb wip 2025-11-18 00:19:04 +05:00
6e03c4d218 wip 2025-11-18 00:16:52 +05:00
53f8c893ca wip 2025-11-18 00:16:04 +05:00
52d070f4e5 wip 2025-11-18 00:13:08 +05:00
209f3899b5 wip 2025-11-18 00:11:11 +05:00
58d6ef0a9a Add navigation sort order to clusters and update profile panel sorting 2025-11-17 23:56:51 +05:00
1a0756fdca wip 2025-11-17 23:37:18 +05:00
f506972c91 wip 2025-11-16 18:05:56 +05:00
19abe08a90 wip 2025-11-16 18:04:18 +05:00
6428c9e20c wip 2025-11-16 17:29:03 +05:00
9d66f21854 wip 2025-11-16 17:27:33 +05:00
da5bb6e99b wip 2025-11-16 17:24:55 +05:00
680d5d4130 wip 2025-11-16 17:23:26 +05:00
f73a510984 wip 2025-11-16 17:20:40 +05:00
4b7c3da3d4 wip 2025-11-16 17:20:09 +05:00
b0877399eb wip 2025-11-16 17:17:23 +05:00
3b5b7f10c4 wip 2025-11-16 17:13:18 +05:00
45e95725cd wip 2025-11-16 17:09:54 +05:00
a98a41efab wip 2025-11-16 17:06:44 +05:00
3911e8e21a wip 2025-11-16 17:03:30 +05:00
dc871f36d4 wip 2025-11-16 17:00:58 +05:00
2dd06d0e4f wip 2025-11-16 16:50:55 +05:00
a9e5f7ece8 wip 2025-11-16 16:48:37 +05:00
7e7d583973 wip 2025-11-16 16:45:36 +05:00
ba6d703893 wip 2025-11-16 16:41:37 +05:00
2bb9ec2ae2 wip 2025-11-16 16:40:06 +05:00
1d67616cdb wip 2025-11-16 16:37:19 +05:00
5a37d56cfb wip 2025-11-16 16:31:16 +05:00
c1b3a58c3a wip 2025-11-16 00:52:35 +05:00
c94ac5d12d some stan 2025-11-15 21:32:03 +05:00
c24f7cbac6 Refactor payment processing: update method names for clarity, enhance VisaMasterPaymentOrderRepository with payment validation logic, and improve Turkish translations for payment notifications. 2025-11-15 18:51:40 +05:00
8637c22ed7 Refactor CurrencyRateForm and PayVisaMasterPaymentAction: streamline component definitions, enhance layout with fieldsets, and update Turkish translations for payment-related terms. 2025-11-14 18:29:14 +05:00
2659aae278 Refactor CurrencyRateResource and VisaMasterSettings: remove unnecessary whitespace, add property annotations, and define possible setting types. Update Turkish translations for warning text and content. 2025-11-13 22:46:19 +05:00
038b5ea2fb Add navigation sorting to SettingsCluster, enhance CurrencyRate model with table association and name attribute, and update Turkish translations for currency terms 2025-11-13 22:12:35 +05:00
54e11dcbb4 work on pay visa master action 2025-11-13 22:00:23 +05:00
3ea35f6bca currency rates 2025-11-13 21:51:43 +05:00
b67fd61bf8 wip 2025-11-13 21:48:38 +05:00
816a6c2f60 Refactor VisaMasterPaymentOrder resource: removed infolist method, updated header actions in edit and view pages, and improved infolist styling. 2025-11-13 21:17:20 +05:00
294ec0144d locale fix 2025-11-13 11:49:41 +05:00
c2e9198dbc wip 2025-11-13 01:14:53 +05:00
074fe44ab3 wip 2025-11-13 00:35:29 +05:00
31ab39b21c wip 2025-11-13 00:13:29 +05:00
7bdd5243bd wip 2025-11-10 19:53:22 +05:00
0da1efe5c3 wip 2025-11-10 19:18:53 +05:00
8c24f00382 wip 2025-11-10 17:58:06 +05:00
60f7efd6ba wip 2025-11-10 17:55:13 +05:00
c16bb98ed3 wip 2025-11-10 17:51:23 +05:00
0779e4b9f6 visa master basic form 2025-11-07 01:43:27 +05:00
9be16228f7 add resource for visamaster 2025-11-07 01:34:58 +05:00
84f551ddc6 add visa 2025-11-07 01:31:12 +05:00
96415c2bc1 Refactor getRedirectUrl method to ensure URL is treated as a string 2025-11-07 01:17:01 +05:00
8c33cc6645 wip 2025-11-04 23:35:46 +05:00
cc3a9cd854 wip 2025-11-04 23:10:50 +05:00
65a47e8028 wip 2025-11-04 22:04:41 +05:00
310590010c wip 2025-11-04 21:07:23 +05:00
84c4a584a0 wip 2025-11-04 00:23:41 +05:00
1f31b020e8 wip 2025-11-03 23:42:35 +05:00
10a08d5f45 wip 2025-11-03 23:31:51 +05:00
504ddfbf8d wip 2025-11-03 23:29:08 +05:00
516c17326c wip 2025-11-03 20:03:02 +05:00
e9f95b7ca8 wip 2025-11-03 18:20:57 +05:00
e79d77625f wip 2025-11-03 17:34:58 +05:00
3953ee93b9 wip 2025-11-03 16:19:26 +05:00
b728a374a5 wip 2025-11-03 14:56:02 +05:00
3369e97a3c wip 2025-11-03 14:01:00 +05:00
5f2760713f wip 2025-11-03 13:57:50 +05:00
09c0bec901 wip 2025-11-03 12:18:26 +05:00
38b9908e1e wip 2025-11-03 11:54:07 +05:00
870aba0bcf wip 2025-11-03 11:44:46 +05:00
c147f47876 wip 2025-11-03 10:44:43 +05:00
6c38642af8 wip 2025-11-03 10:11:04 +05:00
fadda1f0d2 wip 2025-11-03 10:06:07 +05:00
7119ae4cb7 wip 2025-11-03 09:43:08 +05:00
af0f24a6b6 wok 2025-11-03 09:23:33 +05:00
711b5373da add policies 2025-11-03 09:15:36 +05:00
e9e9f7dbd5 refactor loan paid off letter for clarity and consistency 2025-11-03 00:15:31 +05:00
2df920af47 add file to lan paid off letter 2025-11-02 23:47:06 +05:00
9c799ac105 add loan paid off letters 2025-11-02 23:43:46 +05:00
44f00a8f5d loan paid off ltter 2025-11-02 23:16:32 +05:00
8c1b128df2 russian translation 2025-11-02 23:03:26 +05:00
3916f7f8ea wip 2025-11-02 22:55:52 +05:00
422 changed files with 15341 additions and 17070 deletions

2
.gitignore vendored
View File

@@ -11,9 +11,9 @@
/.phpunit.cache
/.vscode
/.zed
/database/data/
/auth.json
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key

View File

@@ -6,3 +6,6 @@ create database nurmuhammetsdb;
ALTER USER ahat WITH PASSWORD 'K}#zL9@}QkR>MAHMYTJ';
SELECT setval('users_id_seq', (SELECT MAX(id) from users));
SELECT nextval('users_id_seq');

View File

@@ -11,7 +11,7 @@ class CreateCardOrder extends CreateRecord
protected static string $resource = CardOrderResource::class;
/**
* @return array<Action | ActionGroup>
* @return array<int, \Filament\Actions\Action>
*/
protected function getFormActions(): array
{
@@ -23,7 +23,7 @@ class CreateCardOrder extends CreateRecord
protected function getRedirectUrl(): string
{
$defaultUrl = $this->getResource()::getUrl('index');
$defaultUrl = string($this->getResource()::getUrl('index'));
/** @var \App\Modules\CardOrder\Models\CardOrder */
$record = $this->record;

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Cards\CardOrders\Pages;
use App\Filament\Clusters\Cards\CardOrders\CardOrderResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListCardOrders extends ListRecords
{
@@ -16,4 +18,23 @@ class ListCardOrders extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Filament\Clusters\Cards\CardOrders\Schemas;
use App\Modules\Country\Repositories\CountryRepository;
use App\Modules\FilamentPermission\Repositories\FilamentPermissionRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use App\Modules\Region\Repositories\RegionRepository;
@@ -20,6 +21,7 @@ use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Wizard;
use Filament\Schemas\Components\Wizard\Step;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
@@ -35,19 +37,24 @@ class CardOrderForm
Section::make(__('New card order'))
->columnSpan(4)
->columns(4)
->disabled(fn (string $context): bool => FilamentPermissionRepository::forClients())
->hidden(fn (string $context) => FilamentPermissionRepository::defaultSystemInput($context))
->components([
Select::make('status')
->label(__('Status'))
->options(OrderStatusRepository::statusValues())
->default(OrderStatusRepository::defaultStatus())
->native(false)
->required()
->columnSpan(2),
Toggle::make('paid')
->label(__('Paid'))
->inline(false)
->required(),
->disabled(true)
->onIcon(Heroicon::CheckCircle)
->offIcon(Heroicon::XCircle)
->onColor('success')
->offColor('danger'),
RichEditor::make('notes')
->label(__('Bellik'))
@@ -249,7 +256,7 @@ class CardOrderForm
->required()
->columnSpan(2),
])->columnSpan(4),
])->columnSpanFull(),
])->columnSpanFull()->skippable(fn (string $context) => $context === 'edit'),
]);
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Filament\Clusters\Cards\CardOrders\Tables;
use App\Modules\CardOrder\Filament\Actions\PayCardOrderAction;
use App\Modules\CardOrder\Models\CardOrder;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
@@ -15,17 +16,30 @@ use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class CardOrdersTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('unique_id')
->label(__('ID'))
->searchable(),
TextColumn::make('status')
->label(__('Status'))
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->badge()
->sortable()
->searchable(),
TextColumn::make('cardState.name')
->label(__('Reason'))
->searchable(),
@@ -64,10 +78,6 @@ class CardOrdersTable
->label(__('Phone'))
->searchable(),
TextColumn::make('status')
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->searchable(),
TextColumn::make('updated_at')
->label(__('Updated At'))
->dateTime()

View File

@@ -90,6 +90,10 @@ class CardResource extends Resource
{
return $table
->modifyQueryUsing(function (Builder $query) {
if (user()->isAdmin()) {
return;
}
$query->where('user_id', user()->id);
})
->columns([

View File

@@ -5,15 +5,23 @@ namespace App\Filament\Clusters\Cards\Cards\Pages;
use App\Filament\Clusters\Cards\Cards\CardResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ManageRecords;
use Illuminate\Contracts\Support\Htmlable;
class ManageCards extends ManageRecords
{
protected static string $resource = CardResource::class;
public function getTitle(): string|Htmlable
{
return __('My cards');
}
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
CreateAction::make()
->label(__('Add card'))
->createAnother(false),
];
}
}

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class CardsCluster extends Cluster
{
protected static ?int $navigationSort = 1;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCreditCard;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
@@ -17,4 +19,9 @@ class CardsCluster extends Cluster
{
return __('Cards');
}
public static function getClusterBreadcrumb(): string
{
return __('Cards');
}
}

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Cards\Resources\CardPinOrders\Pages;
use App\Filament\Clusters\Cards\Resources\CardPinOrders\CardPinOrderResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListCardPinOrders extends ListRecords
{
@@ -16,4 +18,23 @@ class ListCardPinOrders extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Filament\Clusters\Cards\Resources\CardPinOrders\Schemas;
use App\Modules\FilamentPermission\Repositories\FilamentPermissionRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use App\Modules\Region\Repositories\RegionRepository;
@@ -27,8 +28,10 @@ class CardPinOrderForm
->components([
Hidden::make('user_id')->default(Auth::id()),
Section::make(__('New loan order'))
Section::make(__('New card pin order'))
->columnSpanFull()
->disabled(fn (string $context): bool => FilamentPermissionRepository::forClients())
->hidden(fn (string $context) => FilamentPermissionRepository::defaultSystemInput($context))
->components([
Select::make('status')
->label(__('Status'))
@@ -43,7 +46,7 @@ class CardPinOrderForm
->columnSpanFull(),
]),
Section::make(__('New loan order'))
Section::make(__('New card pin order'))
->columnSpanFull()
->columns(6)
->components([

View File

@@ -2,6 +2,7 @@
namespace App\Filament\Clusters\Cards\Resources\CardPinOrders\Tables;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
@@ -9,12 +10,17 @@ use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class CardPinOrdersTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID'),
@@ -50,7 +56,10 @@ class CardPinOrdersTable
->searchable(),
TextColumn::make('status')
->label(__('Status'))
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->sortable()
->badge()
->searchable(),
])
->filters([

View File

@@ -5,9 +5,7 @@ namespace App\Filament\Clusters\Loans\LoanOrders;
use App\Filament\Clusters\Loans\LoanOrders\Pages\CreateLoanOrder;
use App\Filament\Clusters\Loans\LoanOrders\Pages\EditLoanOrder;
use App\Filament\Clusters\Loans\LoanOrders\Pages\ListLoanOrders;
use App\Filament\Clusters\Loans\LoanOrders\Pages\ViewLoanOrder;
use App\Filament\Clusters\Loans\LoanOrders\Schemas\LoanOrderForm;
use App\Filament\Clusters\Loans\LoanOrders\Schemas\LoanOrderInfolist;
use App\Filament\Clusters\Loans\LoanOrders\Tables\LoanOrdersTable;
use App\Filament\Clusters\Loans\LoansCluster;
use App\Modules\LoanOrder\Models\LoanOrder;
@@ -53,11 +51,6 @@ class LoanOrderResource extends Resource
return LoanOrderForm::configure($schema);
}
public static function infolist(Schema $schema): Schema
{
return LoanOrderInfolist::configure($schema);
}
public static function table(Table $table): Table
{
return LoanOrdersTable::configure($table);
@@ -75,7 +68,6 @@ class LoanOrderResource extends Resource
return [
'index' => ListLoanOrders::route('/'),
'create' => CreateLoanOrder::route('/create'),
'view' => ViewLoanOrder::route('/{record}'),
'edit' => EditLoanOrder::route('/{record}/edit'),
];
}

View File

@@ -6,7 +6,6 @@ use App\Filament\Clusters\Loans\LoanOrders\LoanOrderResource;
use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction;
use Filament\Actions\ViewAction;
use Filament\Resources\Pages\EditRecord;
class EditLoanOrder extends EditRecord
@@ -16,7 +15,6 @@ class EditLoanOrder extends EditRecord
protected function getHeaderActions(): array
{
return [
ViewAction::make(),
DeleteAction::make(),
ForceDeleteAction::make(),
RestoreAction::make(),

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Loans\LoanOrders\Pages;
use App\Filament\Clusters\Loans\LoanOrders\LoanOrderResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListLoanOrders extends ListRecords
{
@@ -16,4 +18,23 @@ class ListLoanOrders extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Filament\Clusters\Loans\LoanOrders\Schemas;
use App\Modules\FilamentPermission\Repositories\FilamentPermissionRepository;
use App\Modules\LoanOrder\Models\LoanOrderRequiredDocs;
use App\Modules\LoanOrder\Repositories\LoanOrderRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
@@ -39,6 +40,8 @@ class LoanOrderForm
Section::make(__('New loan order'))
->columnSpan(4)
->columns(4)
->disabled(fn (string $context): bool => FilamentPermissionRepository::forClients())
->hidden(fn (string $context) => FilamentPermissionRepository::defaultSystemInput($context))
->components([
Select::make('status')
->label(__('Status'))
@@ -91,7 +94,6 @@ class LoanOrderForm
TextInput::make('loan_amount')
->label(__('Loan amount'))
->numeric()
->required()
->minValue(1)
->maxValue(40000)
->suffix('TMT')
@@ -343,9 +345,10 @@ class LoanOrderForm
->displayFormat('d.m.Y')
->beforeOrEqual('today')
->required()
->native(false)
->columnSpan(1),
]),
])->columnSpan(4),
])->columnSpan(4)->skippable(fn (string $context) => $context === 'edit'),
]);
}
}

View File

@@ -2,21 +2,30 @@
namespace App\Filament\Clusters\Loans\LoanOrders\Tables;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Actions\ViewAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class LoanOrdersTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
$query->where('source', 'web');
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID')
@@ -30,7 +39,8 @@ class LoanOrdersTable
TextColumn::make('region')
->label(__('Region'))
->sortable()
->searchable(),
->searchable()
->formatStateUsing(fn (string $state) => RegionRepository::label($state)),
TextColumn::make('branch.name')
->label(__('Branch'))
@@ -53,9 +63,15 @@ class LoanOrdersTable
TextColumn::make('status')
->label(__('Status'))
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->sortable()
->searchable(),
TextColumn::make('status')
->badge()
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state)),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime()
@@ -78,7 +94,6 @@ class LoanOrdersTable
TrashedFilter::make(),
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([

View File

@@ -22,6 +22,7 @@ class LoanForm
->default(user()->getOption('passport_id')),
TextInput::make('account_number')
->label(__('Account number'))
->required()
->string()
->maxLength(23),

View File

@@ -4,18 +4,28 @@ namespace App\Filament\Clusters\Loans\Loans\Tables;
use App\Modules\Loan\Filaments\Actions\ShowLoanRemainingAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class LoansTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
if (user()->isAdmin()) {
return;
}
$query->where('user_id', user()->id);
})
->columns([
TextColumn::make('account_number')
->label(__('Account number'))
->searchable()
->sortable(),
@@ -34,6 +44,7 @@ class LoansTable
->recordActions([
ShowLoanRemainingAction::make(),
EditAction::make(),
DeleteAction::make(),
])
->toolbarActions([
BulkActionGroup::make([

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class LoansCluster extends Cluster
{
protected static ?int $navigationSort = 2;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedBanknotes;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles;
use App\Filament\Clusters\Loans\LoansCluster;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages\CreateLoanOrderMobile;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages\EditLoanOrderMobile;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages\ListLoanOrderMobiles;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Schemas\LoanOrderMobileForm;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Tables\LoanOrderMobilesTable;
use App\Modules\LoanOrderMobile\Models\LoanOrderMobile;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class LoanOrderMobileResource extends Resource
{
protected static ?string $model = LoanOrderMobile::class;
protected static ?int $navigationSort = 3;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDevicePhoneMobile;
protected static string|BackedEnum|null $activeNavigationIcon = Heroicon::DevicePhoneMobile;
protected static ?string $cluster = LoansCluster::class;
protected static ?string $recordTitleAttribute = 'unique_id';
public static function getNavigationLabel(): string
{
return __('module.loan-order::loan-order.loan_order').' (mobile app)';
}
public static function getModelLabel(): string
{
return __('module.loan-order::loan-order.loan_order').' (mobile app)';
}
public static function getPluralModelLabel(): string
{
return __('module.loan-order::loan-order.loan_orders').' (mobile app)';
}
public static function form(Schema $schema): Schema
{
return LoanOrderMobileForm::configure($schema);
}
public static function table(Table $table): Table
{
return LoanOrderMobilesTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListLoanOrderMobiles::route('/'),
'create' => CreateLoanOrderMobile::route('/create'),
'edit' => EditLoanOrderMobile::route('/{record}/edit'),
];
}
/**
* @return Builder<\App\Modules\LoanOrder\Models\LoanOrder>
*/
public static function getRecordRouteBindingEloquentQuery(): Builder
{
return parent::getRecordRouteBindingEloquentQuery()
->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\LoanOrderMobileResource;
use Filament\Resources\Pages\CreateRecord;
class CreateLoanOrderMobile extends CreateRecord
{
protected static string $resource = LoanOrderMobileResource::class;
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\LoanOrderMobileResource;
use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction;
use Filament\Resources\Pages\EditRecord;
class EditLoanOrderMobile extends EditRecord
{
protected static string $resource = LoanOrderMobileResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
ForceDeleteAction::make(),
RestoreAction::make(),
];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\LoanOrderMobileResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListLoanOrderMobiles extends ListRecords
{
protected static string $resource = LoanOrderMobileResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -0,0 +1,532 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Schemas;
use App\Modules\AppHelpers\Repositories\DateHelper;
use App\Modules\LoanOrder\Models\LoanOrderRequiredDocs;
use App\Modules\LoanOrder\Repositories\LoanOrderRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\PersonStates\Repositories\EducationRepository;
use App\Modules\PersonStates\Repositories\MarriageRepository;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use App\Modules\Region\Repositories\RegionRepository;
use App\Modules\TurkmenPassport\Repositories\TurkmenPassportRepository;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\FusedGroup;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Schema;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class LoanOrderMobileForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->columns(4)
->components([
Hidden::make('source')->default('mobile'),
Hidden::make('user_id')->default(Auth::id()),
Section::make(__('New loan order'))
->columnSpan(4)
->columns(4)
->components([
Select::make('status')
->label(__('Status'))
->options(OrderStatusRepository::statusValues())
->default(OrderStatusRepository::defaultStatus())
->native(false)
->required()
->columnSpan(2),
Select::make('satisfiable')
->label(__('Loan history'))
->options(LoanOrderRepository::satisfiableValues())
->native(false)
->columnSpan(2),
Select::make('loan_order_required_doc_id')
->label(__('Required documents'))
->relationship('requiredDocs', 'name')
->searchable()
->native(false)
->preload()
->live()
->afterStateUpdated(function ($state, callable $set) {
if ($state) {
/** @var null|LoanOrderRequiredDocs */
$requiredDoc = LoanOrderRequiredDocs::find($state);
if ($requiredDoc) {
$set('notes', $requiredDoc->value);
}
}
})
->columnSpanFull(),
RichEditor::make('notes')
->label(__('Bellik'))
->columnSpanFull(),
]),
Tabs::make('Loan Order')
->tabs([
Tab::make(__('Loan & Bank'))
->schema([
Fieldset::make(__('Loan type and amount'))
->schema([
Select::make('loan_type')
->label(__('Loan type'))
->relationship('loanType', 'name', fn (Builder $query) => $query->orderByTranslation('name')->where('id', 2))
->required(),
TextInput::make('loan_amount')
->label(__('Loan amount'))
->numeric()
->required()
->minValue(1)
->maxValue(40000)
->suffix('TMT')
->belowContent(__('Max is 40 000 TMT'))
->live(),
]),
Fieldset::make(__('Location'))
->schema([
Select::make('region')
->label(__('Region'))
->options(RegionRepository::values())
->live()
->afterStateUpdated(fn (callable $set) => $set('branch_id', null))
->required(),
Select::make('branch_id')
->label(__('Branch'))
->relationship('branch', 'name', function ($query, callable $get) {
$query->orderByTranslation('name');
$region = $get('region');
if ($region) {
$query->where('region', $region);
}
})
->required(),
]),
]),
Tab::make(__('Personal information'))
->columns(8)
->schema([
TextInput::make('customer_name')
->label(__('Name'))
->columnSpan(2)
->required()
->maxLength(255)
->autocomplete(Str::random(10))
->default(user()->first_name),
TextInput::make('customer_surname')
->label(__('Surname'))
->columnSpan(2)
->required()
->maxLength(255)
->default(user()->last_name),
TextInput::make('customer_patronic_name')
->label(__('Patronic name'))
->columnSpan(2)
->maxLength(255)
->default(user()->getOption('patronic_name')),
DatePicker::make('born_at')
->displayFormat('d.m.Y')
->label(__('Birth date'))
->native(false)
->columnSpan(2)
->required()
->beforeOrEqual('today')
->default(user()->getOption('born_at')),
FusedGroup::make([
Select::make('passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1)
->default(user()->getOption('passport_serie')),
TextInput::make('passport_id')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999')
->default(user()->getOption('passport_id')),
])
->columnSpan(3)
->label(__('Passport serie and number'))
->columns(2),
DatePicker::make('passport_given_at')
->label(__('Passport given date'))
->columnSpan(2)
->displayFormat('d.m.Y')
->native(false)
->closeOnDateSelection()
->beforeOrEqual('today')
->required()
->default(user()->getOption('passport_given_at')),
TextInput::make('born_place')
->columnSpan(3)
->label(__('Born place (passport)'))
->maxLength(255)
->required()
->default(user()->getOption('born_place')),
TextInput::make('passport_given_by')
->label(__('Passport given by'))
->columnSpan(4)
->maxLength(255)
->required()
->default(user()->getOption('passport_given_by')),
TextInput::make('passport_address')
->columnSpan(4)
->label(__('Proscription for home'))
->maxLength(255)
->required()
->default(user()->getOption('passport_address')),
TextInput::make('real_address')
->label(__('Current home address'))
->columnSpan(4)
->maxLength(255)
->required()
->default(user()->getOption('real_address')),
TextInput::make('email')
->label(__('Email'))
->email()
->maxLength(255)
->columnSpan(2)
->default(user()->getOption('email')),
TextInput::make('phone')
->label(__('Phone'))
->required()
->mask('99 99 99 99')
->prefix('+993')
->rules([
new PhoneNumberVerificationRule,
])
->columnSpan(2)
->default(user()->phone),
TextInput::make('phone_additional')
->label(__('Additional phone'))
->mask('99 99 99 99')
->prefix('+993')
->rules([
new PhoneNumberVerificationRule,
])
->columnSpan(2),
TextInput::make('phone_home')
->label(__('Home phone'))
->numeric()
->prefix('+993')
->columnSpan(2),
Select::make('education')
->columnSpan(2)
->label(__('Education'))
->options(EducationRepository::values())
->native(false)
->required(),
Select::make('marriage_status')
->columnSpan(2)
->label(__('Marital status'))
->options(MarriageRepository::values())
->native(false)
->required(),
]),
Tab::make(__('Salary card'))
->columns(6)
->schema([
TextInput::make('card_number')
->label(__('Card number'))
->mask('9999 9999 9999 9999')
->dehydrateStateUsing(fn ($state) => str_replace(' ', '', $state))
->columnSpan(2)
->required(),
TextInput::make('card_name')
->label(__('Card name'))
->maxLength(255)
->columnSpan(2)
->required(),
Select::make('card_month')
->label(__('Card month'))
->options(DateHelper::staticNumberMonths())
->native(false)
->columnSpan(1)
->required(),
Select::make('card_year')
->label(__('Card year'))
->options(DateHelper::staticNumberYears())
->native(false)
->columnSpan(1)
->required(),
]),
Tab::make(__('Pasport files'))
->columns(4)
->schema([
FileUpload::make('passport_one')
->label(__('Passport (page 1)'))
->image()
->maxSize(4096)
->required()
->columnSpan(2),
FileUpload::make('passport_two')
->label(__('Passport (page 2-3)'))
->image()
->maxSize(4096)
->required()
->columnSpan(2),
FileUpload::make('passport_three')
->label(__('Passport (page 8-9)'))
->image()
->maxSize(4096)
->required()
->columnSpan(2),
FileUpload::make('passport_four')
->label(__('Passport (page 32)'))
->image()
->maxSize(4096)
->required()
->columnSpan(2),
]),
Tab::make(__('Work'))
->columns(4)
->schema([
Select::make('work_region')
->label(__('Work region'))
->options(RegionRepository::values())
->columnSpan(1)
->live()
->afterStateUpdated(fn (callable $set) => $set('work_province_id', null))
->required(),
Select::make('work_province_id')
->label(__('Work province'))
->relationship('workProvince', 'name', function ($query, callable $get) {
$query->orderByTranslation('name');
$region = $get('work_region');
if ($region) {
$query->where('region', $region);
}
})
->columnSpan(1)
->required(),
TextInput::make('work_company')
->label(__('Work company name'))
->maxLength(255)
->required()
->columnSpan(2),
TextInput::make('work_company_accountant_number')
->label(__('HR number'))
->prefix('+993')
->numeric()
->required()
->columnSpan(1),
TextInput::make('work_position')
->label(__('Work position'))
->required()
->maxLength(255)
->columnSpan(1),
TextInput::make('work_salary')
->label(__('Salary'))
->numeric()
->required()
->columnSpan(1),
DatePicker::make('work_started_at')
->label(__('Work started at'))
->displayFormat('d.m.Y')
->beforeOrEqual('today')
->required()
->columnSpan(1),
]),
Tab::make(__('Guarantor').' 1')
->columns(6)
->schema([
TextInput::make('guarantor_name')
->label(__('Guarantor').' '.__('Name'))
->columnSpan(2)
->required()
->maxLength(255),
TextInput::make('guarantor_surname')
->label(__('Guarantor').' '.__('Surname'))
->columnSpan(2)
->required()
->maxLength(255),
TextInput::make('guarantor_patronic_name')
->label(__('Guarantor').' '.__('Patronic name'))
->columnSpan(2)
->maxLength(255),
FusedGroup::make([
Select::make('guarantor_passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1),
TextInput::make('guarantor_passport_id')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999'),
])
->columnSpan(3)
->label(__('Passport serie and number'))
->columns(2),
TextInput::make('guarantor_note')
->label(__('Guarantor').' '.__('Salary'))
->columnSpan(3)
->required()
->maxLength(255),
TextInput::make('guarantor_card_number')
->label(__('Card number'))
->mask('9999 9999 9999 9999')
->dehydrateStateUsing(fn ($state) => str_replace(' ', '', $state))
->columnSpan(2)
->required(),
TextInput::make('guarantor_card_name')
->label(__('Card name'))
->maxLength(255)
->columnSpan(2)
->required(),
Select::make('guarantor_card_month')
->label(__('Card month'))
->options(DateHelper::staticNumberMonths())
->native(false)
->columnSpan(1)
->required(),
Select::make('guarantor_card_year')
->label(__('Card year'))
->options(DateHelper::staticNumberYears())
->native(false)
->columnSpan(1)
->required(),
]),
Tab::make(__('Guarantor').' 2')
->columns(6)
->hidden(function (Get $get): bool {
$loan_amount = $get('loan_amount') ? string($get('loan_amount')) : 1;
return ! ($loan_amount && intval($loan_amount) > 20000);
})
->schema([
TextInput::make('guarantor_2_name')
->label(__('Guarantor').' '.__('Name'))
->columnSpan(2)
->required()
->maxLength(255),
TextInput::make('guarantor_2_surname')
->label(__('Guarantor').' '.__('Surname'))
->columnSpan(2)
->required()
->maxLength(255),
TextInput::make('guarantor_2_patronic_name')
->label(__('Guarantor').' '.__('Patronic name'))
->columnSpan(2)
->maxLength(255),
FusedGroup::make([
Select::make('guarantor_2_passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1),
TextInput::make('guarantor_2_passport_id')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999'),
])
->columnSpan(3)
->label(__('Passport serie and number'))
->columns(2),
TextInput::make('guarantor_2_note')
->label(__('Guarantor').' '.__('Salary'))
->columnSpan(3)
->required()
->maxLength(255),
TextInput::make('guarantor_2_card_number')
->label(__('Card number'))
->mask('9999 9999 9999 9999')
->dehydrateStateUsing(fn ($state) => str_replace(' ', '', $state))
->columnSpan(2)
->required(),
TextInput::make('guarantor_2_card_name')
->label(__('Card name'))
->maxLength(255)
->columnSpan(2)
->required(),
Select::make('guarantor_2_card_month')
->label(__('Card month'))
->options(DateHelper::staticNumberMonths())
->native(false)
->columnSpan(1)
->required(),
Select::make('guarantor_2_card_year')
->label(__('Card year'))
->options(DateHelper::staticNumberYears())
->native(false)
->columnSpan(1)
->required(),
]),
])->columnSpan(4),
]);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Schemas;
use Filament\Schemas\Schema;
class LoanOrderMobileInfolist
{
public static function configure(Schema $schema): Schema
{
return $schema
->columns(3)
->components([
]);
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Tables;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class LoanOrderMobilesTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
$query->where('source', 'mobile');
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID')
->sortable(),
TextColumn::make('region')
->label(__('Region'))
->sortable()
->searchable()
->formatStateUsing(fn (string $state) => RegionRepository::label($state)),
TextColumn::make('branch.name')
->label(__('Branch'))
->sortable()
->searchable(),
TextColumn::make('customer_name')
->label(__('Name'))
->sortable()
->searchable(),
TextColumn::make('customer_surname')
->label(__('Surname'))
->sortable()
->searchable(),
TextColumn::make('phone')
->label(__('Phone'))
->searchable(),
TextColumn::make('status')
->label(__('Status'))
->sortable()
->searchable()
->badge()
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state)),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime()
->sortable()
->toggleable(),
TextColumn::make('updated_at')
->dateTime()
->label(__('Updated At'))
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('deleted_at')
->label(__('Deleted At'))
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
TrashedFilter::make(),
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
ForceDeleteBulkAction::make(),
RestoreBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters;
use App\Filament\Clusters\Loans\LoansCluster;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages\CreateLoanPaidOffLetter;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages\EditLoanPaidOffLetter;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages\ListLoanPaidOffLetters;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Schemas\LoanPaidOffLetterForm;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Tables\LoanPaidOffLettersTable;
use App\Modules\LoanPaidOffLetter\Models\LoanPaidOffLetter;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use Illuminate\Contracts\Support\Htmlable;
class LoanPaidOffLetterResource extends Resource
{
protected static ?int $navigationSort = 3;
protected static ?string $model = LoanPaidOffLetter::class;
protected static ?string $cluster = LoansCluster::class;
public static function getNavigationIcon(): string|BackedEnum|Htmlable|null
{
return Heroicon::OutlinedDocumentText;
}
public static function getNavigationLabel(): string
{
return __('Loan paid off letters');
}
public static function getModelLabel(): string
{
return __('Loan paid off letter');
}
public static function getPluralModelLabel(): string
{
return __('Loan paid off letters');
}
protected static ?string $recordTitleAttribute = 'loan_reason';
public static function form(Schema $schema): Schema
{
return LoanPaidOffLetterForm::configure($schema);
}
public static function table(Table $table): Table
{
return LoanPaidOffLettersTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListLoanPaidOffLetters::route('/'),
'create' => CreateLoanPaidOffLetter::route('/create'),
'edit' => EditLoanPaidOffLetter::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\LoanPaidOffLetterResource;
use Filament\Resources\Pages\CreateRecord;
class CreateLoanPaidOffLetter extends CreateRecord
{
protected static string $resource = LoanPaidOffLetterResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\LoanPaidOffLetterResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditLoanPaidOffLetter extends EditRecord
{
protected static string $resource = LoanPaidOffLetterResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\LoanPaidOffLetterResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListLoanPaidOffLetters extends ListRecords
{
protected static string $resource = LoanPaidOffLetterResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Schemas;
use App\Modules\FilamentPermission\Repositories\FilamentPermissionRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use App\Modules\Region\Repositories\RegionRepository;
use App\Modules\TurkmenPassport\Repositories\TurkmenPassportRepository;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\FusedGroup;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class LoanPaidOffLetterForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->columns(6)
->components([
Hidden::make('user_id')->default(Auth::id()),
Section::make(__('New loan paid off letter'))
->columnSpan(4)
->columns(6)
->components([
Select::make('region')
->label(__('Region'))
->options(RegionRepository::values())
->live()
->afterStateUpdated(fn (callable $set) => $set('branch_id', null))
->columnSpan(3)
->required(),
Select::make('branch_id')
->label(__('Branch'))
->relationship('branch', 'name', function ($query, callable $get) {
$query->orderByTranslation('name');
$region = $get('region');
if ($region) {
$query->where('region', $region);
}
})
->columnSpan(3)
->required(),
TextInput::make('customer_name')
->label(__('Name'))
->columnSpan(2)
->default(user()->first_name)
->required()
->maxLength(255)
->autocomplete(Str::random(10))
->columnSpan(2),
TextInput::make('customer_surname')
->label(__('Surname'))
->columnSpan(2)
->default(user()->last_name)
->required()
->maxLength(255)
->columnSpan(2),
TextInput::make('customer_patronic_name')
->label(__('Patronic name'))
->columnSpan(2)
->default(user()->getOption('patronic_name'))
->maxLength(255)
->columnSpan(2),
DatePicker::make('born_at')
->displayFormat('d.m.Y')
->label(__('Birth date'))
->native(false)
->columnSpan(2)
->default(user()->getOption('born_at'))
->required()
->beforeOrEqual('today')
->columnSpan(2),
FusedGroup::make([
Select::make('passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->default(user()->getOption('passport_serie'))
->columnSpan(1),
TextInput::make('passport_id')
->label(__('Passport number'))
->required()
->columnSpan(1)
->default(user()->getOption('passport_id'))
->mask('999999'),
])
->columnSpan(2)
->label(__('Passport serie and number'))
->columns(2),
TextInput::make('phone')
->label(__('Phone'))
->required()
->mask('99 99 99 99')
->prefix('+993')
->default(user()->phone)
->rules([
new PhoneNumberVerificationRule,
])
->columnSpan(2),
TextInput::make('loan_contract_number')
->label(__('Loan contract number'))
->columnSpan(2)
->required()
->maxLength(255),
DatePicker::make('loan_contract_date')
->displayFormat('d.m.Y')
->label(__('Loan contract date'))
->native(false)
->columnSpan(2)
->required()
->beforeOrEqual('today')
->columnSpan(2),
TextInput::make('loan_amount')
->label(__('Loan amount'))
->columnSpan(2)
->required()
->numeric()
->columnSpan(2),
TextInput::make('loan_reason')
->label(__('Loan reason'))
->columnSpan(2)
->required()
->maxLength(255)
->columnSpan(6),
]),
Section::make(__('By operator'))
->columnSpan(2)
->disabled(fn (string $context): bool => FilamentPermissionRepository::forClients())
->hidden(fn (string $context) => FilamentPermissionRepository::defaultSystemInput($context))
->components([
Select::make('status')
->label(__('Status'))
->options(OrderStatusRepository::statusValues())
->default(OrderStatusRepository::defaultStatus())
->native(false)
->required()
->columnSpanFull(),
RichEditor::make('notes')
->label(__('Bellik'))
->toolbarButtons([
'h2',
'italic',
'orderedList',
'bold',
'strike',
'underline',
])
->columnSpanFull(),
FileUpload::make('loan_file')
->label(__('Loan paid off letter file'))
->columnSpanFull()
->maxSize(4096)
->columnSpan(2)
->downloadable()
->previewable(false),
]),
]);
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Tables;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class LoanPaidOffLettersTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID')
->sortable(),
TextColumn::make('region')
->label(__('Region'))
->formatStateUsing(fn (string $state) => RegionRepository::label($state)),
TextColumn::make('branch.name')
->label(__('Branch')),
TextColumn::make('customer_name')
->label(__('Name'))
->searchable(),
TextColumn::make('customer_surname')
->label(__('Surname'))
->searchable(),
TextColumn::make('phone')
->label(__('Phone'))
->searchable(),
TextColumn::make('status')
->label(__('Status'))
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->sortable()
->badge()
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Filament\Clusters\OnlinePayments;
use BackedEnum;
use Filament\Clusters\Cluster;
use Filament\Pages\Enums\SubNavigationPosition;
use Filament\Support\Icons\Heroicon;
class OnlinePaymentsCluster extends Cluster
{
protected static ?int $navigationSort = 5;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedServerStack;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
public static function getNavigationLabel(): string
{
return __('Online payments');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments;
use App\Filament\Clusters\OnlinePayments\OnlinePaymentsCluster;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages\EditOnlinePayment;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages\ListOnlinePayments;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Schemas\OnlinePaymentForm;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Tables\OnlinePaymentsTable;
use App\Modules\OnlinePayment\Models\OnlinePayment;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class OnlinePaymentResource extends Resource
{
protected static ?string $model = OnlinePayment::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static ?string $cluster = OnlinePaymentsCluster::class;
protected static ?string $recordTitleAttribute = 'orderId';
public static function form(Schema $schema): Schema
{
return OnlinePaymentForm::configure($schema);
}
public static function table(Table $table): Table
{
return OnlinePaymentsTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListOnlinePayments::route('/'),
'edit' => EditOnlinePayment::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\OnlinePaymentResource;
use Filament\Resources\Pages\CreateRecord;
class CreateOnlinePayment extends CreateRecord
{
protected static string $resource = OnlinePaymentResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\OnlinePaymentResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditOnlinePayment extends EditRecord
{
protected static string $resource = OnlinePaymentResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\OnlinePaymentResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListOnlinePayments extends ListRecords
{
protected static string $resource = OnlinePaymentResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,157 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Schemas;
use App\Modules\OnlinePayment\Repositories\OnlinePaymentRepository;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class OnlinePaymentForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->columnSpanFull()
->columns([
'default' => 1,
'md' => 2,
'xl' => 3,
])
->schema([
// Payment Information
Fieldset::make(__('Payment Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('amount')
->label(__('Amount'))
->required()
->numeric()
->suffix('TMT')
->rules(['required', 'numeric', 'min:0']),
TextInput::make('description')
->label(__('Description'))
->required()
->maxLength(255),
TextInput::make('orderNumber')
->label(__('Order Number'))
->maxLength(255)
->helperText(__('Leave empty to auto-generate')),
Select::make('paymentStatus')
->label(__('Payment Status'))
->options(OnlinePaymentRepository::statusValues())
->default(OnlinePaymentRepository::PENDING)
->required(),
]),
// Card Information
Fieldset::make(__('Card Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('cardholderName')
->label(__('Cardholder Name'))
->maxLength(255),
TextInput::make('pan')
->label(__('Card Number (PAN)'))
->maxLength(255)
->mask('9999 9999 9999 9999')
->placeholder('1234 5678 9012 3456'),
TextInput::make('expiration')
->label(__('Expiration Date'))
->mask('99/99')
->placeholder('MM/YY'),
TextInput::make('approvalCode')
->label(__('Approval Code'))
->maxLength(255),
]),
// Additional Information
Fieldset::make(__('Additional Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('booking_number')
->label(__('Booking Number'))
->maxLength(255),
TextInput::make('depositedAmount')
->label(__('Deposited Amount'))
->numeric()
->suffix('TMT'),
TextInput::make('refunded_amount')
->label(__('Refunded Amount'))
->numeric()
->suffix('TMT'),
TextInput::make('username')
->label(__('Username'))
->maxLength(255)
->helperText(__('Payment provider username')),
]),
// URLs
Fieldset::make(__('URLs'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('formUrl')
->label(__('Form URL'))
->url()
->maxLength(255),
TextInput::make('successUrl')
->label(__('Success URL'))
->url()
->maxLength(255),
TextInput::make('errorUrl')
->label(__('Error URL'))
->url()
->maxLength(255),
]),
// API Information
Fieldset::make(__('API Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('api_client')
->label(__('API Client'))
->maxLength(255),
TextInput::make('callbackStatus')
->label(__('Callback Status'))
->maxLength(255),
TextInput::make('orderId')
->label(__('Order ID'))
->maxLength(255)
->helperText(__('External order identifier')),
]),
]),
]);
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Tables;
use App\Modules\OnlinePayment\Repositories\OnlinePaymentRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class OnlinePaymentsTable
{
public static function configure(Table $table): Table
{
return $table
->defaultSort('created_at', direction: 'desc')
->columns([
TextColumn::make('id')
->label(__('ID'))
->sortable(),
TextColumn::make('orderNumber')
->label(__('Order Number'))
->searchable()
->sortable(),
TextColumn::make('amount')
->label(__('Amount'))
->money('TMT')
->sortable(),
TextColumn::make('description')
->label(__('Description'))
->limit(50),
TextColumn::make('paymentStatus')
->label(__('Payment Status'))
->badge()
->color(fn (string $state): string => match ($state) {
OnlinePaymentRepository::PAID => 'success',
OnlinePaymentRepository::FAILED => 'danger',
default => 'warning',
})
->formatStateUsing(fn (string $state): string => OnlinePaymentRepository::statusValues()[$state] ?? $state)
->sortable(),
TextColumn::make('cardholderName')
->label(__('Cardholder Name'))
->searchable()
->sortable(),
TextColumn::make('pan')
->label(__('Card Number'))
->formatStateUsing(fn (string $state): string => $state ? '**** **** **** '.substr($state, -4) : '-')
->searchable(),
TextColumn::make('orderId')
->label(__('Order ID'))
->searchable()
->sortable(),
TextColumn::make('username')
->label(__('Username'))
->searchable()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime()
->sortable()
->toggleable(),
TextColumn::make('updated_at')
->label(__('Updated At'))
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages\CreateCurrencyRate;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages\EditCurrencyRate;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages\ListCurrencyRates;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Schemas\CurrencyRateForm;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Tables\CurrencyRatesTable;
use App\Filament\Clusters\Settings\SettingsCluster;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class CurrencyRateResource extends Resource
{
protected static ?string $model = CurrencyRate::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCurrencyDollar;
protected static ?string $cluster = SettingsCluster::class;
protected static ?string $recordTitleAttribute = 'name';
public static function getNavigationGroup(): ?string
{
return __('Currencies');
}
public static function getModelLabel(): string
{
return __('Currency rate');
}
public static function getPluralModelLabel(): string
{
return __('Currency rates');
}
public static function form(Schema $schema): Schema
{
return CurrencyRateForm::configure($schema);
}
public static function table(Table $table): Table
{
return CurrencyRatesTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListCurrencyRates::route('/'),
'create' => CreateCurrencyRate::route('/create'),
'edit' => EditCurrencyRate::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\CurrencyRateResource;
use Filament\Resources\Pages\CreateRecord;
class CreateCurrencyRate extends CreateRecord
{
protected static string $resource = CurrencyRateResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\CurrencyRateResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditCurrencyRate extends EditRecord
{
protected static string $resource = CurrencyRateResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\CurrencyRateResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListCurrencyRates extends ListRecords
{
protected static string $resource = CurrencyRateResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Schemas;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class CurrencyRateForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Select::make('currency_from')
->label(__('Currency from'))
->native(false)
->searchable()
->options(CurrencyRate::currencies())
->rules('required')
->belowLabel('1 möçberi'),
Select::make('currency_to')
->label(__('Currency to'))
->native(false)
->searchable()
->options(CurrencyRate::currencies())
->rules('required')
->belowLabel('1 möçberi'),
TextInput::make('value')
->label(__('Currency value'))
->required()
->numeric()
->belowLabel('Bitin däl sanlary "." bilen ýazmaly'),
]);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class CurrencyRatesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('currency_from')
->label(__('Currency from'))
->searchable(),
TextColumn::make('currency_to')
->label(__('Currency to'))
->searchable(),
TextColumn::make('value')
->label(__('Currency value'))
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\VisaMasterSettingsResource;
use Filament\Resources\Pages\CreateRecord;
class CreateVisaMasterSettings extends CreateRecord
{
protected static string $resource = VisaMasterSettingsResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\VisaMasterSettingsResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditVisaMasterSettings extends EditRecord
{
protected static string $resource = VisaMasterSettingsResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\VisaMasterSettingsResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListVisaMasterSettings extends ListRecords
{
protected static string $resource = VisaMasterSettingsResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Schemas;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class VisaMasterSettingsForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Select::make('name')
->label(__('Type'))
->options(VisaMasterSettings::types())
->native(false)
->required(),
TextInput::make('display_name')
->label(__('Name'))
->required()
->string()
->maxLength(255)
->readonly(! user()->isMe()),
RichEditor::make('value')
->label(__('Content'))
->required()
->columnSpanFull(),
]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class VisaMasterSettingsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('display_name')
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages\CreateVisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages\EditVisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages\ListVisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Schemas\VisaMasterSettingsForm;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Tables\VisaMasterSettingsTable;
use App\Filament\Clusters\Settings\SettingsCluster;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class VisaMasterSettingsResource extends Resource
{
protected static ?string $model = VisaMasterSettings::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDocumentText;
protected static ?string $cluster = SettingsCluster::class;
protected static ?string $recordTitleAttribute = 'display_name';
public static function getNavigationGroup(): ?string
{
return __('Currencies');
}
public static function getModelLabel(): string
{
return __('Warning text');
}
public static function getPluralModelLabel(): string
{
return __('Warning text');
}
public static function form(Schema $schema): Schema
{
return VisaMasterSettingsForm::configure($schema);
}
public static function table(Table $table): Table
{
return VisaMasterSettingsTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListVisaMasterSettings::route('/'),
'create' => CreateVisaMasterSettings::route('/create'),
'edit' => EditVisaMasterSettings::route('/{record}/edit'),
];
}
}

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class SettingsCluster extends Cluster
{
protected static ?int $navigationSort = 100;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCog8Tooth;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::End;

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Filament\Clusters\Users;
use BackedEnum;
use Filament\Clusters\Cluster;
use Filament\Pages\Enums\SubNavigationPosition;
use Filament\Support\Icons\Heroicon;
class UsersCluster extends Cluster
{
protected static ?int $navigationSort = 6;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;
protected static string|BackedEnum|null $activeNavigationIcon = Heroicon::Users;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
public static function getNavigationLabel(): string
{
return __('Users');
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use Filament\Resources\Pages\CreateRecord;
class CreateVisaMasterPaymentOrder extends CreateRecord
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use App\Modules\VisaMasterPaymentOrder\Filament\Actions\PayVisaMasterPaymentAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Contracts\Support\Htmlable;
class EditVisaMasterPaymentOrder extends EditRecord
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
protected function getHeaderActions(): array
{
return [
PayVisaMasterPaymentAction::make(),
DeleteAction::make(),
ForceDeleteAction::make(),
RestoreAction::make(),
];
}
public function getTitle(): string|Htmlable
{
return __('Order details');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListVisaMasterPaymentOrders extends ListRecords
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas\VisaMasterPaymentOrderInfolist;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use Filament\Actions\EditAction;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
class ViewVisaMasterPaymentOrder extends ViewRecord
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
protected function getHeaderActions(): array
{
return [
EditAction::make()
->icon(Heroicon::OutlinedPencil),
];
}
public function infolist(Schema $schema): Schema
{
return VisaMasterPaymentOrderInfolist::configure($schema);
}
}

View File

@@ -0,0 +1,263 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas;
use App\Modules\FilamentPermission\Repositories\FilamentPermissionRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use App\Modules\Region\Repositories\RegionRepository;
use App\Modules\TurkmenPassport\Repositories\TurkmenPassportRepository;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\FusedGroup;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Wizard;
use Filament\Schemas\Components\Wizard\Step;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Facades\Auth;
use RalphJSmit\Filament\Upload\Filament\Forms\Components\AdvancedFileUpload;
class VisaMasterPaymentOrderForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Hidden::make('user_id')->default(Auth::id()),
Section::make()
->columnSpan(4)
->columns(4)
->disabled(fn (string $context): bool => FilamentPermissionRepository::forClients())
->components([
Select::make('status')
->label(__('Status'))
->options(OrderStatusRepository::statusValues())
->default(OrderStatusRepository::defaultStatus())
->native(false)
->columnSpan(2),
Toggle::make('paid')
->label(sprintf('%s (%s)', __('Paid'), __('This month')))
->inline(false)
->disabled(true)
->onIcon(Heroicon::CheckCircle)
->offIcon(Heroicon::XCircle)
->onColor('success')
->offColor('danger'),
RichEditor::make('notes')
->label(__('Bellik'))
->columnSpanFull(),
]),
Wizard::make([
Step::make(__('Order type and bank'))
->schema([
Fieldset::make(__('Order type'))
->schema([
Select::make('type')
->label(__('Type'))
->options(VisaMasterPaymentOrder::applicationTypes())
->native(false)
->required(),
]),
Fieldset::make(__('Location'))
->schema([
Select::make('region')
->label(__('Region'))
->options(RegionRepository::values())
->live()
->afterStateUpdated(fn (callable $set) => $set('branch_id', null))
->required(),
Select::make('branch_id')
->label(__('Branch'))
->relationship('branch', 'name', function ($query, callable $get) {
$query->orderByTranslation('name');
$region = $get('region');
if ($region) {
$query->where('region', $region);
}
})
->required(),
]),
]),
Step::make(__('Payment sender data'))
->columns(8)
->schema([
TextInput::make('sender_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(4)
->default(user()->fullName())
->required(),
FusedGroup::make([
Select::make('sender_passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1)
->default(user()->getOption('passport_serie')),
TextInput::make('sender_passport_number')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999')
->default(user()->getOption('passport_id')),
])
->columnSpan(2)
->label(__('Passport serie and number'))
->columns(2),
TextInput::make('phone')
->label(__('Phone'))
->required()
->mask('99 99 99 99')
->prefix('+993')
->rules([
new PhoneNumberVerificationRule,
])
->columnSpan(2)
->default(user()->phone),
TextInput::make('sender_deposit_account')
->label(__('Deposit account'))
->columnSpan(4)
->required(),
TextInput::make('address')
->label(__('Address'))
->columnSpan(4)
->required(),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
AdvancedFileUpload::make('sender_passport_local')
->spatieMediaLibrary(collection: 'sender_passport_local')
->multiple(),
SpatieMediaLibraryFileUpload::make('sender_passport_international')
->collection('sender_passport_international')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportynyň asyl görnüşi we göçürmesi')),
SpatieMediaLibraryFileUpload::make('sender_travel_stamp_on_passport')
->collection('sender_travel_stamp_on_passport')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary döwletine gidendigi we daşary döwlete barandygy baradaky (ştampyň) bellenen sahypasynyň göçürmesi'))
->required(),
SpatieMediaLibraryFileUpload::make('sender_proof_of_kinship')
->collection('sender_proof_of_kinship')
->label(__('Ugradyjynyň we kabul edijiniň (talybyň) özara garyndaşlyk derejesini tassyklaýjy resminamalarynyň göçürmesi')),
SpatieMediaLibraryFileUpload::make('sender_passport_local_old')
->collection('sender_passport_local_old')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alan bolsa, onda birinji gezek alan pasportynyň seriýasy baradaky maglumat')),
SpatieMediaLibraryFileUpload::make('sender_passport_local_old_replacement')
->collection('sender_passport_local_old_replacement')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alandan soňra birinji gezek alan pasportynyň seriýasy baradaky maglumaty bilmeýän ,bolsa onda polisiýanyň degişli edaralaryndan birinji alan pasportynyň seriýasy baradaky güwänamasy')),
]),
]),
Step::make(__('Payee information'))
->columns(8)
->schema([
TextInput::make('reciever_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(5)
->required(),
FusedGroup::make([
Select::make('reciever_passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1),
TextInput::make('reciever_passport_number')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999'),
])
->columnSpan(3)
->label(__('Passport serie and number'))
->columns(2),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
SpatieMediaLibraryFileUpload::make('receiver_requisite')
->collection('receiver_requisite')
->label(__('Talyba degişli walýuta "VISA" kartyň rekwizitleri'))
->maxSize(4096)
->columnSpan(1)
->required(),
SpatieMediaLibraryFileUpload::make('receiver_document_stating_he_is_studying')
->collection('receiver_document_stating_he_is_studying')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasy'))
->maxSize(4096)
->openable()
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_ticket')
->collection('receiver_ticket')
->label(__('Talybyň bilediniň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_passport_local')
->collection('receiver_passport_local')
->label(__('Talyba degişli Türkmenistanyň raýatynyň (içki milli) pasportynyň asyl görnüşi we göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_passport_international')
->collection('receiver_passport_international')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin (zagran) pasportynyň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_visa')
->collection('receiver_visa')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary ýurtda galyp okap bilýändigi baradaky berlen möhleti hereket edýän rugsatnamasynyň (wizasynyň) bellenen sahypasynyň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_travel_stamp_on_passport')
->collection('receiver_travel_stamp_on_passport')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky Türkmenistandan çykandygy we daşary ýurt döwletine girendigi baradaky ştamplaryň (seneli ştampyň) bellenen sahypasynyň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_document_stating_he_is_studying_2')
->collection('receiver_document_stating_he_is_studying_2')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasyndaky maglumatyň doly takyk däl ýagdaýynda takyk däl maglumatyň sebäpleri baradaky daşary ýurt döwletiniň ýokary okuw mekdebinden haty'))
->maxSize(4096)
->columnSpan(1),
]),
]),
])->columnSpanFull()->skippable(fn (string $context) => $context === 'edit'),
]);
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas;
use App\Filament\Infolists\Components\SpatieMediaLibraryFileEntry;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use App\Modules\TurkmenPassport\Repositories\TurkmenPassportRepository;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use Filament\Infolists\Components\IconEntry;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Str;
class VisaMasterPaymentOrderInfolist
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->columnSpanFull()
->columns(4)
->components([
TextEntry::make('unique_id')
->label(__('Order ID'))
->columnSpan(1)
->extraAttributes(['style' => 'text-transform:uppercase;font-size:1.5em;font-weight:bold;']),
TextEntry::make('type')
->label(__('Payment type'))
->extraAttributes(['style' => 'text-transform:uppercase;font-size:1.5em;font-weight:bold;']),
TextEntry::make('status')
->label(__('Status'))
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->extraAttributes(['style' => 'text-transform:uppercase;font-size:1.2em;font-weight:bold;'])
->color(OrderStatusRepository::statusColorMatching()),
IconEntry::make('paid')
->label(sprintf('%s (%s)', __('Paid'), __('This month')))
->boolean()
->trueIcon(Heroicon::CheckCircle)
->falseIcon(Heroicon::XCircle)
->trueColor('success')
->falseColor('danger'),
Fieldset::make(__('Location'))
->schema([
TextEntry::make('region')
->label(__('Region'))
->formatStateUsing(fn (string $state): string => RegionRepository::label($state)),
TextEntry::make('branch.name')
->label(__('Branch'))
->placeholder('-'),
])
->columnSpan(2),
TextEntry::make('notes')
->label(__('Bellik'))
->html()
->visible(fn (VisaMasterPaymentOrder $record): bool => ! is_null($record->notes) && $record->notes != '' && $record->notes != '<p></p>' && Str::length($record->notes) > 7)
->columnSpan(2)
->placeholder('-'),
]),
Tabs::make('Order Information')
->tabs([
Tab::make(__('Payment sender data'))
->columns(8)
->schema([
Fieldset::make(__('Data'))
->columnSpan(8)
->columns(8)
->schema([
TextEntry::make('sender_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(4)
->placeholder('-'),
TextEntry::make('sender_passport')
->label(__('Passport serie and number'))
->formatStateUsing(function ($record) {
$serie = TurkmenPassportRepository::values()[$record->sender_passport_serie] ?? $record->sender_passport_serie;
return $serie.' '.$record->sender_passport_number;
})
->columnSpan(2)
->placeholder('-'),
TextEntry::make('phone')
->label(__('Phone'))
->formatStateUsing(fn ($state) => '+993 '.$state)
->columnSpan(2)
->placeholder('-'),
TextEntry::make('sender_deposit_account')
->label(__('Deposit account'))
->columnSpan(4)
->placeholder('-'),
TextEntry::make('address')
->label(__('Address'))
->columnSpan(4)
->placeholder('-'),
]),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
SpatieMediaLibraryFileEntry::make('sender_passport_local')
->collection('sender_passport_local')
->label(__('Ugradyja degişli Türkmenistanyň raýatynyň (içki milli) pasportynyň asyl görnüşi we göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_passport_international')
->collection('sender_passport_international')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportynyň asyl görnüşi we göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_travel_stamp_on_passport')
->collection('sender_travel_stamp_on_passport')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary döwletine gidendigi we daşary döwlete barandygy baradaky (ştampyň) bellenen sahypasynyň göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_proof_of_kinship')
->collection('sender_proof_of_kinship')
->label(__('Ugradyjynyň we kabul edijiniň (talybyň) özara garyndaşlyk derejesini tassyklaýjy resminamalarynyň göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_passport_local_old')
->collection('sender_passport_local_old')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alan bolsa, onda birinji gezek alan pasportynyň seriýasy baradaky maglumat')),
SpatieMediaLibraryFileEntry::make('sender_passport_local_old_replacement')
->collection('sender_passport_local_old_replacement')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alandan soňra birinji gezek alan pasportynyň seriýasy baradaky maglumaty bilmeýän ,bolsa onda polisiýanyň degişli edaralaryndan birinji alan pasportynyň seriýasy baradaky güwänamasy')),
]),
]),
Tab::make(__('Payee information'))
->columns(8)
->schema([
TextEntry::make('reciever_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(5)
->placeholder('-'),
TextEntry::make('reciever_passport')
->label(__('Passport serie and number'))
->formatStateUsing(function ($record) {
$serie = TurkmenPassportRepository::values()[$record->reciever_passport_serie] ?? $record->reciever_passport_serie;
return $serie.' '.$record->reciever_passport_number;
})
->columnSpan(3)
->placeholder('-'),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
SpatieMediaLibraryFileEntry::make('receiver_requisite')
->collection('receiver_requisite')
->label(__('Talyba degişli walýuta "VISA" kartyň rekwizitleri'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_document_stating_he_is_studying')
->collection('receiver_document_stating_he_is_studying')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasy'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_ticket')
->collection('receiver_ticket')
->label(__('Talybyň bilediniň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_passport_local')
->collection('receiver_passport_local')
->label(__('Talyba degişli Türkmenistanyň raýatynyň (içki milli) pasportynyň asyl görnüşi we göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_passport_international')
->collection('receiver_passport_international')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin (zagran) pasportynyň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_visa')
->collection('receiver_visa')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary ýurtda galyp okap bilýändigi baradaky berlen möhleti hereket edýän rugsatnamasynyň (wizasynyň) bellenen sahypasynyň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_travel_stamp_on_passport')
->collection('receiver_travel_stamp_on_passport')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky Türkmenistandan çykandygy we daşary ýurt döwletine girendigi baradaky ştamplaryň (seneli ştampyň) bellenen sahypasynyň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_document_stating_he_is_studying_2')
->collection('receiver_document_stating_he_is_studying_2')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasyndaky maglumatyň doly takyk däl ýagdaýynda takyk däl maglumatyň sebäpleri baradaky daşary ýurt döwletiniň ýokary okuw mekdebinden haty'))
->columnSpan(1),
]),
]),
])->columnSpanFull(),
]);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Tables;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Actions\ViewAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
class VisaMasterPaymentOrdersTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', direction: 'desc')
->columns([
TextColumn::make('id')
->label(__('ID'))
->sortable(),
TextColumn::make('type')
->label(__('Type'))
->formatStateUsing(fn (string $state) => Str::upper($state)),
TextColumn::make('status')
->label(__('Status'))
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->badge(),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime()
->sortable(),
TextColumn::make('region')
->label(__('Region'))
->formatStateUsing(fn (string $state): string => RegionRepository::label($state))
->searchable(),
TextColumn::make('branch.name')
->label(__('Branch'))
->searchable(),
TextColumn::make('sender_full_name')
->label(__('Full Name'))
->searchable(),
TextColumn::make('phone')
->label(__('Phone'))
->searchable(),
IconColumn::make('paid')
->label(sprintf('%s (%s)', __('Paid'), __('This month')))
->boolean(),
])
->filters([
TrashedFilter::make(),
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
ForceDeleteBulkAction::make(),
RestoreBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\CreateVisaMasterPaymentOrder;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\EditVisaMasterPaymentOrder;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\ListVisaMasterPaymentOrders;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\ViewVisaMasterPaymentOrder;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas\VisaMasterPaymentOrderForm;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Tables\VisaMasterPaymentOrdersTable;
use App\Filament\Clusters\VisaMasterPayments\VisaMasterPaymentsCluster;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class VisaMasterPaymentOrderResource extends Resource
{
protected static ?int $navigationSort = 1;
protected static ?string $model = VisaMasterPaymentOrder::class;
protected static ?string $cluster = VisaMasterPaymentsCluster::class;
protected static ?string $recordTitleAttribute = 'unique_id';
protected static string|BackedEnum|null $navigationIcon = 'icon-visa-plain';
public static function getNavigationGroup(): ?string
{
return __('Visa/Master payments');
}
public static function getModelLabel(): string
{
return __('Visa/Master payment');
}
public static function getPluralModelLabel(): string
{
return __('Visa/Master payments');
}
public static function form(Schema $schema): Schema
{
return VisaMasterPaymentOrderForm::configure($schema);
}
public static function table(Table $table): Table
{
return VisaMasterPaymentOrdersTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListVisaMasterPaymentOrders::route('/'),
'create' => CreateVisaMasterPaymentOrder::route('/create'),
'view' => ViewVisaMasterPaymentOrder::route('/{record}/view'),
'edit' => EditVisaMasterPaymentOrder::route('/{record}/edit'),
];
}
/**
* Get the query for the record route binding
*
* @return \Illuminate\Database\Eloquent\Builder<\App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder>
*/
public static function getRecordRouteBindingEloquentQuery(): Builder
{
return parent::getRecordRouteBindingEloquentQuery()
->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments;
use BackedEnum;
use Filament\Clusters\Cluster;
use Filament\Pages\Enums\SubNavigationPosition;
class VisaMasterPaymentsCluster extends Cluster
{
protected static ?int $navigationSort = 3;
protected static string|BackedEnum|null $navigationIcon = 'icon-visa-plain';
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
public static function getNavigationLabel(): string
{
return __('International payments');
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace App\Filament\Infolists\Components;
use Closure;
use Filament\Actions\Action;
use Filament\Infolists\Components\Entry;
use Filament\Support\Concerns\HasMediaFilter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Spatie\MediaLibrary\MediaCollections\Models\Collections\MediaCollection;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Throwable;
class SpatieMediaLibraryFileEntry extends Entry
{
use HasMediaFilter;
public function getViewMediaActionName(): string
{
return 'view_media_'.$this->getName();
}
protected function setUp(): void
{
parent::setUp();
$this->registerActions([
Action::make($this->getViewMediaActionName())
->label(__('Watch Full'))
->modalContent(fn (array $arguments) => view('filament.infolists.components.image-modal', ['url' => $arguments['url']]))
->modalSubmitAction(false)
->modalCancelAction(false)
->modalWidth('5xl'),
]);
}
protected string $view = 'filament.infolists.components.spatie-media-library-file-entry';
protected string|Closure|null $collection = null;
protected string|Closure|null $conversion = null;
protected string|Closure $visibility = 'private';
protected bool|Closure $isDownloadable = true;
protected bool|Closure $isPreviewable = true;
public function collection(string|Closure|null $collection): static
{
$this->collection = $collection;
return $this;
}
public function conversion(string|Closure|null $conversion): static
{
$this->conversion = $conversion;
return $this;
}
public function visibility(string|Closure $visibility): static
{
$this->visibility = $visibility;
return $this;
}
public function downloadable(bool|Closure $condition = true): static
{
$this->isDownloadable = $condition;
return $this;
}
public function previewable(bool|Closure $condition = true): static
{
$this->isPreviewable = $condition;
return $this;
}
public function getCollection(): ?string
{
return $this->evaluate($this->collection);
}
public function getConversion(): ?string
{
return $this->evaluate($this->conversion);
}
public function getVisibility(): string
{
return (string) $this->evaluate($this->visibility);
}
public function isDownloadable(): bool
{
return (bool) $this->evaluate($this->isDownloadable);
}
public function isPreviewable(): bool
{
return (bool) $this->evaluate($this->isPreviewable);
}
public function getMediaUrl(Media $media, ?string $conversion = null): string
{
if ($this->getVisibility() === 'private') {
try {
return $media->getTemporaryUrl(
now()->addMinutes(30)->endOfHour(),
$conversion ?? '',
);
} catch (Throwable $exception) {
// This driver does not support creating temporary URLs.
}
}
return $media->getUrl($conversion ?? '');
}
/**
* Get the media for the entry
*
* @return Collection<array-key, Media>
*/
public function getMedia(): Collection
{
$record = $this->getRecord();
if (! $record) {
return collect([]);
}
if ($this->hasStateRelationship($record)) {
$record = $this->getStateRelationshipResults($record);
}
$records = Arr::wrap($record);
$allMedia = collect([]);
$collection = $this->getCollection() ?? 'default';
foreach ($records as $record) {
/** @var Model $record */
$media = $record->getRelationValue('media');
if (! $media) {
continue;
}
$filteredMedia = $media
->when(
is_string($collection),
fn (MediaCollection $mediaCollection) => $mediaCollection->filter(fn (Media $media): bool => $media->getAttributeValue('collection_name') === $collection),
)
->when(
$this->hasMediaFilter(),
fn (Collection $media) => $this->filterMedia($media)
)
->sortBy('order_column');
$allMedia = $allMedia->merge($filteredMedia);
}
return $allMedia;
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\Users\Pages;
use App\Filament\Resources\Users\UserResource;
use Filament\Resources\Pages\CreateRecord;
class CreateUser extends CreateRecord
{
protected static string $resource = UserResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Users\Pages;
use App\Filament\Resources\Users\UserResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditUser extends EditRecord
{
protected static string $resource = UserResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Users\Pages;
use App\Filament\Resources\Users\UserResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListUsers extends ListRecords
{
protected static string $resource = UserResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Filament\Resources\Users\Schemas;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Hash;
class UserForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->columns(6)
->components([
TextInput::make('name')
->label(__('Full Name'))
->required()
->columnSpan(3),
TextInput::make('username')
->label(__('Username'))
->required()
->unique(ignoreRecord: true)
->columnSpan(3),
TextInput::make('phone')
->label(__('Phone'))
->unique(ignoreRecord: true)
->mask('99 99 99 99')
->prefix('+993')
->dehydrateStateUsing(fn ($state) => unMaskTurkmenNumber($state))
->rules([
new PhoneNumberVerificationRule,
])
->columnSpan(2),
TextInput::make('email')
->label(__('Email'))
->email()
->unique(ignoreRecord: true)
->columnSpan(2),
TextInput::make('password')
->label(__('Password'))
->password()
->dehydrateStateUsing(fn ($state) => Hash::make($state))
->dehydrated(fn ($state) => filled($state))
->required(fn (string $context): bool => $context === 'create')
->columnSpan(2),
Select::make('roles')
->label(__('Roles'))
->relationship('roles', 'name')
->multiple()
->preload()
->native(false)
->columnSpan(3),
Select::make('branches')
->label(__('Branches'))
->relationship('branches', 'name', fn (Builder $query) => $query->distinct('id')->orderBy('id'))
->multiple()
->preload()
->native(false)
->columnSpan(3),
]);
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Filament\Resources\Users\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class UsersTable
{
public static function configure(Table $table): Table
{
return $table
->defaultSort('created_at', direction: 'desc')
->columns([
TextColumn::make('id'),
TextColumn::make('first_name')
->label(__('First name'))
->searchable()
->sortable(),
TextColumn::make('last_name')
->label(__('Last name'))
->searchable()
->sortable(),
TextColumn::make('username')
->label(__('Username'))
->searchable()
->sortable(),
TextColumn::make('phone')
->label(__('Phone number'))
->searchable()
->sortable(),
TextColumn::make('email')
->label(__('Email'))
->searchable()
->sortable(),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace App\Filament\Resources\Users;
use App\Filament\Clusters\Users\UsersCluster;
use App\Filament\Resources\Users\Pages\CreateUser;
use App\Filament\Resources\Users\Pages\EditUser;
use App\Filament\Resources\Users\Pages\ListUsers;
use App\Filament\Resources\Users\Schemas\UserForm;
use App\Filament\Resources\Users\Tables\UsersTable;
use App\Models\User;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class UserResource extends Resource
{
protected static ?string $model = User::class;
protected static ?string $cluster = UsersCluster::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;
protected static string|BackedEnum|null $activeNavigationIcon = Heroicon::Users;
protected static ?string $recordTitleAttribute = 'phone';
public static function getNavigationLabel(): string
{
return __('Users');
}
public static function getModelLabel(): string
{
return __('User');
}
public static function getPluralModelLabel(): string
{
return __('Users');
}
public static function form(Schema $schema): Schema
{
return UserForm::configure($schema);
}
public static function table(Table $table): Table
{
return UsersTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListUsers::route('/'),
'create' => CreateUser::route('/create'),
'edit' => EditUser::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Database\Seeders\Migrators\ActionEventsMigrator;
use Database\Seeders\Migrators\BranchUserMigrator;
use Database\Seeders\Migrators\CurrencyRatesMigrator;
use Database\Seeders\Migrators\LoanOrderRequiredDocsMigrator;
use Database\Seeders\Migrators\LoanOrdersMigrator;
use Database\Seeders\Migrators\LoanTypesMigrator;
use Database\Seeders\Migrators\MediaMigrator;
use Database\Seeders\Migrators\ProvincesMigrator;
use Database\Seeders\Migrators\BranchesMigrator;
use Database\Seeders\Migrators\UsersMigrator;
use Database\Seeders\Migrators\CardStatesMigrator;
use Database\Seeders\Migrators\CardTypesMigrator;
use Database\Seeders\Migrators\VisaMasterPaymentOrdersMigrator;
use Database\Seeders\Migrators\VerificationsMigrator;
use Database\Seeders\Migrators\PersonalAccessTokensMigrator;
use Database\Seeders\Migrators\CardOrdersMigrator;
use Database\Seeders\Migrators\VisaMasterSettingsMigrator;
use Database\Seeders\Migrators\CardPinOrdersMigrator;
use Database\Seeders\Migrators\ModelHasRolesMigrator;
use Database\Seeders\Migrators\OnlinePaymentsMigrator;
class MigrationController extends Controller
{
public function index()
{
return $this->test();
$migrators = [
new ActionEventsMigrator(),
new UsersMigrator(),
new ProvincesMigrator(),
new BranchesMigrator(),
new BranchUserMigrator(),
new CardStatesMigrator(),
new CardTypesMigrator(),
new VerificationsMigrator(),
new CurrencyRatesMigrator(),
new LoanOrderRequiredDocsMigrator(),
new PersonalAccessTokensMigrator(),
new LoanTypesMigrator(),
new CardOrdersMigrator(),
new CardPinOrdersMigrator(),
new LoanOrdersMigrator(),
new ModelHasRolesMigrator(),
new VisaMasterPaymentOrdersMigrator(),
new OnlinePaymentsMigrator(),
new MediaMigrator(),
];
foreach ($migrators as $migrator) {
$migrator->migrate();
}
return 'done';
}
public function test()
{
(new OnlinePaymentsMigrator())->migrate();
return 'done';
}
}

View File

@@ -20,6 +20,11 @@ class EnsureProfileIsFilled
/** @var \App\Models\User */
$user = $request->user();
// Skip if user is system user...
if ($user->isSystemUser()) {
return $next($request);
}
// 1. If user is not logged in, or profile is already complete, do nothing.
// (Based on your logic: must_fill_profile == true means complete)
if (! $user->must_fill_profile) {

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// if user does not have any role, add role "client"
/** @var \App\Models\User */
$user = $request->user();
if ($user->roles->count() == 0) {
$user->assignRole('client');
}
return $next($request);
}
}

View File

@@ -4,11 +4,13 @@ namespace App\Models;
use App\Modules\UserAdjustments\Traits\UserAdjustments;
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasAvatar;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Date;
use Laravel\Sanctum\HasApiTokens;
/**
* @property int $id
@@ -20,13 +22,14 @@ use Illuminate\Support\Facades\Date;
* @property Date|null $created_at
* @property Date|null $updated_at
*/
class User extends Authenticatable implements FilamentUser
class User extends Authenticatable implements FilamentUser, HasAvatar
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory;
use Notifiable;
use UserAdjustments;
use HasApiTokens;
/**
* The attributes that are mass assignable.
@@ -56,4 +59,12 @@ class User extends Authenticatable implements FilamentUser
{
return true;
}
/**
* Get the avatar URL for the user.
*/
public function getFilamentAvatarUrl(): ?string
{
return '/assets/images/avatar.png';
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Modules\ActivityLog;
use App\Modules\Makeable;
use App\Modules\ModuleContract;
class ActivityLogModule implements ModuleContract
{
use Makeable;
/**
* Module is enabled
*/
protected bool $enabled = true;
/**
* Check if is module enabled
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Disable module
*/
public function disable(): void
{
$this->enabled = false;
}
/**
* Enable module
*/
public function enable(): void
{
$this->enabled = true;
}
/**
* Check if module has a filament resource
*/
public function hasFilamentResource(): bool
{
return false;
}
/**
* Get module composer requirements
*/
public function getComposerRequirements(): array
{
return [];
}
/**
* Get module composer suggestions
*/
public function getComposerSuggestions(): array
{
return [];
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace App\Modules\ActivityLog\Repositories;
class ActivityLogRepository {}

View File

@@ -2,7 +2,7 @@
namespace App\Modules\AppHelpers\Contracts;
interface HasOnlinePaymentStatusFields
interface HasFailedMethod
{
public function failed(): bool;

View File

@@ -14,6 +14,7 @@ use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
class LoginController extends Controller
{
@@ -113,7 +114,7 @@ class LoginController extends Controller
protected function validateLogin(Request $request): void
{
$request->validate([
$this->username() => ['required', 'string', 'max:250'],
$this->username() => ['required', 'string', 'max:250', Rule::notIn('65999990', 'ulanyjy_ady')],
'password' => ['required', 'string', 'max:250'],
]);
}
@@ -159,6 +160,10 @@ class LoginController extends Controller
$request->session()->put('auth.password_confirmed_at', time());
}
if ($user->roles->count() == 0) {
$user->assignRole('client');
}
return $this->sendLoginResponse($request);
}

View File

@@ -110,6 +110,8 @@ class RegisterController extends Controller
'must_fill_profile' => true,
]);
$user->assignRole('client');
return $user;
}

View File

@@ -16,8 +16,18 @@ class RedirectIfUserPhoneIsUnVerfied
*/
public function handle(Request $request, Closure $next): Response
{
if (Auth::check() && is_null($request->user()?->phone_verified_at)) {
return redirect()->route('sms-verification');
if (Auth::check()) {
/** @var \App\Models\User */
$user = $request->user();
// Skip if user is system user...
if ($user->isSystemUser()) {
return $next($request);
}
if (is_null($user->phone_verified_at)) {
return redirect()->route('sms-verification');
}
}
return $next($request);

View File

@@ -16,8 +16,13 @@ class RedirectIfUserPhoneIsVerfied
*/
public function handle(Request $request, Closure $next): Response
{
if (Auth::check() && ! is_null($request->user()?->phone_verified_at)) {
return redirect()->route(config()->string('module.base-auth.redirect_path'));
if (Auth::check()) {
/** @var \App\Models\User */
$user = $request->user();
if (! is_null($user->phone_verified_at) || $user->isSystemUser()) {
return redirect(config()->string('module.base-auth.redirect_path'));
}
}
return $next($request);

View File

@@ -30,4 +30,5 @@ return [
'go_back' => 'Yza',
'successfully_changed_phone' => 'Telefon belgiňiz üýtgedildi',
'resend' => 'Täze tassyklaýyş belgi ugratmak',
'phone_or_username_placeholder' => 'meselem: 65999990 yada ulanyjy_ady',
];

View File

@@ -49,11 +49,12 @@ async function login(event) {
id="username"
type="text"
name="username"
placeholder="+99365999990 {{ __('or') }} {{ __('module.base-auth::base.username') }}"
placeholder="65999990 {{ __('or') }} {{ __('module.base-auth::base.username') }}"
autofocus=""
value="{{ old('username') }}"
>
<span class="text-gray-500 text-xs">{{ __('module.base-auth::base.phone_or_username_placeholder') }}</span>
<span id="username-error-box" class="text-red-500 text-italic error-box"></span>
</div>

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Modules\Branch\Models;
use App\Models\User;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserBranch extends Pivot
{
public $incrementing = true;
protected $table = 'branch_user';
/**
* Branch
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Branch, $this>
*/
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class);
}
/**
* User
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<User, $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\Branch\Policies;
use App\Modules\Branch\Models\Branch;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class BranchPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Branch');
}
public function view(AuthUser $authUser, Branch $branch): bool
{
return $authUser->can('View:Branch');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Branch');
}
public function update(AuthUser $authUser, Branch $branch): bool
{
return $authUser->can('Update:Branch');
}
public function delete(AuthUser $authUser, Branch $branch): bool
{
return $authUser->can('Delete:Branch');
}
public function restore(AuthUser $authUser, Branch $branch): bool
{
return $authUser->can('Restore:Branch');
}
public function forceDelete(AuthUser $authUser, Branch $branch): bool
{
return $authUser->can('ForceDelete:Branch');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Branch');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Branch');
}
public function replicate(AuthUser $authUser, Branch $branch): bool
{
return $authUser->can('Replicate:Branch');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Branch');
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\Card\Policies;
use App\Modules\Card\Models\Card;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CardPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Card');
}
public function view(AuthUser $authUser, Card $card): bool
{
return $authUser->can('View:Card');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Card');
}
public function update(AuthUser $authUser, Card $card): bool
{
return $authUser->can('Update:Card');
}
public function delete(AuthUser $authUser, Card $card): bool
{
return $authUser->can('Delete:Card');
}
public function restore(AuthUser $authUser, Card $card): bool
{
return $authUser->can('Restore:Card');
}
public function forceDelete(AuthUser $authUser, Card $card): bool
{
return $authUser->can('ForceDelete:Card');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Card');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Card');
}
public function replicate(AuthUser $authUser, Card $card): bool
{
return $authUser->can('Replicate:Card');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Card');
}
}

View File

@@ -53,7 +53,7 @@ return new class extends Migration
$table->text('notes')->nullable();
$table->foreignId('user_id')->constrained('users')->restrictOnDelete();
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
$table->boolean('paid')->default(false);

View File

@@ -109,7 +109,16 @@ class CardOrder extends Model implements BelongsToBranch, HasStatus
parent::boot();
static::creating(LoanOrderRepository::creating());
static::created(LoanOrderRepository::created());
static::created(function ($model) {
$uniqueId = LoanOrderRepository::generateUniqueId($model);
$model->update(['unique_id' => $uniqueId]);
sendSMS(
$model->phone,
__('module.card-order::base.card_order_created', ['order_id' => $uniqueId])
);
});
}
/**

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\CardOrder\Policies;
use App\Modules\CardOrder\Models\CardOrder;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CardOrderPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CardOrder');
}
public function view(AuthUser $authUser, CardOrder $cardOrder): bool
{
return $authUser->can('View:CardOrder');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CardOrder');
}
public function update(AuthUser $authUser, CardOrder $cardOrder): bool
{
return $authUser->can('Update:CardOrder');
}
public function delete(AuthUser $authUser, CardOrder $cardOrder): bool
{
return $authUser->can('Delete:CardOrder');
}
public function restore(AuthUser $authUser, CardOrder $cardOrder): bool
{
return $authUser->can('Restore:CardOrder');
}
public function forceDelete(AuthUser $authUser, CardOrder $cardOrder): bool
{
return $authUser->can('ForceDelete:CardOrder');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CardOrder');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CardOrder');
}
public function replicate(AuthUser $authUser, CardOrder $cardOrder): bool
{
return $authUser->can('Replicate:CardOrder');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CardOrder');
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\CardOrder\Policies;
use App\Modules\CardOrder\Models\CardState;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CardStatePolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CardState');
}
public function view(AuthUser $authUser, CardState $cardState): bool
{
return $authUser->can('View:CardState');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CardState');
}
public function update(AuthUser $authUser, CardState $cardState): bool
{
return $authUser->can('Update:CardState');
}
public function delete(AuthUser $authUser, CardState $cardState): bool
{
return $authUser->can('Delete:CardState');
}
public function restore(AuthUser $authUser, CardState $cardState): bool
{
return $authUser->can('Restore:CardState');
}
public function forceDelete(AuthUser $authUser, CardState $cardState): bool
{
return $authUser->can('ForceDelete:CardState');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CardState');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CardState');
}
public function replicate(AuthUser $authUser, CardState $cardState): bool
{
return $authUser->can('Replicate:CardState');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CardState');
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\CardOrder\Policies;
use App\Modules\CardOrder\Models\CardType;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CardTypePolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CardType');
}
public function view(AuthUser $authUser, CardType $cardType): bool
{
return $authUser->can('View:CardType');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CardType');
}
public function update(AuthUser $authUser, CardType $cardType): bool
{
return $authUser->can('Update:CardType');
}
public function delete(AuthUser $authUser, CardType $cardType): bool
{
return $authUser->can('Delete:CardType');
}
public function restore(AuthUser $authUser, CardType $cardType): bool
{
return $authUser->can('Restore:CardType');
}
public function forceDelete(AuthUser $authUser, CardType $cardType): bool
{
return $authUser->can('ForceDelete:CardType');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CardType');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CardType');
}
public function replicate(AuthUser $authUser, CardType $cardType): bool
{
return $authUser->can('Replicate:CardType');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CardType');
}
}

View File

@@ -19,8 +19,8 @@ class CardOrderRepository
/** @var \App\Modules\Branch\Models\Branch */
$branch = $record->branch;
return OnlinePaymentRepository::make(relatedModel: $record)
->paymentProvider(
return OnlinePaymentRepository::make(relatedModel: $record, apiClient: 'billing')
->setPaymentProvider(
HalkbankOnlinePaymentRepository::make()
->setUsername($branch->billingUsername())
->setPassword($branch->billingPassword())

View File

@@ -0,0 +1,5 @@
<?php
return [
'card_order_created' => 'Kart sargydyňyz :order_id belgi bilen hasaba alyndy. Sargydy tassyklamak üçin operatorymyz habarlaşar.',
];

View File

@@ -3,15 +3,18 @@
namespace App\Modules\CardPinOrder\Models;
use App\Models\User;
use App\Modules\Branch\Interfaces\BelongsToBranch;
use App\Modules\Branch\Models\Branch;
use App\Modules\CardOrder\Models\CardType;
use App\Modules\LoanOrder\Repositories\LoanOrderRepository;
use App\Modules\OrderStatus\Interfaces\HasStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @param int $id
*
* @property string unique_id
* @property string $unique_id
* @property int $user_id
* @property int $card_type_id
* @property string $card_number
@@ -20,20 +23,20 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property string $customer_name
* @property string $customer_surname
* @property string $customer_patronic_name
* @property date $born_at
* @property \Illuminate\Support\Carbon $born_at
* @property string $phone
* @property string $passport_serie
* @property string $passport_id
* @property text $passport_one
* @property text $passport_two
* @property text $passport_three
* @property text $passport_four
* @property string $passport_one
* @property string $passport_two
* @property string $passport_three
* @property string $passport_four
* @property string $status
* @property null|string $notes
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
*/
class CardPinOrder extends Model
class CardPinOrder extends Model implements BelongsToBranch, HasStatus
{
/**
* The attributes that should be cast.
@@ -73,4 +76,15 @@ class CardPinOrder extends Model
{
return $this->belongsTo(Branch::class);
}
/**
* "boot" method for model
*/
protected static function boot(): void
{
parent::boot();
static::creating(LoanOrderRepository::creating());
static::created(LoanOrderRepository::created());
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\CardPinOrder\Policies;
use App\Modules\CardPinOrder\Models\CardPinOrder;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CardPinOrderPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CardPinOrder');
}
public function view(AuthUser $authUser, CardPinOrder $cardPinOrder): bool
{
return $authUser->can('View:CardPinOrder');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CardPinOrder');
}
public function update(AuthUser $authUser, CardPinOrder $cardPinOrder): bool
{
return $authUser->can('Update:CardPinOrder');
}
public function delete(AuthUser $authUser, CardPinOrder $cardPinOrder): bool
{
return $authUser->can('Delete:CardPinOrder');
}
public function restore(AuthUser $authUser, CardPinOrder $cardPinOrder): bool
{
return $authUser->can('Restore:CardPinOrder');
}
public function forceDelete(AuthUser $authUser, CardPinOrder $cardPinOrder): bool
{
return $authUser->can('ForceDelete:CardPinOrder');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CardPinOrder');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CardPinOrder');
}
public function replicate(AuthUser $authUser, CardPinOrder $cardPinOrder): bool
{
return $authUser->can('Replicate:CardPinOrder');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CardPinOrder');
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace App\Modules\CurrencyRate\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class CurrencyRateController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): void
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): void
{
//
}
/**
* Display the specified resource.
*/
public function show(Request $request): void
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request): void
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Request $request): void
{
//
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Modules\CurrencyRate;
use App\Modules\Makeable;
use App\Modules\ModuleContract;
class CurrencyRateModule implements ModuleContract
{
use Makeable;
/**
* Module is enabled
*/
protected bool $enabled = true;
/**
* Check if is module enabled
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Disable module
*/
public function disable(): void
{
$this->enabled = false;
}
/**
* Enable module
*/
public function enable(): void
{
$this->enabled = true;
}
/**
* Check if module has a filament resource
*/
public function hasFilamentResource(): bool
{
return false;
}
/**
* Get module composer requirements
*/
public function getComposerRequirements(): array
{
return [];
}
/**
* Get module composer suggestions
*/
public function getComposerSuggestions(): array
{
return [];
}
}

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('currency_rates', function (Blueprint $table) {
$table->id();
$table->string('currency_from');
$table->string('currency_to');
$table->string('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('currency_rates');
}
};

View File

@@ -0,0 +1,217 @@
<?php
namespace App\Modules\CurrencyRate\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $currency_from
* @property string $currency_to
* @property string $value
* @property \Illuminate\Support\Carbon $created_at
* @property \Illuminate\Support\Carbon $updated_at
*/
class CurrencyRate extends Model
{
/**
* The table associated with the model.
*/
protected $table = 'currency_rates';
/**
* Get the user's first name.
*
* @return Attribute<string, void>
*/
protected function name(): Attribute
{
return Attribute::make(
get: fn (): string => $this->currency_from.'-'.$this->currency_to,
);
}
/**
* Currencies
*
* @return array<string, string>
*/
public static function currencies(): array
{
return [
'AED' => 'AED',
'AFN' => 'AFN',
'ALL' => 'ALL',
'AMD' => 'AMD',
'ANG' => 'ANG',
'AOA' => 'AOA',
'ARS' => 'ARS',
'AUD' => 'AUD',
'AWG' => 'AWG',
'AZN' => 'AZN',
'BAM' => 'BAM',
'BBD' => 'BBD',
'BDT' => 'BDT',
'BGN' => 'BGN',
'BHD' => 'BHD',
'BIF' => 'BIF',
'BMD' => 'BMD',
'BND' => 'BND',
'BOB' => 'BOB',
'BRL' => 'BRL',
'BSD' => 'BSD',
'BTN' => 'BTN',
'BWP' => 'BWP',
'BYN' => 'BYN',
'BZD' => 'BZD',
'CAD' => 'CAD',
'CDF' => 'CDF',
'CHF' => 'CHF',
'CKD' => 'CKD',
'CLP' => 'CLP',
'CNY' => 'CNY',
'COP' => 'COP',
'CRC' => 'CRC',
'CUC' => 'CUC',
'CUP' => 'CUP',
'CVE' => 'CVE',
'CZK' => 'CZK',
'DJF' => 'DJF',
'DKK' => 'DKK',
'DOP' => 'DOP',
'DZD' => 'DZD',
'EGP' => 'EGP',
'EHP' => 'EHP',
'ERN' => 'ERN',
'ETB' => 'ETB',
'EUR' => 'EUR',
'FJD' => 'FJD',
'FKP' => 'FKP',
'FOK' => 'FOK',
'GBP' => 'GBP',
'GEL' => 'GEL',
'GGP' => 'GGP',
'GHS' => 'GHS',
'GIP' => 'GIP',
'GMD' => 'GMD',
'GNF' => 'GNF',
'GTQ' => 'GTQ',
'GYD' => 'GYD',
'HKD' => 'HKD',
'HNL' => 'HNL',
'HRK' => 'HRK',
'HTG' => 'HTG',
'HUF' => 'HUF',
'IDR' => 'IDR',
'ILS' => 'ILS',
'IMP' => 'IMP',
'INR' => 'INR',
'IQD' => 'IQD',
'IRR' => 'IRR',
'ISK' => 'ISK',
'JEP' => 'JEP',
'JMD' => 'JMD',
'JOD' => 'JOD',
'JPY' => 'JPY',
'KES' => 'KES',
'KGS' => 'KGS',
'KHR' => 'KHR',
'KID' => 'KID',
'KMF' => 'KMF',
'KPW' => 'KPW',
'KRW' => 'KRW',
'KWD' => 'KWD',
'KYD' => 'KYD',
'KZT' => 'KZT',
'LAK' => 'LAK',
'LBP' => 'LBP',
'LKR' => 'LKR',
'LRD' => 'LRD',
'LSL' => 'LSL',
'LYD' => 'LYD',
'MAD' => 'MAD',
'MDL' => 'MDL',
'MGA' => 'MGA',
'MKD' => 'MKD',
'MMK' => 'MMK',
'MNT' => 'MNT',
'MOP' => 'MOP',
'MRU' => 'MRU',
'MUR' => 'MUR',
'MVR' => 'MVR',
'MWK' => 'MWK',
'MXN' => 'MXN',
'MYR' => 'MYR',
'MZN' => 'MZN',
'NAD' => 'NAD',
'NGN' => 'NGN',
'NIO' => 'NIO',
'NOK' => 'NOK',
'NPR' => 'NPR',
'NZD' => 'NZD',
'OMR' => 'OMR',
'PAB' => 'PAB',
'PEN' => 'PEN',
'PGK' => 'PGK',
'PHP' => 'PHP',
'PKR' => 'PKR',
'PLN' => 'PLN',
'PND' => 'PND',
'PRB' => 'PRB',
'PYG' => 'PYG',
'QAR' => 'QAR',
'RON' => 'RON',
'RSD' => 'RSD',
'RUB' => 'RUB',
'RWF' => 'RWF',
'SAR' => 'SAR',
'SBD' => 'SBD',
'SCR' => 'SCR',
'SDG' => 'SDG',
'SEK' => 'SEK',
'SGD' => 'SGD',
'SHP' => 'SHP',
'SLL' => 'SLL',
'SLS' => 'SLS',
'SOS' => 'SOS',
'SRD' => 'SRD',
'SSP' => 'SSP',
'STN' => 'STN',
'SVC' => 'SVC',
'SYP' => 'SYP',
'SZL' => 'SZL',
'THB' => 'THB',
'TJS' => 'TJS',
'TMT' => 'TMT',
'TND' => 'TND',
'TOP' => 'TOP',
'TRY' => 'TRY',
'TTD' => 'TTD',
'TVD' => 'TVD',
'TWD' => 'TWD',
'TZS' => 'TZS',
'UAH' => 'UAH',
'UGX' => 'UGX',
'USD' => 'USD',
'UYU' => 'UYU',
'UZS' => 'UZS',
'VED' => 'VED',
'VES' => 'VES',
'VND' => 'VND',
'VUV' => 'VUV',
'WST' => 'WST',
'XAF' => 'XAF',
'XCD' => 'XCD',
'XOF' => 'XOF',
'XPF' => 'XPF',
'YER' => 'YER',
'ZAR' => 'ZAR',
'ZMW' => 'ZMW',
'ZWB' => 'ZWB',
'ZWL' => 'ZWL',
'Abkhazia' => 'Abkhazia',
'Artsakh' => 'Artsakh',
];
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\CurrencyRate\Policies;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CurrencyRatePolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CurrencyRate');
}
public function view(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('View:CurrencyRate');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CurrencyRate');
}
public function update(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Update:CurrencyRate');
}
public function delete(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Delete:CurrencyRate');
}
public function restore(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Restore:CurrencyRate');
}
public function forceDelete(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('ForceDelete:CurrencyRate');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CurrencyRate');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CurrencyRate');
}
public function replicate(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Replicate:CurrencyRate');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CurrencyRate');
}
}

Some files were not shown because too many files have changed in this diff Show More