diff --git a/.env b/.env index fda3586c51..a86f1447f6 100644 --- a/.env +++ b/.env @@ -46,3 +46,4 @@ MAILER_DSN=null://null ###< symfony/mailer ### SYLIUS_PAYMENT_ENCRYPTION_KEY_PATH=%kernel.project_dir%/config/encryption/dev.key +TEST_SYLIUS_USE_LEGACY_GRID_CONFIG=0 diff --git a/.github/workflows/ci_e2e-mariadb.yaml b/.github/workflows/ci_e2e-mariadb.yaml index d0e72bc8c5..a21b41069d 100644 --- a/.github/workflows/ci_e2e-mariadb.yaml +++ b/.github/workflows/ci_e2e-mariadb.yaml @@ -44,7 +44,7 @@ jobs: phpunit-cli-api: needs: get-matrix runs-on: ubuntu-latest - name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}" + name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}${{ matrix.legacy_grid_config && format(', LegacyGridConfig {0}', matrix.legacy_grid_config) || '' }}" timeout-minutes: 20 strategy: fail-fast: false @@ -53,6 +53,7 @@ jobs: env: APP_ENV: test_cached TEST_SYLIUS_STATE_MACHINE_ADAPTER: "${{ matrix.state_machine_adapter }}" + TEST_SYLIUS_USE_LEGACY_GRID_CONFIG: ${{ matrix.legacy_grid_config || true }} BEHAT_BASE_CMD: "vendor/bin/behat --colors --strict --no-interaction -vvv -f progress" BEHAT_RERUN_CMD: "vendor/bin/behat --colors --strict --no-interaction -vvv -f pretty --rerun" @@ -153,7 +154,7 @@ jobs: behat-ui: needs: get-matrix runs-on: ubuntu-latest - name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}" + name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}${{ matrix.legacy_grid_config && format(', LegacyGridConfig {0}', matrix.legacy_grid_config) || '' }}" timeout-minutes: 20 strategy: fail-fast: false @@ -162,6 +163,7 @@ jobs: env: APP_ENV: test_cached TEST_SYLIUS_STATE_MACHINE_ADAPTER: "${{ matrix.state_machine_adapter }}" + TEST_SYLIUS_USE_LEGACY_GRID_CONFIG: ${{ matrix.legacy_grid_config || true }} BEHAT_BASE_CMD: "vendor/bin/behat --colors --strict --no-interaction -vvv -f progress" BEHAT_RERUN_CMD: "vendor/bin/behat --colors --strict --no-interaction -vvv -f pretty --rerun" diff --git a/.github/workflows/matrix.json b/.github/workflows/matrix.json index 419200a3e4..a9837b0d92 100644 --- a/.github/workflows/matrix.json +++ b/.github/workflows/matrix.json @@ -23,14 +23,16 @@ "symfony": "~6.4.0", "mariadb": "10.11.13", "doctrine_bundle": "^2.0", - "state_machine_adapter": "winzou_state_machine" + "state_machine_adapter": "winzou_state_machine", + "legacy_grid_config": true }, { "php": "8.5", "symfony": "~7.4.0", "mariadb": "11.4.7", "doctrine_bundle": "^3.0", - "state_machine_adapter": "symfony_workflow" + "state_machine_adapter": "symfony_workflow", + "legacy_grid_config": false } ] }, @@ -166,13 +168,17 @@ "state_machine_adapter": [ "symfony_workflow" ], + "legacy_grid_config": [ + false + ], "include": [ { "php": "8.3", "symfony": "~7.4.0", "mariadb": "11.4.7", "doctrine_bundle": "^2.0", - "state_machine_adapter": "winzou_state_machine" + "state_machine_adapter": "winzou_state_machine", + "legacy_grid_config": true } ] }, diff --git a/UPGRADE-2.3.md b/UPGRADE-2.3.md index 474fe538f5..cfd2a22404 100644 --- a/UPGRADE-2.3.md +++ b/UPGRADE-2.3.md @@ -71,6 +71,62 @@ > and their default parameters include Payment Request-specific values (e.g. `hash: paymentRequest.getHash()`). > Do not use them to configure the Payum after-pay redirect. +### Grid providers are now configurable + +As part of the ongoing modernization of the Grid component, Sylius now provides grid definitions in both **YAML** and **PHP**. + +Moving grid configuration to PHP provides several benefits: + +* Better IDE support, including autocompletion and static analysis. +* Improved maintainability for complex grid definitions. + +PHP grid definitions are the recommended approach going forward and represent the direction of Sylius 3.0. To support a +smooth migration, Sylius continues to support legacy YAML-based grids and now allows you to choose which format is used +for each grid. + +### Migration strategy + +During the migration period, grid definitions can be loaded from either: + +* **YAML** (legacy format) +* **PHP** (recommended format) + +You can configure the format globally: + +```yaml +sylius_core: + grid: + use_legacy_config: true # Use YAML grid definitions globally (default: false) +``` + +Or override the format for individual grids: + +```yaml +sylius_core: + grid: + grids: + sylius_admin_product_variant: + use_legacy_config: false # Use PHP configuration for this grid +``` + +This makes it possible to migrate grids incrementally rather than converting your entire application at once. + +> **Important** +> +> A grid can only be loaded from a single source. YAML and PHP definitions for the same grid cannot be merged. When migrating a grid to PHP, you must recreate the complete grid definition in PHP, including any vendor configuration you want to preserve. + +### Migration tooling + +To simplify the conversion process, you can use the community-maintained Grid configuration converter: + +* [Grid Config Converter](https://github.com/mamazu/grid-config-converter?utm_source=chatgpt.com) + +The converter can help bootstrap the migration from YAML to PHP and reduce the amount of manual work required. + +### Learn more + +For a complete overview of the Grid component, see the [Grid documentation](https://stack.sylius.com/grid/index). + ## Dependencies 1. The `behat/transliterator` package has been **deprecated** and will be removed in Sylius 3.0. diff --git a/composer.json b/composer.json index 218679f096..1831ea5999 100644 --- a/composer.json +++ b/composer.json @@ -74,8 +74,8 @@ "sylius-labs/association-hydrator": "^1.2", "sylius-labs/doctrine-migrations-extra-bundle": "^0.2 || ^0.3", "sylius/fixtures-bundle": "^1.9", - "sylius/grid": "^1.13", - "sylius/grid-bundle": "^1.13", + "sylius/grid": "^1.14", + "sylius/grid-bundle": "^1.16@alpha", "sylius/mailer": "^2.1", "sylius/mailer-bundle": "^2.1", "sylius/registry": "^1.6", diff --git a/config/packages/_sylius.yaml b/config/packages/_sylius.yaml index 1aa9eff54d..e21f092b69 100644 --- a/config/packages/_sylius.yaml +++ b/config/packages/_sylius.yaml @@ -8,6 +8,8 @@ imports: parameters: sylius_core.public_dir: '%kernel.project_dir%/public' + sylius_core.grids_configuration: + use_legacy_config: '%env(bool:TEST_SYLIUS_USE_LEGACY_GRID_CONFIG)%' sylius_api: enabled: true diff --git a/phparkitect.php b/phparkitect.php index cda20cfed7..2d025243e5 100644 --- a/phparkitect.php +++ b/phparkitect.php @@ -5,10 +5,12 @@ declare(strict_types=1); use Arkitect\ClassSet; use Arkitect\CLI\Config; use Arkitect\Expression\ForClasses\HaveNameMatching; +use Arkitect\Expression\ForClasses\Implement; use Arkitect\Expression\ForClasses\IsFinal; use Arkitect\Expression\ForClasses\NotDependsOnTheseNamespaces; use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces; use Arkitect\Rules\Rule; +use Sylius\Bundle\GridBundle\Grid\GridInterface; return static function (Config $config): void { @@ -23,6 +25,15 @@ return static function (Config $config): void , ); + $config->add( + $testsClassSet, + Rule::allClasses() + ->that(new Implement(GridInterface::class)) + ->should(new IsFinal()) + ->because('Grids should only be decorated') + , + ); + $separationClassSet = ClassSet::fromDir(__DIR__ . '/src/Sylius/{Component,Bundle}'); $config->add( @@ -86,5 +97,10 @@ return static function (Config $config): void )) ->because('Api should not depend on Admin and Shop') , + Rule::allClasses() + ->that(new Implement(GridInterface::class)) + ->should(new IsFinal()) + ->because('Grids should only be decorated') + , ); }; diff --git a/src/Sylius/Bundle/AdminBundle/Grid/AdminUserGrid.php b/src/Sylius/Bundle/AdminBundle/Grid/AdminUserGrid.php new file mode 100644 index 0000000000..8c5f7c38f1 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/AdminUserGrid.php @@ -0,0 +1,86 @@ +addOrderBy('createdAt', 'desc') + ->setLimits([10, 25, 50]) + ->withFields( + TwigField::create('email', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.email') + ->setSortable(true), + StringField::create('firstName') + ->setLabel('sylius.ui.first_name') + ->setSortable(true), + StringField::create('lastName') + ->setLabel('sylius.ui.last_name') + ->setSortable(true), + StringField::create('username') + ->setLabel('sylius.ui.username') + ->setSortable(true), + TwigField::create('createdAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.registration_date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + ->withFilters( + StringFilter::create( + name: 'search', + fields: ['email', 'username', 'firstName', 'lastName'], + ) + ->setLabel('sylius.ui.search'), + BooleanFilter::create('enabled') + ->setLabel('sylius.ui.enabled'), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/AdminUserGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/AdminUserGridInterface.php new file mode 100644 index 0000000000..18e88a8c55 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/AdminUserGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('code', 'asc') + + ->withFields( + TwigField::create('priority', '@SyliusAdmin/catalog_promotion/grid/field/priority.html.twig') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]) + ->setLabel('sylius.ui.priority') + ->setSortable(true), + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + TwigField::create('channels', '@SyliusAdmin/shared/grid/field/channels.html.twig') + ->setLabel('sylius.ui.channels') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('startDate', '@SyliusAdmin/catalog_promotion/grid/field/date.html.twig') + ->setLabel('sylius.ui.start_date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'text-center', + ], + ]), + TwigField::create('endDate', '@SyliusAdmin/catalog_promotion/grid/field/date.html.twig') + ->setLabel('sylius.ui.end_date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'text-center', + ], + ]), + TwigField::create('state', '@SyliusAdmin/catalog_promotion/grid/field/state.html.twig') + ->setLabel('sylius.ui.state') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + + ->withFilters( + StringFilter::create( + 'search', + fields: ['name', 'code'], + type: 'contains', + ) + ->setLabel('sylius.ui.search'), + EntityFilter::create( + 'channel', + resourceClass: $this->channelClass, + fields: ['channels.id'], + ) + ->setLabel('sylius.ui.channel'), + DateFilter::create('startDate') + ->setLabel('sylius.ui.start_date') + ->setOptions([ + 'inclusive_to' => true, + ]), + DateFilter::create('endDate') + ->setLabel('sylius.ui.end_date') + ->setOptions([ + 'inclusive_to' => true, + ]), + BooleanFilter::create('enabled') + ->setLabel('sylius.ui.enabled'), + Filter::create('state', 'select') + ->setLabel('sylius.ui.state') + ->addFormOption('choices', [ + 'sylius.ui.active' => CatalogPromotionStates::STATE_ACTIVE, + 'sylius.ui.inactive' => CatalogPromotionStates::STATE_INACTIVE, + ]), + ) + + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + ShowAction::create(), + Action::create('show_variants', 'show') + ->setLabel('sylius.ui.list_variants') + ->setIcon('tabler:list-letters') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_catalog_promotion_product_variant_index', + 'parameters' => [ + 'id' => 'resource.id', + ], + ], + ]), + UpdateAction::create(), + Action::create('delete', 'delete_catalog_promotion') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_catalog_promotion_delete', + 'parameters' => [ + 'code' => 'resource.code', + ], + ], + 'state' => 'resource.state', + ]), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/CatalogPromotionGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/CatalogPromotionGridInterface.php new file mode 100644 index 0000000000..2a81fedb80 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/CatalogPromotionGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('nameAndDescription', 'asc') + ->withFields( + TwigField::create('nameAndDescription', '@SyliusAdmin/shared/grid/field/channel.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.') + ->setSortable(true, 'name'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true, 'code'), + TwigField::create('themeName', '@SyliusAdmin/channel/grid/field/theme.html.twig') + ->setLabel('sylius.ui.theme') + ->setSortable(true), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + ) + ->withFilters( + StringFilter::create('search', ['code', 'name']) + ->setLabel('sylius.ui.search'), + BooleanFilter::create('enabled') + ->setLabel('sylius.ui.enabled'), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ChannelGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ChannelGridInterface.php new file mode 100644 index 0000000000..12866d2950 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ChannelGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createByChannelPricingIdListQueryBuilder', [ + '$channelPricingId', + ]) + ->setLimits([10, 25, 50]) + + ->withFields( + TwigField::create('price', '@SyliusAdmin/channel_pricing_log_entry/grid/field/price.html.twig') + ->setLabel('sylius.ui.price') + ->setPath('.'), + TwigField::create('originalPrice', '@SyliusAdmin/channel_pricing_log_entry/grid/field/original_price.html.twig') + ->setLabel('sylius.ui.original_price') + ->setPath('.'), + DateTimeField::create('loggedAt') + ->setLabel('sylius.ui.logged_at'), + ) + + ->withFilters( + DateFilter::create('loggedAt') + ->setLabel('sylius.ui.logged_at'), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ChannelPricingLogEntryGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ChannelPricingLogEntryGridInterface.php new file mode 100644 index 0000000000..181ea8124a --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ChannelPricingLogEntryGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('code', 'asc') + + ->withFields( + TwigField::create('name', '@SyliusAdmin/country/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.') + ->setSortable(true, 'code'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + ) + + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + Filter::create('enabled', 'boolean') + ->setLabel('sylius.ui.enabled'), + ) + + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/CountryGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/CountryGridInterface.php new file mode 100644 index 0000000000..e4aa49b700 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/CountryGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('code', 'asc') + ->withFields( + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + StringField::create('name') + ->setLabel('sylius.ui.name'), + ) + ->withFilters( + StringFilter::create('code') + ->setLabel('sylius.ui.code'), + ) + ->withMainActions( + CreateAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/CurrencyGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/CurrencyGridInterface.php new file mode 100644 index 0000000000..3abcfe29be --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/CurrencyGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('createdAt', 'desc') + ->withFields( + TwigField::create('email', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.email') + ->setSortable(true), + StringField::create('lastName') + ->setLabel('sylius.ui.last_name') + ->setSortable(true), + StringField::create('firstName') + ->setLabel('sylius.ui.first_name') + ->setSortable(true), + TwigField::create('createdAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.registration_date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('enabled', '@SyliusAdmin/customer/grid/field/enabled.html.twig') + ->setLabel('sylius.ui.enabled') + ->setPath('.') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('verified', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.verified') + ->setPath('user?.verified') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + ) + ->withFilters( + StringFilter::create('search', ['email', 'firstName', 'lastName']) + ->setLabel('sylius.ui.search'), + Filter::create('group', 'ux_autocomplete') + ->setLabel('sylius.ui.customer_groups') + ->setFormOptions([ + 'multiple' => true, + 'extra_options' => [ + 'class' => $this->customerGroupClass, + 'choice_label' => 'name', + ], + ]) + ->setOptions([ + 'fields' => ['group.id'], + ]), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + Action::create('show_orders', 'show') + ->setLabel('sylius.ui.show_orders') + ->setIcon('tabler:shopping-bag') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_customer_order_index', + 'parameters' => ['id' => 'resource.id'], + ], + ]), + ShowAction::create(), + UpdateAction::create(), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/CustomerGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/CustomerGridInterface.php new file mode 100644 index 0000000000..e00df73f1f --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/CustomerGridInterface.php @@ -0,0 +1,24 @@ +addOrderBy('name', 'asc') + ->setLimits([10, 25, 50]) + ->withFields( + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-75', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-25', + ], + ]), + ) + ->withFilters( + StringFilter::create('search', ['code', 'name']) + ->setLabel('sylius.ui.search'), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/CustomerGroupGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/CustomerGroupGridInterface.php new file mode 100644 index 0000000000..c4986f689c --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/CustomerGroupGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createByCustomerIdCriteriaAwareQueryBuilder', [ + 'criteria' => null, + 'customerId' => '$id', + ]) + ->extends('sylius_admin_order') + ->addOrderBy('number', 'desc') + ->withFields( + StringField::create('customer') + ->setEnabled(false), + ) + ->withFilters( + Filter::create('customer', 'string') + ->setEnabled(false), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/CustomerOrderGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/CustomerOrderGridInterface.php new file mode 100644 index 0000000000..250c4ee73f --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/CustomerOrderGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('id', 'desc') + + ->withFields( + StringField::create('id') + ->setEnabled(false) + ->setSortable(true), + StringField::create('sourceCurrency') + ->setLabel('sylius.ui.source_currency') + ->setPath('sourceCurrency.name'), + StringField::create('targetCurrency') + ->setLabel('sylius.ui.target_currency') + ->setPath('targetCurrency.name'), + StringField::create('ratio') + ->setLabel('sylius.ui.ratio') + ->setPath('ratio') + ->setSortable(true), + ) + + ->withFilters( + EntityFilter::create('currency', $this->currencyClass, fields: ['sourceCurrency', 'targetCurrency']) + ->setLabel('sylius.ui.currency') + ->addFormOption('choice_label', 'name'), + ) + + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ExchangeRateGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ExchangeRateGridInterface.php new file mode 100644 index 0000000000..b3b125d369 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ExchangeRateGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('name', 'asc') + ->setRepositoryMethod('createInventoryListQueryBuilder', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + ]) + ->withFields( + TwigField::create('name', '@SyliusAdmin/inventory/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.') + ->setSortable(true, 'product.translations.name') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-75', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-25', + ], + ]), + TwigField::create('inventory', '@SyliusAdmin/inventory/grid/field/inventory.html.twig') + ->setLabel('sylius.ui.inventory') + ->setPath('.'), + ) + + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + Filter::create('name', 'string') + ->setLabel('sylius.ui.name') + ->setOptions([ + 'fields' => [ + 'translation.name', + ], + ]), + Filter::create('product', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.product') + ->setFormOptions([ + 'multiple' => false, + 'extra_options' => [ + 'class' => $this->productClass, + 'translation_fields' => ['name'], + 'choice_label' => 'name', + ], + ]) + ->setOptions([ + 'fields' => ['product.id'], + ]), + ) + ->withItemActions( + Action::create('update_product', 'update') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_update', + 'parameters' => [ + 'id' => 'resource.product.id', + ], + ], + ]) + ->setLabel('sylius.ui.edit_product'), + UpdateAction::create([ + 'link' => [ + 'parameters' => [ + 'id' => 'resource.id', + 'productId' => 'resource.product.id', + ], + ], + ]), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/InventoryGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/InventoryGridInterface.php new file mode 100644 index 0000000000..39a5127cff --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/InventoryGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('code', 'asc') + ->withFields( + TwigField::create('name', '@SyliusAdmin/locale/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + ) + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + DeleteAction::create(), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/LocaleGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/LocaleGridInterface.php new file mode 100644 index 0000000000..591801835f --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/LocaleGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod( + 'createCriteriaAwareSearchListQueryBuilder', + ['criteria' => '$criteria'], + ) + ->addOrderBy('number', 'desc') + ->withFields( + TwigField::create('number', '@SyliusAdmin/shared/grid/field/order_number.html.twig') + ->setLabel('sylius.ui.number') + ->setPath('.') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1', + ], + ]), + TwigField::create('date', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.date') + ->setPath('checkoutCompletedAt') + ->setSortable(true, 'checkoutCompletedAt') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('customer', '@SyliusAdmin/shared/grid/field/customer.html.twig') + ->setLabel('sylius.ui.customer') + ->setSortable(true, 'customer.lastName') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-100', + ], + ]), + TwigField::create('channel', '@SyliusAdmin/shared/grid/field/channel.html.twig') + ->setLabel('sylius.ui.channel') + ->setSortable(true, 'channel.code'), + TwigField::create('state', '@SyliusAdmin/order/grid/field/order_state.html.twig') + ->setLabel('sylius.ui.state') + ->setSortable(true), + TwigField::create('paymentState', '@SyliusAdmin/order/grid/field/payment_state.html.twig') + ->setLabel('sylius.ui.payment_state') + ->setSortable(true), + TwigField::create('shippingState', '@SyliusAdmin/order/grid/field/shipping_state.html.twig') + ->setLabel('sylius.ui.shipping_state') + ->setSortable(true), + TwigField::create('total', '@SyliusAdmin/order/grid/field/order_total.html.twig') + ->setLabel('sylius.ui.total') + ->setPath('.') + ->setSortable(true, 'total') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-end', + 'td_class' => 'text-end', + ], + ]), + StringField::create('currencyCode') + ->setLabel('sylius.ui.currency') + ->setSortable(true), + ) + + ->withFilters( + SelectFilter::create('state', [ + 'sylius.ui.cancelled' => 'cancelled', + 'sylius.ui.new' => 'new', + 'sylius.ui.fulfilled' => 'fulfilled', + ]) + ->setLabel('sylius.ui.state'), + Filter::create('product', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.product') + ->setFormOptions([ + 'multiple' => true, + 'extra_options' => [ + 'class' => $this->productClass, + 'translation_fields' => ['name'], + 'choice_label' => 'name', + ], + ]), + Filter::create('variant', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.variant') + ->setFormOptions([ + 'multiple' => true, + 'extra_options' => [ + 'class' => $this->productVariantClass, + 'translation_fields' => ['name'], + 'choice_label' => 'descriptor', + ], + ]), + Filter::create('number', 'string') + ->setLabel('sylius.ui.number') + ->setFormOptions([ + 'type' => StringFilter::TYPE_CONTAINS, + ]), + Filter::create('customer', 'ux_autocomplete') + ->setLabel('sylius.ui.customer') + ->setFormOptions([ + 'extra_options' => [ + 'class' => $this->customerClass, + 'choice_label' => 'fullname', + ], + ]), + Filter::create('date', 'date') + ->setLabel('sylius.ui.date') + ->setOptions([ + 'field' => 'checkoutCompletedAt', + 'inclusive_to' => true, + ]), + Filter::create('channel', 'ux_autocomplete') + ->setLabel('sylius.ui.channel') + ->setFormOptions([ + 'extra_options' => [ + 'class' => $this->channelClass, + 'choice_label' => 'name', + ], + ]), + Filter::create('total', 'money') + ->setLabel('sylius.ui.total') + ->setOptions([ + 'currency_field' => 'currencyCode', + ]), + Filter::create('shipping_method', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.shipping_method') + ->setOptions([ + 'fields' => [ + 'shipments.method.id', + ], + ]) + ->setFormOptions([ + 'extra_options' => [ + 'class' => $this->shippingMethodClass, + 'translation_fields' => ['name'], + 'choice_label' => 'name', + ], + ]), + ) + ->withItemActions( + ShowAction::create(), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/OrderGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/OrderGridInterface.php new file mode 100644 index 0000000000..8ba25150db --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/OrderGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createListQueryBuilder') + ->addOrderBy('createdAt', 'desc') + ->setLimits([10, 25, 50]) + ->withFields( + TwigField::create('createdAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('number', '@SyliusAdmin/shared/grid/field/order_number.html.twig') + ->setLabel('sylius.ui.order') + ->setPath('order') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1', + ], + ]), + TwigField::create('customer', '@SyliusAdmin/shared/grid/field/customer.html.twig') + ->setLabel('sylius.ui.customer') + ->setPath('order.customer') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-100', + ], + ]), + TwigField::create('channel', '@SyliusAdmin/shared/grid/field/channel.html.twig') + ->setLabel('sylius.ui.channel') + ->setPath('order.channel'), + TwigField::create('state', '@SyliusAdmin/shared/grid/field/payment_state.html.twig') + ->setLabel('sylius.ui.state') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + ->withFilters( + Filter::create('state', 'select') + ->setLabel('sylius.ui.state') + ->setFormOptions([ + 'choices' => [ + 'sylius.ui.cancelled' => 'cancelled', + 'sylius.ui.completed' => 'completed', + 'sylius.ui.failed' => 'failed', + 'sylius.ui.new' => 'new', + 'sylius.ui.processing' => 'processing', + 'sylius.ui.refunded' => 'refunded', + ], + ]), + Filter::create('channel', 'entity') + ->setLabel('sylius.ui.channel') + ->setOptions([ + 'fields' => [ + 'order.channel', + ], + ]) + ->setFormOptions([ + 'class' => $this->channelClass, + ]), + ) + ->withItemActions( + Action::create('list_payment_requests', 'show') + ->setIcon('tabler:list-letters') + ->setLabel('sylius.ui.list_payment_requests') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_payment_request_index', + 'parameters' => [ + 'paymentId' => 'resource.id', + ], + ], + ]), + Action::create('complete', 'apply_transition') + ->setLabel('sylius.ui.complete') + ->setIcon('tabler:credit-card-pay') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_payment_complete', + 'parameters' => [ + 'id' => 'resource.id', + ], + ], + 'class' => 'teal', + 'transition' => 'complete', + 'graph' => 'sylius_payment', + ]), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/PaymentGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/PaymentGridInterface.php new file mode 100644 index 0000000000..722b156584 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/PaymentGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createListQueryBuilder', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + ]) + ->addOrderBy('position', 'asc') + ->setLimits([10, 25, 50]) + + ->withFields( + TwigField::create('position', '@SyliusUi/grid/field/position.html.twig') + ->setLabel('sylius.ui.position') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true, 'translation.name'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true, 'code'), + TwigField::create('gateway', '@SyliusUi/grid/field/humanized.html.twig') + ->setLabel('sylius.ui.gateway') + ->setPath('gatewayConfig.factoryName') + ->setSortable(true, 'gatewayConfig.factoryName'), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + ->withFilters( + StringFilter::create('search', ['code', 'translation.name']) + ->setLabel('sylius.ui.search') + ->setFormOptions(['type' => StringFilter::TYPE_CONTAINS]), + Filter::create('enabled', 'boolean') + ->setLabel('sylius.ui.enabled'), + ) + + ->withMainActions( + Action::create('create', 'create_payment_method'), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/PaymentMethodGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/PaymentMethodGridInterface.php new file mode 100644 index 0000000000..1507ca193f --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/PaymentMethodGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createQueryBuilderForPayment', [ + 'paymentId' => '$paymentId', + ]); + $gridBuilder->setLimits([10, 25, 50]); + $gridBuilder->addOrderBy('createdAt', 'desc'); + + $gridBuilder + ->withFields( + StringField::create('hash') + ->setLabel('sylius.ui.hash') + ->setSortable(true), + TwigField::create('method', '@SyliusAdmin/payment_request/grid/field/method.html.twig') + ->setLabel('sylius.ui.payment_method') + ->setPath('.'), + TwigField::create('action', '@SyliusAdmin/payment_request/grid/field/action.html.twig') + ->setLabel('sylius.ui.action'), + TwigField::create('state', '@SyliusAdmin/payment_request/grid/field/state.html.twig') + ->setLabel('sylius.ui.state'), + TwigField::create('createdAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.creation_date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('updatedAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.updating_date') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + ) + + ->withFilters( + Filter::create('payment_method', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.payment_method') + ->setFormOptions(['extra_options' => [ + 'class' => $this->paymentMethodClass, + 'translation_fields' => ['name'], + 'choice_label' => 'name', + ]]) + ->setOptions([ + 'fields' => ['method.id'], + ]), + SelectFilter::create('action', [ + 'sylius.ui.authorize' => PaymentRequestInterface::ACTION_AUTHORIZE, + 'sylius.ui.cancel' => PaymentRequestInterface::ACTION_CANCEL, + 'sylius.ui.capture' => PaymentRequestInterface::ACTION_CAPTURE, + 'sylius.ui.notify' => PaymentRequestInterface::ACTION_NOTIFY, + 'sylius.ui.payout' => PaymentRequestInterface::ACTION_PAYOUT, + 'sylius.ui.refund' => PaymentRequestInterface::ACTION_REFUND, + 'sylius.ui.status' => PaymentRequestInterface::ACTION_STATUS, + 'sylius.ui.sync' => PaymentRequestInterface::ACTION_SYNC, + ]) + ->setLabel('sylius.ui.action'), + SelectFilter::create('state', [ + 'sylius.ui.cancelled' => PaymentRequestInterface::STATE_CANCELLED, + 'sylius.ui.completed' => PaymentRequestInterface::STATE_COMPLETED, + 'sylius.ui.failed' => PaymentRequestInterface::STATE_FAILED, + 'sylius.ui.new' => PaymentRequestInterface::STATE_NEW, + 'sylius.ui.processing' => PaymentRequestInterface::STATE_PROCESSING, + ]) + ->setLabel('sylius.ui.state'), + ) + + ->withItemActions( + ShowAction::create() + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_payment_request_show', + 'parameters' => [ + 'hash' => 'resource.hash', + 'paymentId' => 'resource.payment.id', + ], + ], + ]), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/PaymentRequestGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/PaymentRequestGridInterface.php new file mode 100644 index 0000000000..e535f6e3be --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/PaymentRequestGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createListQueryBuilder', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + ]) + ->addOrderBy('code', 'asc') + ->setLimits([10, 25, 50]) + + ->withFields( + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-75', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-25', + ], + ]), + ) + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + Filter::create('name', 'string') + ->setLabel('sylius.ui.name') + ->setOptions([ + 'fields' => [ + 'translation.name', + ], + ]), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductAssociationTypeGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductAssociationTypeGridInterface.php new file mode 100644 index 0000000000..371638dd72 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductAssociationTypeGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createListQueryBuilder', [ + "expr:service('sylius.context.locale').getLocaleCode()", + ]) + ->setLimits([10, 25, 50]) + ->addOrderBy('position', 'asc') + + ->withFields( + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-75', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-25', + ], + ]), + TwigField::create('type', '@SyliusUi/grid/field/label.html.twig') + ->setLabel('sylius.ui.type') + ->setSortable(true), + TwigField::create('translatable', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.translatable') + ->setSortable(true), + TwigField::create('position', '@SyliusUi/grid/field/position.html.twig') + ->setLabel('sylius.ui.position') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + + ->withFilters( + StringFilter::create('search', ['translations.name', 'code']) + ->setLabel('sylius.ui.search') + ->setFormOptions(['type' => StringFilter::TYPE_CONTAINS]), + SelectFilter::create('type', [ + 'sylius.ui.checkbox' => CheckboxAttributeType::TYPE, + 'sylius.ui.date' => DateAttributeType::TYPE, + 'sylius.ui.datetime' => DatetimeAttributeType::TYPE, + 'sylius.ui.float' => FloatAttributeType::TYPE, + 'sylius.ui.integer' => IntegerAttributeType::TYPE, + 'sylius.ui.percent' => PercentAttributeType::TYPE, + 'sylius.ui.select' => SelectAttributeType::TYPE, + 'sylius.ui.text' => TextAttributeType::TYPE, + 'sylius.ui.textarea' => TextareaAttributeType::TYPE, + ]) + ->setLabel('sylius.ui.type') + ->addFormOption('multiple', true) + ->addFormOption('autocomplete', true), + Filter::create('translatable', 'boolean') + ->setLabel('sylius.ui.translatable'), + ) + + ->withMainActions( + Action::create('create', 'create_product_attribute'), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductAttributeGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductAttributeGridInterface.php new file mode 100644 index 0000000000..6a444ca4fe --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductAttributeGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createListQueryBuilder', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + ]) + ->setLimits([10, 25, 50]) + ->addOrderBy('code', 'asc') + ->withFields( + TwigField::create('image', '@SyliusAdmin/product/grid/field/product_image.html.twig') + ->setLabel('sylius.ui.image') + ->setPath('.') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1', + ], + ]), + TwigField::create('name', '@SyliusAdmin/product/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true, 'translation.name') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-33', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + TwigField::create('mainTaxon', '@SyliusAdmin/product/grid/field/main_taxon.html.twig') + ->setLabel('sylius.ui.main_taxon'), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled'), + ) + + ->withFilters( + StringFilter::create('search', ['code', 'translation.name']) + ->setLabel('sylius.ui.search') + ->addFormOption('type', StringFilter::TYPE_CONTAINS), + BooleanFilter::create('enabled') + ->setLabel('sylius.ui.enabled'), + EntityFilter::create('channel', $this->channelClass, fields: ['channels.id']) + ->setLabel('sylius.ui.channel'), + Filter::create('taxon', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.taxon') + ->setFormOptions([ + 'multiple' => false, + 'extra_options' => [ + 'class' => $this->taxonClass, + 'translation_fields' => ['name'], + 'choice_label' => 'fullname', + ], + ]) + ->addOption('fields', ['productTaxons.taxon.id']), + Filter::create('main_taxon', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.main_taxon') + ->setFormOptions([ + 'multiple' => false, + 'extra_options' => [ + 'class' => $this->taxonClass, + 'translation_fields' => ['name'], + 'choice_label' => 'fullname', + ], + ]) + ->addOption('fields', ['mainTaxon.id']), + ) + + ->withMainActions( + Action::create('create', 'links') + ->setLabel('sylius.ui.create') + ->setOptions([ + 'class' => 'primary', + 'icon' => 'tabler:plus', + 'header' => [ + 'icon' => 'tabler:cube', + 'label' => 'sylius.ui.type', + ], + 'links' => [ + 'simple' => [ + 'label' => 'sylius.ui.simple_product', + 'route' => 'sylius_admin_product_create_simple', + ], + 'configurable' => [ + 'label' => 'sylius.ui.configurable_product', + 'route' => 'sylius_admin_product_create', + ], + ], + ]), + ) + ->withItemActions( + Action::create('details', 'show') + ->setLabel('sylius.ui.details'), + UpdateAction::create(), + DeleteAction::create(), + ) + ->withSubItemActions( + Action::create('variants', 'list') + ->setLabel('sylius.ui.variants') + ->setOptions([ + 'links' => [ + 'index' => [ + 'label' => 'sylius.ui.list_variants', + 'route' => 'sylius_admin_product_variant_index', + 'parameters' => [ + 'productId' => 'resource.id', + ], + ], + 'create' => [ + 'label' => 'sylius.ui.create', + 'route' => 'sylius_admin_product_variant_create', + 'parameters' => [ + 'productId' => 'resource.id', + ], + ], + 'generate' => [ + 'label' => 'sylius.ui.generate', + 'route' => 'sylius_admin_product_variant_generate', + 'visible' => 'resource.hasOptions', + 'parameters' => [ + 'productId' => 'resource.id', + ], + ], + ], + ]), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } + + public function getResourceClass(): string + { + return $this->productClass; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductGridInterface.php new file mode 100644 index 0000000000..2cae130c04 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod('createListQueryBuilder', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + ]) + ->addOrderBy('position', 'asc') + ->withFields( + TwigField::create('position', '@SyliusUi/grid/field/position.html.twig') + ->setLabel('sylius.ui.position') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true, 'translation.name') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-75', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-25', + ], + ]), + ) + ->withFilters( + StringFilter::create('search', ['code', 'translation.name']) + ->setLabel('sylius.ui.search') + ->addFormOption('type', StringFilter::TYPE_CONTAINS), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductOptionGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductOptionGridInterface.php new file mode 100644 index 0000000000..3e8be4a8a0 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductOptionGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('date', 'desc') + ->withFields( + TwigField::create('rating', '@SyliusAdmin/product_review/grid/field/rating.html.twig') + ->setLabel('sylius.ui.rating') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('author', '@SyliusAdmin/shared/grid/field/customer.html.twig') + ->setLabel('sylius.ui.customer'), + StringField::create('title') + ->setLabel('sylius.ui.title') + ->setSortable(true), + StringField::create('reviewSubject') + ->setLabel('sylius.ui.product'), + TwigField::create('date', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.date') + ->setPath('createdAt') + ->setSortable(true, 'createdAt') + ->withOptions([ + 'vars' => [ + 'th_class' => 'text-center', + ], + ]), + TwigField::create('status', '@SyliusAdmin/product_review/grid/field/status.html.twig') + ->setLabel('sylius.ui.status') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + + ->withFilters( + Filter::create('title', 'string') + ->setLabel('sylius.ui.title'), + SelectFilter::create( + 'status', + [ + 'sylius.ui.new' => 'new', + 'sylius.ui.accepted' => 'accepted', + 'sylius.ui.rejected' => 'rejected', + ], + ) + ->setLabel('sylius.ui.status'), + Filter::create('product', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.product') + ->setFormOptions([ + 'multiple' => false, + 'extra_options' => [ + 'class' => $this->productClass, + 'translation_fields' => ['name'], + 'choice_label' => 'name', + ], + ]) + ->addOption('fields', ['reviewSubject.id']), + ) + + ->withItemActions( + Action::create('accept', 'apply_transition') + ->setLabel('sylius.ui.accept') + ->setIcon('tabler:check') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_review_accept', + 'parameters' => [ + 'id' => 'resource.id', + ], + ], + 'class' => 'btn-success btn-icon', + 'show_disabled' => false, + 'transition' => 'accept', + 'graph' => 'sylius_product_review', + ]), + Action::create('reject', 'apply_transition') + ->setLabel('sylius.ui.reject') + ->setIcon('tabler:x') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_review_reject', + 'parameters' => [ + 'id' => 'resource.id', + ], + ], + 'class' => 'btn-warning btn-icon', + 'show_disabled' => false, + 'transition' => 'reject', + 'graph' => 'sylius_product_review', + ]), + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductReviewGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductReviewGridInterface.php new file mode 100644 index 0000000000..f6682d0ccc --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductReviewGridInterface.php @@ -0,0 +1,24 @@ +addOrderBy('position', 'asc') + ->setLimits([10, 25, 50]) + ->setRepositoryMethod('createListQueryBuilderForTaxon', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + '$taxonId', + ]) + + ->withFields( + SyliusTwigField::create('image', '@SyliusAdmin/product/grid/field/product_image.html.twig') + ->setLabel('sylius.ui.image') + ->setPath('product'), + TwigField::create('name', '@SyliusAdmin/product/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('product') + ->setSortable(true, 'product.translations.name') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-75', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setPath('product') + ->setSortable(true, 'product.code') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-25', + ], + ]), + TwigField::create('mainTaxon', '@SyliusAdmin/product/grid/field/main_taxon.html.twig') + ->setLabel('sylius.ui.main_taxon') + ->setPath('product.mainTaxon'), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setPath('product.enabled'), + TwigField::create('position', '@SyliusAdmin/product_taxon/grid/field/position.html.twig') + ->setLabel('sylius.ui.position') + ->setPath('.') + ->setSortable(true), + ) + + ->withFilters( + StringFilter::create('search', ['product.code', 'product.translations.name']) + ->setLabel('sylius.ui.search') + ->setFormOptions([ + 'type' => StringFilter::TYPE_CONTAINS, + ]), + BooleanFilter::create('enabled') + ->setLabel('sylius.ui.enabled') + ->setOptions([ + 'field' => 'product.enabled', + ]), + EntityFilter::create('channel', $this->channelClass, fields: ['product.channels.id']) + ->setLabel('sylius.ui.channel'), + ) + + ->withMainActions( + Action::create('update_positions', 'update_product_taxon_positions'), + ) + ->withItemActions( + Action::create('details', 'show') + ->setLabel('sylius.ui.details') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_show', + 'parameters' => [ + 'id' => 'resource.product.id', + ], + ], + ]), + Action::create('update', 'update') + ->setLabel('sylius.ui.edit') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_update', + 'parameters' => [ + 'id' => 'resource.product.id', + ], + ], + ]), + Action::create('delete', 'delete') + ->setLabel('sylius.ui.delete') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_delete', + 'parameters' => [ + 'id' => 'resource.product.id', + ], + ], + ]), + ) + ->withSubItemActions( + Action::create('variants', 'list') + ->setLabel('sylius.ui.variants') + ->setOptions([ + 'links' => [ + 'index' => [ + 'label' => 'sylius.ui.list_variants', + 'route' => 'sylius_admin_product_variant_index', + 'parameters' => [ + 'productId' => 'resource.product.id', + ], + ], + 'create' => [ + 'label' => 'sylius.ui.create', + 'route' => 'sylius_admin_product_variant_create', + 'parameters' => [ + 'productId' => 'resource.product.id', + ], + ], + 'generate' => [ + 'label' => 'sylius.ui.generate', + 'route' => 'sylius_admin_product_variant_generate', + 'visible' => 'resource.product.hasOptions', + 'parameters' => [ + 'productId' => 'resource.product.id', + ], + ], + ], + ]), + ) + ->withBulkActions( + Action::create('delete', 'delete') + ->setLabel('sylius.ui.delete_products') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_taxon_bulk_delete_products', + 'parameters' => [ + 'taxonId' => '$taxonId', + ], + ], + ]), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductTaxonGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductTaxonGridInterface.php new file mode 100644 index 0000000000..9ab125c379 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductTaxonGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod('createQueryBuilderByProductId', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + '$productId', + ]) + ->addOrderBy('position', 'asc') + ->withFields( + TwigField::create('name', '@SyliusAdmin/product_variant/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.'), + StringField::create('code') + ->setLabel('sylius.ui.code'), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->withOptions([ + 'vars' => [ + 'th_class' => 'text-center', + ], + ]), + TwigField::create('inventory', '@SyliusAdmin/product_variant/grid/field/inventory.html.twig') + ->setLabel('sylius.ui.inventory') + ->setPath('.') + ->withOptions([ + 'vars' => [ + 'th_class' => 'text-center', + ], + ]), + TwigField::create('position', '@SyliusAdmin/product_variant/grid/field/position.html.twig') + ->setLabel('sylius.ui.position') + ->setPath('.') + ->setSortable(true, 'position') + ->withOptions([ + 'vars' => [ + 'th_class' => 'text-center', + ], + ]), + ) + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + Filter::create('name', 'string') + ->setLabel('sylius.ui.name') + ->setOptions([ + 'fields' => [ + 'translation.name', + ], + ]), + ) + ->withMainActions( + Action::create('generate', 'generate_variants') + ->setOptions([ + 'product' => 'expr:service(\'sylius.repository.product\').find($productId)', + ]), + Action::create('update_positions', 'update_product_variant_positions'), + CreateAction::create() + ->setOptions([ + 'link' => [ + 'parameters' => [ + 'productId' => '$productId', + ], + ], + ]), + ) + ->withItemActions( + UpdateAction::create([ + 'link' => [ + 'parameters' => [ + 'id' => 'resource.id', + 'productId' => '$productId', + ], + ], + ]), + DeleteAction::create([ + 'link' => [ + 'parameters' => [ + 'id' => 'resource.id', + 'productId' => '$productId', + ], + ], + ]), + ) + ->withBulkActions( + DeleteAction::create([ + 'link' => [ + 'parameters' => [ + 'productId' => '$productId', + ], + ], + ]), + ) + ->addActionGroup( + ActionGroup::create( + 'subitem', + Action::create('price_history', 'price_history') + ->setLabel('sylius.ui.price_history'), + ), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductVariantGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductVariantGridInterface.php new file mode 100644 index 0000000000..cb599a059f --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductVariantGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod('createCatalogPromotionListQueryBuilder', [ + $this->locale, + 'expr:notFoundOnNull(service("sylius.repository.catalog_promotion").find($id))', + ]) + ->addOrderBy('code', 'asc') + + ->withFields( + TwigField::create('name', '@SyliusAdmin/product_variant/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.'), + StringField::create('code') + ->setLabel('sylius.ui.code'), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled'), + TwigField::create('inventory', '@SyliusAdmin/product_variant/grid/field/inventory.html.twig') + ->setLabel('sylius.ui.inventory') + ->setPath('.'), + ) + + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + StringFilter::create('name', ['translation.name']) + ->setLabel('sylius.ui.name'), + ) + + ->withItemActions( + Action::create('show_product', 'show') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_product_show', + 'parameters' => [ + 'id' => 'resource.product.id', + ], + ], + ]) + ->setLabel('sylius.ui.show_product'), + UpdateAction::create([ + 'link' => [ + 'parameters' => [ + 'id' => 'resource.id', + 'productId' => 'resource.product.id', + ], + ], + ]), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ProductVariantWithCatalogPromotionGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ProductVariantWithCatalogPromotionGridInterface.php new file mode 100644 index 0000000000..46005c658c --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ProductVariantWithCatalogPromotionGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->orderBy('used', 'desc') + ->setRepositoryMethod('createQueryBuilderByPromotionId', [ + 'promotionId' => '$promotionId', + ]) + ->withFields( + StringField::create('code') + ->setLabel('sylius.ui.code') + ->setSortable(true), + StringField::create('usageLimit') + ->setLabel('sylius.ui.usage_limit') + ->setSortable(true), + StringField::create('perCustomerUsageLimit') + ->setLabel('sylius.ui.per_customer_usage_limit') + ->setSortable(true), + TwigField::create('used', '@SyliusAdmin/promotion_coupon/grid/field/used.html.twig') + ->setLabel('sylius.ui.used') + ->setSortable(true) + ->setPath('.'), + DateTimeField::create('expiresAt') + ->setLabel('sylius.ui.expires_at') + ->setSortable(true) + ->setOption('format', 'd-m-Y'), + ) + ->withFilters( + Filter::create('code', 'string') + ->setLabel('sylius.ui.code'), + ) + ->withMainActions( + Action::create('generate', 'default') + ->setLabel('sylius.ui.generate') + ->setIcon('tabler:arrows-split') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_admin_promotion_coupon_generate', + 'parameters' => [ + 'promotionId' => '$promotionId', + ], + ], + ]), + CreateAction::create() + ->setOptions([ + 'link' => [ + 'parameters' => [ + 'promotionId' => '$promotionId', + ], + ], + ]), + ) + ->withItemActions( + UpdateAction::create([ + 'link' => [ + 'parameters' => [ + 'id' => 'resource.id', + 'promotionId' => '$promotionId', + ], + ], + ]), + DeleteAction::create([ + 'link' => [ + 'parameters' => [ + 'id' => 'resource.id', + 'promotionId' => '$promotionId', + ], + ], + ]), + ) + ->withBulkActions( + DeleteAction::create([ + 'link' => [ + 'parameters' => [ + 'promotionId' => '$promotionId', + ], + ], + ]), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/PromotionCouponGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/PromotionCouponGridInterface.php new file mode 100644 index 0000000000..9a2b1b7f8d --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/PromotionCouponGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('priority', 'desc') + ->withFields( + TwigField::create('priority', '@SyliusAdmin/promotion/grid/field/priority.html.twig') + ->setLabel('sylius.ui.priority') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('name', '@SyliusUi/grid/field/name_and_description.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-100', + ], + ]), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + TwigField::create('couponBased', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.coupons') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('usage', '@SyliusAdmin/promotion/grid/field/usage.html.twig') + ->setLabel('sylius.ui.usage') + ->setPath('.') + ->setSortable(true, 'used'), + ) + + ->withFilters( + StringFilter::create('search', ['code', 'name']) + ->setLabel('sylius.ui.search'), + Filter::create('couponBased', 'boolean') + ->setLabel('sylius.ui.coupon_based'), + Filter::create('coupon_code', 'string') + ->setLabel('sylius.ui.coupon') + ->setOptions([ + 'fields' => [ + 'coupons.code', + ], + ]), + ExistsFilter::create('archival', 'archivedAt') + ->setLabel('sylius.ui.archival') + ->setDefaultValue(false), + ) + + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + Action::create('archive', 'archive'), + ) + ->addActionGroup( + ActionGroup::create( + 'subitem', + Action::create('coupons', 'list') + ->setLabel('sylius.ui.manage_coupons') + ->setOptions([ + 'visible' => 'resource.couponBased', + 'links' => [ + 'index' => [ + 'label' => 'sylius.ui.list_coupons', + 'route' => 'sylius_admin_promotion_coupon_index', + 'parameters' => [ + 'promotionId' => 'resource.id', + ], + ], + 'create' => [ + 'label' => 'sylius.ui.create', + 'route' => 'sylius_admin_promotion_coupon_create', + 'parameters' => [ + 'promotionId' => 'resource.id', + ], + ], + 'generate' => [ + 'label' => 'sylius.ui.generate', + 'route' => 'sylius_admin_promotion_coupon_generate', + 'parameters' => [ + 'promotionId' => 'resource.id', + ], + ], + ], + ]), + ), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/PromotionGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/PromotionGridInterface.php new file mode 100644 index 0000000000..306ef13d31 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/PromotionGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod('createListQueryBuilder') + ->addOrderBy('createdAt', 'desc') + + ->withFields( + TwigField::create('createdAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.created_at') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('shippedAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.shipped_at') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('order', '@SyliusAdmin/shared/grid/field/order_number.html.twig') + ->setLabel('sylius.ui.order') + ->setPath('order') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1', + ], + ]), + TwigField::create('channel', '@SyliusAdmin/shared/grid/field/channel.html.twig') + ->setLabel('sylius.ui.channel') + ->setPath('order.channel'), + TwigField::create('state', '@SyliusAdmin/shared/grid/field/shipment_state.html.twig') + ->setLabel('sylius.ui.state') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + ) + ->withFilters( + SelectFilter::create('state', [ + 'sylius.ui.cancelled' => 'cancelled', + 'sylius.ui.ready' => 'ready', + 'sylius.ui.shipped' => 'shipped', + ]) + ->setLabel('sylius.ui.state'), + Filter::create('channel', 'ux_autocomplete') + ->setLabel('sylius.ui.channel') + ->setOptions([ + 'fields' => [ + 'order.channel.id', + ], + ]) + ->setFormOptions([ + 'extra_options' => [ + 'class' => $this->channelClass, + 'choice_label' => 'name', + ], + ]), + Filter::create('method', 'ux_translatable_autocomplete') + ->setLabel('sylius.ui.method') + ->setOptions([ + 'fields' => [ + 'method.id', + ], + ]) + ->setFormOptions([ + 'extra_options' => [ + 'class' => $this->shippingMethodClass, + 'translation_fields' => ['name'], + 'choice_label' => 'name', + ], + ]), + ) + ->withItemActions( + Action::create('ship', 'ship_with_tracking_code'), + ShowAction::create(), + ); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ShipmentGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ShipmentGridInterface.php new file mode 100644 index 0000000000..998d5f18a0 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ShipmentGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod('createListQueryBuilder') + ->withFields( + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code'), + TwigField::create('createdAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.creation_date') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + TwigField::create('updatedAt', '@SyliusAdmin/shared/grid/field/date.html.twig') + ->setLabel('sylius.ui.updating_date') + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + ], + ]), + ) + ->withFilters( + Filter::create('search', 'string') + ->setLabel('sylius.ui.search') + ->setOptions([ + 'fields' => [ + 'code', + 'name', + ], + ]), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ShippingCategoryGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ShippingCategoryGridInterface.php new file mode 100644 index 0000000000..48be1e0f51 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ShippingCategoryGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->setRepositoryMethod('createListQueryBuilder', [ + 'expr:service(\'sylius.context.locale\').getLocaleCode()', + ]) + ->addOrderBy('position', 'asc') + + ->withFields( + TwigField::create('position', '@SyliusUi/grid/field/position.html.twig') + ->setLabel('sylius.ui.position') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true, 'translation.name'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + StringField::create('zone') + ->setLabel('sylius.ui.zone'), + TwigField::create('enabled', '@SyliusAdmin/shared/grid/field/boolean.html.twig') + ->setLabel('sylius.ui.enabled') + ->setSortable(true), + ) + + ->withFilters( + StringFilter::create('search', ['code', 'translation.name']) + ->setLabel('sylius.ui.search'), + Filter::create('enabled', 'boolean') + ->setLabel('sylius.ui.enabled'), + ExistsFilter::create('archival', 'archivedAt') + ->setLabel('sylius.ui.archival') + ->setDefaultValue(false), + ) + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + Action::create('archive', 'archive'), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ShippingMethodGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ShippingMethodGridInterface.php new file mode 100644 index 0000000000..fb31bb9286 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ShippingMethodGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('nameAndDescription', 'asc') + + ->withFields( + TwigField::create('nameAndDescription', '@SyliusUi/grid/field/name_and_description.html.twig') + ->setLabel('sylius.ui.name') + ->setPath('.') + ->setSortable(true, 'name'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + ) + + ->withFilters( + StringFilter::create('search', ['code', 'name']) + ->setLabel('sylius.ui.search') + ->addFormOption('type', StringFilter::TYPE_CONTAINS), + ) + + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/TaxCategoryGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/TaxCategoryGridInterface.php new file mode 100644 index 0000000000..43aa476e24 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/TaxCategoryGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('name', 'asc') + + ->withFields( + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name') + ->setSortable(true), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code') + ->setSortable(true), + StringField::create('zone') + ->setLabel('sylius.ui.zone'), + StringField::create('category') + ->setLabel('sylius.ui.category'), + TwigField::create('amount', '@SyliusUi/grid/field/percent.html.twig') + ->setLabel('sylius.ui.amount') + ->setSortable(true), + ) + ->withFilters( + Filter::create('startDate', 'date') + ->setLabel('sylius.ui.start_date') + ->setOptions([ + 'inclusive_to' => true, + ]), + DateFilter::create('endDate') + ->setLabel('sylius.ui.end_date') + ->setOptions([ + 'inclusive_to' => true, + ]), + StringFilter::create('search', ['code', 'name']) + ->setLabel('sylius.ui.search'), + ) + + ->withMainActions( + CreateAction::create(), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/TaxRateGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/TaxRateGridInterface.php new file mode 100644 index 0000000000..439118ac61 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/TaxRateGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createListQueryBuilder') + ->setLimits([10, 25, 50]) + + ->withFilters( + StringFilter::create('code', ['code']), + StringFilter::create('name', ['translation.name']), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/TaxonGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/TaxonGridInterface.php new file mode 100644 index 0000000000..e4755250ab --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/TaxonGridInterface.php @@ -0,0 +1,24 @@ +setLimits([10, 25, 50]) + ->addOrderBy('priority', 'desc') + + ->withFields( + TwigField::create('priority', '@SyliusAdmin/zone/grid/field/priority.html.twig') + ->setLabel('sylius.ui.priority') + ->setSortable(true) + ->withOptions([ + 'vars' => [ + 'th_class' => 'w-1 text-center', + 'td_class' => 'text-center', + ], + ]), + TwigField::create('name', '@SyliusAdmin/shared/grid/field/name.html.twig') + ->setLabel('sylius.ui.name'), + TwigField::create('code', '@SyliusAdmin/shared/grid/field/code.html.twig') + ->setLabel('sylius.ui.code'), + TwigField::create('type', '@SyliusAdmin/zone/grid/field/type.html.twig') + ->setLabel('sylius.ui.type'), + ) + + ->withFilters( + StringFilter::create('name') + ->setLabel('sylius.ui.name'), + StringFilter::create('code') + ->setLabel('sylius.ui.code'), + ) + + ->withMainActions( + Action::create('create', 'links') + ->setLabel('sylius.ui.create') + ->setOptions([ + 'class' => 'primary', + 'icon' => 'tabler:plus', + 'header' => [ + 'icon' => 'tabler:cube', + 'label' => 'sylius.ui.type', + ], + 'links' => [ + 'country' => [ + 'label' => 'sylius.ui.zone_consisting_of_countries', + 'icon' => 'tabler:plus', + 'route' => 'sylius_admin_zone_create', + 'parameters' => [ + 'type' => 'country', + ], + ], + 'province' => [ + 'label' => 'sylius.ui.zone_consisting_of_provinces', + 'icon' => 'tabler:plus', + 'route' => 'sylius_admin_zone_create', + 'parameters' => [ + 'type' => 'province', + ], + ], + 'zone' => [ + 'label' => 'sylius.ui.zone_consisting_of_other_zones', + 'icon' => 'tabler:plus', + 'route' => 'sylius_admin_zone_create', + 'parameters' => [ + 'type' => 'zone', + ], + ], + ], + ]), + ) + ->withItemActions( + UpdateAction::create(), + DeleteAction::create(), + ) + ->withBulkActions( + DeleteAction::create(), + ) + ; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Grid/ZoneGridInterface.php b/src/Sylius/Bundle/AdminBundle/Grid/ZoneGridInterface.php new file mode 100644 index 0000000000..edaa5f0ad3 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Grid/ZoneGridInterface.php @@ -0,0 +1,24 @@ +set('sylius_admin.grid_filter.ux_translatable_autocomplete', EntityFilter::class) ->tag('sylius.grid_filter', ['type' => 'ux_translatable_autocomplete', 'form_type' => UxTranslatableAutocompleteFilterType::class]) ; + + $services->set('sylius_admin.grid.admin_user', AdminUserGrid::class) + ->tag('sylius.invokable_grid') + ; + + $services->alias(AdminUserGridInterface::class, 'sylius_admin.grid.admin_user'); + + $services->set('sylius_admin.grid.catalog_promotion', CatalogPromotionGrid::class) + ->args([ + '%sylius.model.channel.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(CatalogPromotionGridInterface::class, 'sylius_admin.grid.catalog_promotion'); + + $services->set('sylius_admin.grid.channel', ChannelGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ChannelGridInterface::class, 'sylius_admin.grid.channel'); + + $services->set('sylius_admin.grid.currency', CurrencyGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(CurrencyGridInterface::class, 'sylius_admin.grid.currency'); + + $services->set('sylius_admin.grid.customer', CustomerGrid::class) + ->args([ + '%sylius.model.customer_group.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(CustomerGridInterface::class, 'sylius_admin.grid.customer'); + + $services->set('sylius_admin.grid.customer_group', CustomerGroupGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(CustomerGroupGridInterface::class, 'sylius_admin.grid.customer_group'); + + $services->set('sylius_admin.grid.locale', LocaleGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(LocaleGridInterface::class, 'sylius_admin.grid.locale'); + + $services->set('sylius_admin.grid.inventory', InventoryGrid::class) + ->args([ + '%sylius.model.product.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(InventoryGridInterface::class, 'sylius_admin.grid.inventory'); + + $services->set('sylius_admin.grid.payment_method', PaymentMethodGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(PaymentMethodGridInterface::class, 'sylius_admin.grid.payment_method'); + + $services->set('sylius_admin.grid.product_association_type', ProductAssociationTypeGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductAssociationTypeGridInterface::class, 'sylius_admin.grid.product_association_type'); + + $services->set('syllabus_admin.grid.taxon', TaxonGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(TaxonGridInterface::class, 'sylius_admin.grid.taxon'); + + $services->set('sylius_admin.grid.product_taxon', ProductTaxonGrid::class) + ->args([ + '%sylius.model.channel.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductTaxonGridInterface::class, 'sylius_admin.grid.product_taxon'); + + $services->set('sylius_admin.grid.product', ProductGrid::class) + ->args([ + '%sylius.model.product.class%', + '%sylius.model.channel.class%', + '%sylius.model.taxon.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductGridInterface::class, 'sylius_admin.grid.product'); + + $services->set('sylius_admin.grid.product_attribute', ProductAttributeGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductAttributeGridInterface::class, 'sylius_admin.grid.product_attribute'); + + $services->set('sylius_admin.grid.product_variant', ProductVariantGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductVariantGridInterface::class, 'sylius_admin.grid.product_variant'); + + $services->set('sylius_admin.grid.product_variant_with_catalog_promotion', ProductVariantWithCatalogPromotionGrid::class) + ->args([ + '%locale%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductVariantWithCatalogPromotionGridInterface::class, 'sylius_admin.grid.product_variant_with_catalog_promotion'); + + $services->set('sylius_admin.grid.country', CountryGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(CountryGridInterface::class, 'sylius_admin.grid.country'); + + $services->set('sylius_admin.grid.shipment', ShipmentGrid::class) + ->args([ + '%sylius.model.channel.class%', + '%sylius.model.shipping_method.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(ShipmentGridInterface::class, 'sylius_admin.grid.shipment'); + + $services->set('sylius_admin.grid.shipping_category', ShippingCategoryGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ShippingCategoryGridInterface::class, 'sylius_admin.grid.shipping_category'); + + $services->set('sylius_admin.grid.shipping_method', ShippingMethodGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ShippingMethodGridInterface::class, 'sylius_admin.grid.shipping_method'); + + $services->set('sylius_admin.grid.tax_category', TaxCategoryGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(TaxCategoryGridInterface::class, 'sylius_admin.grid.tax_category'); + + $services->set('sylius_admin.grid.tax_rate', TaxRateGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(TaxRateGridInterface::class, 'sylius_admin.grid.tax_rate'); + + $services->set('sylius_admin.grid.promotion_coupon', PromotionCouponGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(PromotionCouponGridInterface::class, 'sylius_admin.grid.promotion_coupon'); + + $services->set('sylius_admin.grid.promotion', PromotionGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(PromotionGridInterface::class, 'sylius_admin.grid.promotion'); + + $services->set('sylius_admin.grid.zone', ZoneGrid::class) + ->tag('sylius.invokable_grid') + ; + + $services->alias(ZoneGridInterface::class, 'sylius_admin.grid.zone'); + + $services->set('sylius_admin.grid.payment', PaymentGrid::class) + ->args([ + '%sylius.model.channel.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(PaymentGridInterface::class, 'sylius_admin.grid.payment'); + + $services->set('sylius_admin.grid.customer_order', CustomerOrderGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(CustomerOrderGridInterface::class, 'sylius_admin.grid.customer_order'); + + $services->set('sylius_admin.grid.order', OrderGrid::class) + ->args([ + '%sylius.model.channel.class%', + '%sylius.model.customer.class%', + '%sylius.model.product.class%', + '%sylius.model.product_variant.class%', + '%sylius.model.shipping_method.class%', + ]) + ->tag('sylius.invokable_grid') + ; + + $services->alias(OrderGridInterface::class, 'sylius_admin.grid.order'); + + $services->set('sylius_admin.grid.exchange_rate', ExchangeRateGrid::class) + ->args([ + '%sylius.model.currency.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(ExchangeRateGridInterface::class, 'sylius_admin.grid.exchange_rate'); + + $services->set('sylius_admin.grid.product_option', ProductOptionGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductOptionGridInterface::class, 'sylius_admin.grid.product_option'); + + $services->set('sylius_admin.grid.product_review', ProductReviewGrid::class) + ->args([ + '%sylius.model.product.class%', + ]) + ->tag('sylius.invokable_grid') + ; + $services->alias(ProductReviewGridInterface::class, 'sylius_admin.grid.product_review'); + + $services->set('sylius_admin.grid.channel_pricing_log_entry', ChannelPricingLogEntryGrid::class) + ->tag('sylius.invokable_grid') + ; + $services->alias(ChannelPricingLogEntryGridInterface::class, 'sylius_admin.grid.channel_pricing_log_entry'); + + $services->set('sylius_admin.grid.payment_request', PaymentRequestGrid::class) + ->args([ + '%sylius.model.payment_method.class%', + ]) + ->tag('sylius.invokable_grid') + ; + + $services->alias(PaymentRequestGridInterface::class, 'sylius_admin.grid.payment_request'); }; diff --git a/src/Sylius/Bundle/CoreBundle/DependencyInjection/Configuration.php b/src/Sylius/Bundle/CoreBundle/DependencyInjection/Configuration.php index 4a39cec7b5..8a0dc6b98d 100644 --- a/src/Sylius/Bundle/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Sylius/Bundle/CoreBundle/DependencyInjection/Configuration.php @@ -146,6 +146,20 @@ final class Configuration implements ConfigurationInterface ->prototype('scalar')->end() ->defaultValue([PaymentInterface::STATE_NEW, PaymentInterface::STATE_CART]) ->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('grid') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('use_legacy_config')->defaultNull()->end() + ->arrayNode('grids') + ->useAttributeAsKey('grid_code') + ->arrayPrototype() + ->children() + ->booleanNode('use_legacy_config')->defaultTrue()->end() + ->end() ->end() ->end() ->end() diff --git a/src/Sylius/Bundle/CoreBundle/DependencyInjection/SyliusCoreExtension.php b/src/Sylius/Bundle/CoreBundle/DependencyInjection/SyliusCoreExtension.php index f4db1a6cdd..53c7cb0127 100644 --- a/src/Sylius/Bundle/CoreBundle/DependencyInjection/SyliusCoreExtension.php +++ b/src/Sylius/Bundle/CoreBundle/DependencyInjection/SyliusCoreExtension.php @@ -79,6 +79,7 @@ final class SyliusCoreExtension extends AbstractResourceExtension implements Pre $container->setParameter('sylius_core.max_int_value', $config['max_int_value']); $container->setParameter('sylius_core.allowed_images_mime_types', $config['allowed_images_mime_types']); $container->setParameter('sylius_core.checkout.payment.allowed_states', $config['checkout']['payment']['allowed_states']); + $container->setParameter('sylius_core.grids_configuration', $config['grid']); /** @var string $env */ $env = $container->getParameter('kernel.environment'); diff --git a/src/Sylius/Bundle/CoreBundle/Grid/Provider/OverrideGridProvider.php b/src/Sylius/Bundle/CoreBundle/Grid/Provider/OverrideGridProvider.php new file mode 100644 index 0000000000..0871afea8f --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Grid/Provider/OverrideGridProvider.php @@ -0,0 +1,48 @@ + $configuration */ + public function __construct( + private array $configuration, + private GridProviderInterface $chainGridProvider, + private GridProviderInterface $arrayGridProvider, + ) { + } + + public function get(string $code): Grid + { + $useYamlGrid = $this->configuration['use_legacy_config'] ?? $this->configuration['grids'][$code]['use_legacy_config'] ?? true; + if ($useYamlGrid) { + try { + return $this->arrayGridProvider->get($code); + } catch(UndefinedGridException) { + } + } + + return $this->chainGridProvider->get($code); + } +} diff --git a/src/Sylius/Bundle/CoreBundle/Installer/Requirement/ExtensionsRequirements.php b/src/Sylius/Bundle/CoreBundle/Installer/Requirement/ExtensionsRequirements.php index b8a9170345..bf9e5c59e7 100644 --- a/src/Sylius/Bundle/CoreBundle/Installer/Requirement/ExtensionsRequirements.php +++ b/src/Sylius/Bundle/CoreBundle/Installer/Requirement/ExtensionsRequirements.php @@ -123,7 +123,7 @@ final class ExtensionsRequirements extends RequirementCollection if (extension_loaded('intl')) { if (defined('INTL_ICU_VERSION')) { - $version = \INTL_ICU_VERSION; + $version = INTL_ICU_VERSION; } else { $reflector = new ReflectionExtension('intl'); diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services/providers.php b/src/Sylius/Bundle/CoreBundle/Resources/config/services/providers.php index 4d828078a2..ac770c2d35 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/services/providers.php +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services/providers.php @@ -13,6 +13,7 @@ declare(strict_types=1); namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Sylius\Bundle\CoreBundle\Grid\Provider\OverrideGridProvider; use Sylius\Bundle\CoreBundle\Provider\ChannelBasedDefaultTaxZoneProvider; use Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProvider; use Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProviderInterface; @@ -136,4 +137,13 @@ return static function (ContainerConfigurator $container) { ->args([service('sylius.repository.order')]) ->tag('sylius.statistics.orders_count_provider', ['type' => 'year']) ; + + $services->set('sylius.provider.override_grid_provider', OverrideGridProvider::class) + ->decorate('sylius.grid.chain_provider') + ->args([ + '%sylius_core.grids_configuration%', + service('sylius.provider.override_grid_provider.inner'), + service('sylius.grid.array_grid_provider'), + ]) + ; }; diff --git a/src/Sylius/Bundle/CoreBundle/composer.json b/src/Sylius/Bundle/CoreBundle/composer.json index f108d02dfc..d41e25f652 100644 --- a/src/Sylius/Bundle/CoreBundle/composer.json +++ b/src/Sylius/Bundle/CoreBundle/composer.json @@ -42,7 +42,7 @@ "sylius/currency-bundle": "^2.0", "sylius/customer-bundle": "^2.0", "sylius/fixtures-bundle": "^1.9", - "sylius/grid-bundle": "^1.13", + "sylius/grid-bundle": "^1.16@alpha", "sylius/inventory-bundle": "^2.0", "sylius/locale-bundle": "^2.0", "sylius/mailer-bundle": "^2.1", diff --git a/src/Sylius/Bundle/CoreBundle/tests/Grid/Provider/OverrideGridProviderTest.php b/src/Sylius/Bundle/CoreBundle/tests/Grid/Provider/OverrideGridProviderTest.php new file mode 100644 index 0000000000..d10059a489 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/tests/Grid/Provider/OverrideGridProviderTest.php @@ -0,0 +1,140 @@ +chainProvider = $this->createMock(GridProviderInterface::class); + $this->arrayProvider = $this->createMock(GridProviderInterface::class); + + $this->gridDefinition = Grid::fromCodeAndDriverConfiguration('app_book', '', []); + } + + public function test_use_override_for_grid(): void + { + $this->arrayProvider + ->expects($this->never()) + ->method('get') + ; + $this->chainProvider + ->expects($this->once()) + ->method('get') + ->with('app_book') + ->willReturn($this->gridDefinition) + ; + + $configurableProvider = new OverrideGridProvider( + [ + 'grids' => [ + 'app_book' => ['use_legacy_config' => false], + ], + ], + $this->chainProvider, + $this->arrayProvider, + ); + + $gridDefinition = $configurableProvider->get('app_book'); + + self::assertEquals($this->gridDefinition, $gridDefinition); + } + + public function test_overriding_for_all_grids(): void + { + $this->arrayProvider + ->expects($this->never()) + ->method('get') + ; + $this->chainProvider + ->expects($this->once()) + ->method('get') + ->with('app_book') + ->willReturn($this->gridDefinition) + ; + + $configurableProvider = new OverrideGridProvider( + ['use_legacy_config' => false], + $this->chainProvider, + $this->arrayProvider, + ); + + $gridDefinition = $configurableProvider->get('app_book'); + + self::assertEquals($this->gridDefinition, $gridDefinition); + } + + public function test_using_the_array_provider_by_default(): void + { + $this->arrayProvider + ->expects($this->once()) + ->method('get') + ->with('app_book') + ->willReturn($this->gridDefinition) + ; + $this->chainProvider + ->expects($this->never()) + ->method('get') + ; + + $configurableProvider = new OverrideGridProvider( + [], + $this->chainProvider, + $this->arrayProvider, + ); + + $gridDefinition = $configurableProvider->get('app_book'); + + self::assertEquals($this->gridDefinition, $gridDefinition); + } + + public function test_fallback_if_no_yaml_grid_is_configured(): void + { + $this->arrayProvider + ->expects($this->once()) + ->method('get') + ->with('app_book') + ->willThrowException(new UndefinedGridException('app_book')) + ; + $this->chainProvider + ->expects($this->once()) + ->method('get') + ->with('app_book') + ->willReturn($this->gridDefinition) + ; + + $configurableProvider = new OverrideGridProvider( + [], + $this->chainProvider, + $this->arrayProvider, + ); + + $gridDefinition = $configurableProvider->get('app_book'); + + self::assertEquals($this->gridDefinition, $gridDefinition); + } +} diff --git a/src/Sylius/Bundle/ShopBundle/Grid/Account/OrderGrid.php b/src/Sylius/Bundle/ShopBundle/Grid/Account/OrderGrid.php new file mode 100644 index 0000000000..f4693739f3 --- /dev/null +++ b/src/Sylius/Bundle/ShopBundle/Grid/Account/OrderGrid.php @@ -0,0 +1,78 @@ +setRepositoryMethod('createByCustomerAndChannelIdQueryBuilder', [ + "expr:service('sylius.context.customer').getCustomer().getId()", + "expr:service('sylius.context.channel').getChannel().getId()", + ]) + ->orderBy('checkoutCompletedAt', 'desc') + ->setLimits([10, 25, 50]) + ->withFields( + TwigField::create('number', '@SyliusShop/account/order/grid/field/number.html.twig') + ->setLabel('sylius.ui.number') + ->setSortable(true), + DateTimeField::create('checkoutCompletedAt', 'm/d/Y') + ->setLabel('sylius.ui.date'), + TwigField::create('shippingAddress', '@SyliusShop/account/order/grid/field/address.html.twig') + ->setLabel('sylius.ui.ship_to'), + TwigField::create('total', '@SyliusShop/account/order/grid/field/total.html.twig') + ->setLabel('sylius.ui.total') + ->setPath('.') + ->setSortable(true, 'total'), + TwigField::create('state', '@SyliusUi/grid/field/label.html.twig') + ->setLabel('sylius.ui.state') + ->setSortable(true) + ->addOptions([ + 'vars' => [ + 'labels' => '@SyliusShop/account/order/label/state', + ], + ]), + ) + ->withItemActions( + Action::create('show', 'shop_show') + ->setLabel('sylius.ui.show') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_shop_account_order_show', + 'parameters' => [ + 'number' => 'resource.number', + ], + ], + ]), + Action::create('pay', 'shop_pay') + ->setLabel('sylius.ui.pay') + ->setOptions([ + 'link' => [ + 'route' => 'sylius_shop_order_show', + 'parameters' => [ + 'tokenValue' => 'resource.tokenValue', + ], + ], + ]), + ); + } +} diff --git a/src/Sylius/Bundle/ShopBundle/Grid/Account/OrderGridInterface.php b/src/Sylius/Bundle/ShopBundle/Grid/Account/OrderGridInterface.php new file mode 100644 index 0000000000..5bbf0ea423 --- /dev/null +++ b/src/Sylius/Bundle/ShopBundle/Grid/Account/OrderGridInterface.php @@ -0,0 +1,24 @@ +setRepositoryMethod('createShopListQueryBuilder', [ + 'channel' => "expr:service('sylius.context.channel').getChannel()", + 'taxon' => "expr:notFoundOnNull(service('sylius.repository.taxon').findOneBySlug(\$slug, service('sylius.context.locale').getLocaleCode()))", + 'locale' => "expr:service('sylius.context.locale').getLocaleCode()", + 'sorting' => "expr:service('request_stack').getCurrentRequest().query.all('sorting')", + 'includeAllDescendants' => "expr:parameter('sylius_shop.product_grid.include_all_descendants')", + ]) + ->orderBy('position', 'asc') + ->setLimits([ + 9, + 18, + 27, + ]) + ->withFields( + DateTimeField::create('createdAt') + ->setSortable(true), + StringField::create('position') + ->setSortable(true, 'productTaxon.position'), + StringField::create('name') + ->setSortable(true, 'translation.name'), + Field::create('price', 'int') + ->setSortable(true, 'channelPricing.price'), + ) + ->withFilters( + Filter::create('search', 'shop_string') + ->setLabel(false) + ->addOption('fields', ['translation.name']) + ->addFormOption('type', 'contains'), + ); + } +} diff --git a/src/Sylius/Bundle/ShopBundle/Grid/ProductGridInterface.php b/src/Sylius/Bundle/ShopBundle/Grid/ProductGridInterface.php new file mode 100644 index 0000000000..12906bcb1a --- /dev/null +++ b/src/Sylius/Bundle/ShopBundle/Grid/ProductGridInterface.php @@ -0,0 +1,24 @@ +services(); + + $services->set('sylius_shop.grid.account.order', \Sylius\Bundle\ShopBundle\Grid\Account\OrderGrid::class) + ->tag('sylius.invokable_grid') + ; + + $services->alias(\Sylius\Bundle\ShopBundle\Grid\Account\OrderGridInterface::class, 'sylius_shop.grid.account.order'); + + $services->set('sylius_shop.grid.product', \Sylius\Bundle\ShopBundle\Grid\ProductGrid::class) + ->tag('sylius.invokable_grid') + ; + + $services->alias(\Sylius\Bundle\ShopBundle\Grid\ProductGridInterface::class, 'sylius_shop.grid.product'); +}; diff --git a/src/Sylius/Bundle/UiBundle/composer.json b/src/Sylius/Bundle/UiBundle/composer.json index 07a40b37b9..7f35c7da9c 100644 --- a/src/Sylius/Bundle/UiBundle/composer.json +++ b/src/Sylius/Bundle/UiBundle/composer.json @@ -33,7 +33,7 @@ "knplabs/knp-menu": "^3.5", "knplabs/knp-menu-bundle": "^3.4", "laminas/laminas-stdlib": "^3.19", - "sylius/grid-bundle": "^1.13", + "sylius/grid-bundle": "^1.16@alpha", "sylius/twig-extra": "^0.9", "symfony/config": "^6.4 || ^7.4", "symfony/expression-language": "^6.4 || ^7.4", diff --git a/symfony.lock b/symfony.lock index 094b82e396..756bbf8ef6 100644 --- a/symfony.lock +++ b/symfony.lock @@ -52,18 +52,6 @@ "dmore/chrome-mink-driver": { "version": "2.7.0" }, - "doctrine/annotations": { - "version": "1.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "1.0", - "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457" - }, - "files": [ - "config/routes/annotations.yaml" - ] - }, "doctrine/cache": { "version": "1.10.2" }, @@ -200,9 +188,6 @@ "knplabs/knp-menu-bundle": { "version": "v3.0.0" }, - "laminas/laminas-code": { - "version": "3.5.1" - }, "laminas/laminas-stdlib": { "version": "3.3.1" }, diff --git a/tests/Grid/OverridenGridsTest.php b/tests/Grid/OverridenGridsTest.php new file mode 100644 index 0000000000..4d3bc64a56 --- /dev/null +++ b/tests/Grid/OverridenGridsTest.php @@ -0,0 +1,224 @@ +get(ArrayGridProvider::class); + $serviceProvider = $container->get(ServiceGridProvider::class); + + $serviceVersion = $serviceProvider->get($gridName); + $yamlVersion = $arrayProvider->get($gridName); + + $this->prefillingRepository($yamlVersion); + $this->prefillingGridFields($yamlVersion); + $this->prefillingGridActions($yamlVersion); + + $this->assertGridSame($yamlVersion, $serviceVersion); + } + + public static function dataProviderGrids(): \Generator + { + $gridConfiguration = self::getContainer()->getParameter('sylius.grids_definitions'); + self::assertNotEmpty($gridConfiguration, 'No grid configuration found'); + + foreach (array_keys($gridConfiguration) as $gridName) { + // This deprecated grid will not be migrated + if ($gridName === 'sylius_admin_address_log_entry') { + continue; + } + yield $gridName => [$gridName]; + } + + self::ensureKernelShutdown(); + } + + /** + * Prefilling the default values for datetime fields as they might not be set in YAML configuration + */ + private function prefillingGridFields(Grid $grid): void + { + foreach ($grid->getFields() as $field) { + // Prefilling the default values for datetime fields as they might not be set in YAML configuration + if ($field->getType() === 'datetime') { + $options = [ + 'format' => 'Y-m-d H:i:s', + 'timezone' => null, + ...$field->getOptions(), + ]; + $field->setOptions($options); + } + } + } + + /** + * Prefilling the default values on Grid definitions. + * PHP Configuration add a default label, not set in YAML configuration + */ + private function prefillingGridActions(Grid $grid): void + { + if ($grid->hasActionGroup('main')) { + foreach ($grid->getActions('main') as $action) { + if ('create' === $action->getType() && null === $action->getLabel()) { + $action->setLabel('sylius.ui.create'); + } + } + } + + if ($grid->hasActionGroup('item')) { + foreach ($grid->getActions('item') as $action) { + if ('show' === $action->getType() && null === $action->getLabel()) { + $action->setLabel('sylius.ui.show'); + } + + if ('update' === $action->getType() && null === $action->getLabel()) { + $action->setLabel('sylius.ui.edit'); + } + + if ('delete' === $action->getType() && null === $action->getLabel()) { + $action->setLabel('sylius.ui.delete'); + } + } + } + + if ($grid->hasActionGroup('bulk')) { + foreach ($grid->getActions('bulk') as $action) { + if ('delete' === $action->getType() && null === $action->getLabel()) { + $action->setLabel('sylius.ui.delete'); + } + } + } + } + + private function prefillingRepository(Grid $grid): void + { + $config = $grid->getDriverConfiguration(); + if (!array_key_exists('repository', $config)) { + return; + } + + if (!array_key_exists('arguments', $config['repository'])) { + $config['repository']['arguments'] = []; + } + + $grid->setDriverConfiguration($config); + } + + public function assertGridSame(Grid $yamlGrid, Grid $serviceGrid): void + { + self::assertSame($yamlGrid->getCode(), $serviceGrid->getCode()); + self::assertSame($yamlGrid->getDriver(), $serviceGrid->getDriver()); + self::assertSameWithIgnoredOrder($yamlGrid->getDriverConfiguration(), $serviceGrid->getDriverConfiguration()); + self::assertSame($yamlGrid->getProvider(), $serviceGrid->getProvider()); + self::assertSame($yamlGrid->getSorting(), $serviceGrid->getSorting()); + self::assertSame($yamlGrid->getLimits(), $serviceGrid->getLimits()); + self::assertSame($yamlGrid->getSorting(), $serviceGrid->getSorting()); + + $serviceFields = $serviceGrid->getFields(); + $this->assertSame( + array_keys($serviceGrid->getFields()), + array_keys($yamlGrid->getFields()), + 'Field keys in grid do not match', + ); + foreach ($yamlGrid->getFields() as $fieldName => $yamlField) { + $this->assertArrayHasKey($fieldName, $serviceFields); + $this->assertEquals($yamlField, $serviceFields[$fieldName]); + + $serviceField = $serviceFields[$fieldName]; + $this->assertSame($yamlField->getName(), $serviceField->getName(), 'Name of field '.$fieldName.' does not match'); + $this->assertSame($yamlField->getType(), $serviceField->getType(), 'Type of field '.$fieldName.' does not match'); + $this->assertSame($yamlField->getPath(), $serviceField->getPath(), 'Path of field '.$fieldName.' does not match'); + $this->assertSame($yamlField->getLabel(), $serviceField->getLabel(), 'Label of field '.$fieldName.' does not match'); + $this->assertSame($yamlField->isEnabled(), $serviceField->isEnabled(), 'Enabled state of field '.$fieldName.' does not match'); + $this->assertSame($yamlField->isSortable(), $serviceField->isSortable(), 'Sortable state of field '.$fieldName.' does not match'); + $this->assertSame($yamlField->getSortable(), $serviceField->getSortable(), 'Sortable path of field '.$fieldName.' does not match'); + $this->assertSameWithIgnoredOrder($yamlField->getOptions(), $serviceField->getOptions(), 'Options of field '.$fieldName.' do not match'); + $this->assertSame($yamlField->getPosition(), $serviceField->getPosition(), 'Position of field '.$fieldName.' does not match'); + } + + $serviceFilters = $serviceGrid->getFilters(); + $this->assertSame( + array_keys($serviceGrid->getFilters()), + array_keys($yamlGrid->getFilters()), + 'Filter keys in grid do not match', + ); + foreach ($yamlGrid->getFilters() as $filterName => $yamlFilter) { + $this->assertArrayHasKey($filterName, $serviceFilters); + + $serviceFilter = $serviceFilters[$filterName]; + $this->assertSame($yamlFilter->isEnabled(), $serviceFilter->isEnabled(), 'Enabled state of filter '.$filterName. ' does not match'); + $this->assertSame($yamlFilter->getName(), $serviceFilter->getName(), 'Name of filter '.$filterName. ' does not match'); + $this->assertSame($yamlFilter->getType(), $serviceFilter->getType(), 'Type of filter '.$filterName. ' does not match'); + $this->assertSame($yamlFilter->getLabel(), $serviceFilter->getLabel(), 'Label of filter '.$filterName. ' does not match'); + $this->assertSame($yamlFilter->getTemplate(), $serviceFilter->getTemplate(), 'Template of filter '.$filterName. ' does not match'); + $this->assertSameWithIgnoredOrder($yamlFilter->getOptions(), $serviceFilter->getOptions(), 'Options of filter '.$filterName. ' does not match'); + $this->assertSameWithIgnoredOrder($yamlFilter->getFormOptions(), $serviceFilter->getFormOptions(), 'Form Options of filter '.$filterName. ' does not match'); + $this->assertSame($yamlFilter->getCriteria(), $serviceFilter->getCriteria(), 'Criteria of filter '.$filterName. ' does not match'); + $this->assertSame($yamlFilter->getPosition(), $serviceFilter->getPosition(), 'Position of filter '.$filterName. ' does not match'); + } + + // Assert actions are the same + $this->assertSame( + array_keys($serviceGrid->getActionGroups()), + array_keys($yamlGrid->getActionGroups()), + 'Action keys in action groups do not match', + ); + $serviceActions = $serviceGrid->getActionGroups(); + foreach ($yamlGrid->getActionGroups() as $actionGroupName => $yamlActionGroup) { + $this->assertArrayHasKey($actionGroupName, $serviceActions); + $this->assertEquals($yamlActionGroup, $serviceActions[$actionGroupName]); + $serviceActionGroup = $serviceActions[$actionGroupName]; + + $this->assertSame( + array_keys($serviceActionGroup->getActions()), + array_keys($yamlActionGroup->getActions()), + 'Action keys in action group '.$actionGroupName.' do not match', + ); + foreach ($serviceActionGroup->getActions() as $name => $expectedAction) { + $actualAction = $yamlActionGroup->getAction($name); + + $this->assertSame($expectedAction->getName(), $actualAction->getName(), 'Name of action '.$actionGroupName.' does not match'); + $this->assertSame($expectedAction->getType(), $actualAction->getType(), 'Type of action '.$actionGroupName.' does not match'); + $this->assertSame($expectedAction->getLabel(), $actualAction->getLabel(), 'Label of action '.$actionGroupName.' does not match'); + $this->assertSame($expectedAction->isEnabled(), $actualAction->isEnabled(), 'Enabled state of action '.$actionGroupName.' does not match'); + $this->assertSame($expectedAction->getTemplate(), $actualAction->getTemplate(), 'Template of action '.$actionGroupName.' does not match'); + $this->assertSame($expectedAction->getIcon(), $actualAction->getIcon(), 'Icon of action '.$actionGroupName.' does not match'); + $this->assertSameWithIgnoredOrder($expectedAction->getOptions(), $actualAction->getOptions(), 'Options of action '.$actionGroupName.' do not match'); + $this->assertSame($expectedAction->getPosition(), $actualAction->getPosition(), 'Position of action '.$actionGroupName.' does not match'); + } + } + } + + private function assertSameWithIgnoredOrder(array $expected, array $actual): void + { + ksort($expected); + ksort($actual); + + $this->assertSame($expected, $actual); + } + +}