From 1e2a9bc7051e14c65eb44b392dba11f766b95d33 Mon Sep 17 00:00:00 2001 From: Nikita Kiselev Date: Fri, 3 Oct 2025 00:26:13 +0300 Subject: [PATCH] feat: add filters to mainpage --- Makefile | 3 + docker-compose.yaml | 1 + .../BotTokenConfiguratorException.php | 0 .../bastion/Handlers/SettingsHandler.php | 0 .../bastion/Services/BotTokenConfigurator.php | 0 .../upload/oc_telegram_shop/composer.json | 4 +- .../upload/oc_telegram_shop/composer.lock | 191 +++- .../CriteriaBuilder/CriteriaBuilder.php | 40 + .../framework/CriteriaBuilder/Criterion.php | 15 + .../Exceptions/CriteriaBuilderException.php | 9 + .../CriteriaBuilder/RuleSerializer.php | 58 ++ .../CriteriaBuilder/Rules/BaseRule.php | 105 ++ .../CriteriaBuilder/RulesRegistry.php | 63 ++ .../Exceptions/EntityNotFoundException.php | 0 .../framework/Logger/LoggerInterface.php | 0 .../framework/Logger/OpenCartLogAdapter.php | 0 .../framework/QueryBuilder/Builder.php | 8 +- .../QueryBuilder/Grammars/Grammar.php | 19 +- .../Telegram/Commands/ChatIdCommand.php | 0 .../Telegram/Commands/TelegramCommand.php | 0 .../Contracts/TelegramCommandInterface.php | 0 .../framework/Telegram/Enums/ChatAction.php | 0 .../TelegramCommandNotFoundException.php | 0 .../Telegram/TelegramBotStateManager.php | 0 .../Telegram/TelegramCommandsRegistry.php | 0 .../framework/Validator/ErrorBag.php | 0 .../ValidationRuleNotFoundException.php | 0 .../Validator/ValidationRules/Email.php | 0 .../Validator/ValidationRules/Required.php | 0 .../ValidationRuleInterface.php | 0 .../Validator/ValidatorInterface.php | 0 .../Validator/ValidatorServiceProvider.php | 0 .../framework/Validator/translations/ru.php | 0 .../FacetSearch/Filters/ProductAttribute.php | 70 ++ .../FacetSearch/Filters/ProductCategories.php | 61 ++ .../Filters/ProductForMainPage.php | 51 + .../Filters/ProductManufacturer.php | 48 + .../src/FacetSearch/Filters/ProductModel.php | 45 + .../src/FacetSearch/Filters/ProductPrice.php | 145 +++ .../FacetSearch/Filters/ProductQuantity.php | 68 ++ .../src/FacetSearch/Filters/ProductStatus.php | 35 + .../src/Handlers/ProductsHandler.php | 23 +- .../ServiceProviders/AppServiceProvider.php | 29 + .../src/Services/ProductsService.php | 55 +- .../src/Telegram/LinkCommand.php | 0 .../tests/Helpers/DatabaseHelpers.php | 60 ++ .../ExampleDatabaseConnection.php | 7 + .../ExampleClasses/ExampleEmailWithConfig.php | 14 + .../ExamplePersonRepository.php | 16 + .../ExampleClasses/ExamplePersonService.php | 24 + .../ExampleClasses/ExampleSmsGateway.php | 7 + .../ExampleClasses/ExampleUserService.php | 16 + .../Helpers/ExampleClasses/FilterDTO.php | 25 + .../ExampleClasses/TestClassWithMethod.php | 14 + .../tests/Helpers/OpencartUrl.php | 8 + .../tests/Telegram/TelegramServiceTest.php | 0 .../oc_telegram_shop/tests/TestCase.php | 59 +- .../oc_telegram_shop/tests/Unit/ArrTest.php | 194 ++++ .../tests/Unit/BuilderTest.php | 347 +++++++ .../tests/Unit/ContainerTest.php | 145 +++ .../tests/Unit/CriteriaBuilderTest.php | 184 ++++ .../tests/Unit/GenericCollectionTest.php | 176 ++++ .../tests/Unit/HelpersTest.php | 54 + .../tests/Unit/MySqlGrammarTest.php | 222 ++++ .../tests/Unit/RuleSerializerTest.php | 46 + .../tests/Unit/SettingsTest.php | 119 +++ .../tests/Unit/TranslatorTest.php | 31 + .../{ => Unit}/Validator/ErrorBagTest.php | 2 +- .../{ => Unit}/Validator/ValidatorTest.php | 2 +- .../product_attribute/contains/input.json | 18 + .../product_attribute/contains/output.sql | 19 + .../product_categories/contains/input.json | 21 + .../product_categories/contains/output.sql | 17 + .../not_contains/input.json | 21 + .../not_contains/output.sql | 17 + .../product_for_main_page/featured/input.json | 16 + .../product_for_main_page/featured/output.sql | 15 + .../featured/settings_override.json | 4 + .../featured_empty_products/input.json | 16 + .../featured_empty_products/output.sql | 13 + .../settings_override.json | 4 + .../product_for_main_page/latests/input.json | 16 + .../product_for_main_page/latests/output.sql | 15 + .../latests/settings_override.json | 3 + .../most_viewed/input.json | 16 + .../most_viewed/output.sql | 15 + .../most_viewed/settings_override.json | 3 + .../product_manufacturer/contains/input.json | 22 + .../product_manufacturer/contains/output.sql | 15 + .../not_contains/input.json | 22 + .../not_contains/output.sql | 15 + .../product_model/contains/input.json | 22 + .../product_model/contains/output.sql | 15 + .../product_model/equals/input.json | 19 + .../product_model/equals/output.sql | 15 + .../product_model/is_empty/input.json | 19 + .../product_model/is_empty/output.sql | 13 + .../product_model/is_not_empty/input.json | 19 + .../product_model/is_not_empty/output.sql | 13 + .../product_model/not_contains/input.json | 19 + .../product_model/not_contains/output.sql | 13 + .../product_model/not_equals/input.json | 19 + .../product_model/not_equals/output.sql | 13 + .../product_price/between/input.json | 19 + .../product_price/between/output.sql | 29 + .../product_price/between_equals/input.json | 19 + .../product_price/between_equals/output.sql | 28 + .../between_from_empty_string/input.json | 19 + .../between_from_empty_string/output.sql | 28 + .../between_from_null/input.json | 19 + .../between_from_null/output.sql | 28 + .../between_num_as_string/input.json | 19 + .../between_num_as_string/output.sql | 29 + .../product_price/equals/input.json | 19 + .../product_price/equals/output.sql | 28 + .../equals_num_as_string/input.json | 19 + .../equals_num_as_string/output.sql | 28 + .../product_price/greater/input.json | 19 + .../product_price/greater/output.sql | 28 + .../greater_or_equals/input.json | 19 + .../greater_or_equals/output.sql | 28 + .../include_discounts/input.json | 25 + .../include_discounts/output.sql | 28 + .../product_price/include_specials/input.json | 25 + .../product_price/include_specials/output.sql | 28 + .../product_price/less/input.json | 19 + .../product_price/less/output.sql | 28 + .../product_price/less_or_equals/input.json | 19 + .../product_price/less_or_equals/output.sql | 28 + .../product_price/not_equals/input.json | 19 + .../product_price/not_equals/output.sql | 28 + .../product_quantity/between/input.json | 19 + .../product_quantity/between/output.sql | 16 + .../between_with_number_as_string/input.json | 19 + .../between_with_number_as_string/output.sql | 16 + .../product_quantity/equals/input.json | 19 + .../product_quantity/equals/output.sql | 15 + .../product_quantity/greater/input.json | 19 + .../product_quantity/greater/output.sql | 15 + .../greater_or_equals/input.json | 19 + .../greater_or_equals/output.sql | 15 + .../product_quantity/less/input.json | 19 + .../product_quantity/less/output.sql | 15 + .../less_or_equals/input.json | 19 + .../less_or_equals/output.sql | 15 + .../product_quantity/not_equals/input.json | 19 + .../product_quantity/not_equals/output.sql | 15 + .../product_status/equals_false/input.json | 16 + .../product_status/equals_false/output.sql | 15 + .../product_status/equals_true/input.json | 16 + .../product_status/equals_true/output.sql | 15 + spa/index.html | 2 +- spa/package-lock.json | 978 +++++++++--------- spa/package.json | 3 +- spa/src/App.vue | 52 +- spa/src/components/BottomPanel.vue | 5 + spa/src/components/Icons/IconFunnel.vue | 7 + .../ProductFilters/Components/ForMainPage.vue | 21 + .../Components/ProductPrice.vue | 43 + spa/src/components/ProductFilters/Filters.vue | 105 ++ spa/src/components/ProductFilters/filters.js | 31 + spa/src/components/ProductsList.vue | 75 +- spa/src/router.js | 12 +- spa/src/stores/ProductFiltersStore.js | 23 + spa/src/stores/ProductsStore.js | 97 +- spa/src/translations.js | 3 + spa/src/views/Home.vue | 70 +- spa/src/views/Products.vue | 54 + 168 files changed, 5367 insertions(+), 662 deletions(-) mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Exceptions/BotTokenConfiguratorException.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/SettingsHandler.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Services/BotTokenConfigurator.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/CriteriaBuilder.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Criterion.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Exceptions/CriteriaBuilderException.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/RuleSerializer.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Rules/BaseRule.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/RulesRegistry.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Exceptions/EntityNotFoundException.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Logger/LoggerInterface.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Logger/OpenCartLogAdapter.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Commands/ChatIdCommand.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Commands/TelegramCommand.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Contracts/TelegramCommandInterface.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Enums/ChatAction.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Exceptions/TelegramCommandNotFoundException.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramBotStateManager.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramCommandsRegistry.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ErrorBag.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRuleNotFoundException.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/Email.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/Required.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/ValidationRuleInterface.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidatorInterface.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidatorServiceProvider.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/translations/ru.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductAttribute.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductCategories.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductForMainPage.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductManufacturer.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductModel.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductPrice.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductQuantity.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductStatus.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/Telegram/LinkCommand.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/DatabaseHelpers.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleDatabaseConnection.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleEmailWithConfig.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonRepository.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonService.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleSmsGateway.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleUserService.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/FilterDTO.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/TestClassWithMethod.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/OpencartUrl.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Telegram/TelegramServiceTest.php mode change 100644 => 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/TestCase.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ArrTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/BuilderTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ContainerTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/CriteriaBuilderTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/GenericCollectionTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/HelpersTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/MySqlGrammarTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/RuleSerializerTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/SettingsTest.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/TranslatorTest.php rename module/oc_telegram_shop/upload/oc_telegram_shop/tests/{ => Unit}/Validator/ErrorBagTest.php (93%) mode change 100644 => 100755 rename module/oc_telegram_shop/upload/oc_telegram_shop/tests/{ => Unit}/Validator/ValidatorTest.php (99%) mode change 100644 => 100755 create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_attribute/contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_attribute/contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_categories/contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_categories/contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_categories/not_contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_categories/not_contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/featured/input.json create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/featured/output.sql create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/featured/settings_override.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/featured_empty_products/input.json create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/featured_empty_products/output.sql create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/featured_empty_products/settings_override.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/latests/input.json create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/latests/output.sql create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/latests/settings_override.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/most_viewed/input.json create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/most_viewed/output.sql create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_for_main_page/most_viewed/settings_override.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_manufacturer/contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_manufacturer/contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_manufacturer/not_contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_manufacturer/not_contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/is_empty/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/is_empty/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/is_not_empty/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/is_not_empty/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/not_contains/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/not_contains/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/not_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_model/not_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/output.sql create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/input.json create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/output.sql create mode 100644 spa/src/components/BottomPanel.vue create mode 100644 spa/src/components/Icons/IconFunnel.vue create mode 100644 spa/src/components/ProductFilters/Components/ForMainPage.vue create mode 100644 spa/src/components/ProductFilters/Components/ProductPrice.vue create mode 100644 spa/src/components/ProductFilters/Filters.vue create mode 100644 spa/src/components/ProductFilters/filters.js create mode 100644 spa/src/stores/ProductFiltersStore.js create mode 100644 spa/src/translations.js diff --git a/Makefile b/Makefile index ee4e94d..759028b 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,9 @@ stop: start: docker compose up -d +restart: + docker compose down && docker compose up -d + ssh: docker compose exec -w /module/oc_telegram_shop/upload/oc_telegram_shop web bash diff --git a/docker-compose.yaml b/docker-compose.yaml index 25f18a7..bba1f2d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,6 +6,7 @@ services: - "./src:/web" - "./scripts:/scripts" - "./module:/module" + - "/Users/nikitakiselev/code/msvlad.com/image/catalog/products:/web/upload/image/catalog/products" ports: - "8000:80" restart: always diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Exceptions/BotTokenConfiguratorException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Exceptions/BotTokenConfiguratorException.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/SettingsHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/SettingsHandler.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Services/BotTokenConfigurator.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Services/BotTokenConfigurator.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json index 33482d0..cdff946 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json @@ -30,6 +30,8 @@ "require-dev": { "roave/security-advisories": "dev-latest", "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^9.6", + "doctrine/sql-formatter": "^1.3", + "mockery/mockery": "^1.6" } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock index e849aff..d2b706e 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7800f8b8834540a6bcbca3336bef7949", + "content-hash": "e8ed2d3d0e11eac86a27bb2972b115cd", "packages": [ { "name": "graham-campbell/result-type", @@ -1757,6 +1757,195 @@ ], "time": "2022-12-30T00:15:36+00:00" }, + { + "name": "doctrine/sql-formatter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/sql-formatter.git", + "reference": "3447381095d32a171fe3a58323749f44dbb5ac7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/3447381095d32a171fe3a58323749f44dbb5ac7d", + "reference": "3447381095d32a171fe3a58323749f44dbb5ac7d", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^8.5 || ^9.6", + "vimeo/psalm": "^4.11" + }, + "bin": [ + "bin/sql-formatter" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\SqlFormatter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "https://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/doctrine/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "support": { + "issues": "https://github.com/doctrine/sql-formatter/issues", + "source": "https://github.com/doctrine/sql-formatter/tree/1.3.0" + }, + "time": "2024-05-06T21:49:18+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.13.4", diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/CriteriaBuilder.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/CriteriaBuilder.php new file mode 100755 index 0000000..59fd41d --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/CriteriaBuilder.php @@ -0,0 +1,40 @@ +rulesRegistry = $rulesRegistry; + } + + public function apply(Builder $queryBuilder, array $query): void + { + $operand = $query['operand'] ?? 'AND'; + $rules = $query['rules'] ?? []; + + foreach ($rules as $ruleId => $rule) { + if (! $this->rulesRegistry->has($ruleId)) { + throw new InvalidArgumentException('Invalid rule: ' . $ruleId); + } + + $className = $this->rulesRegistry->get($ruleId); + + $criteria = []; + foreach ($rule['criteria'] as $name => $item) { + $criteria[$name] = new Criterion($item['type'], $item['params']); + } + + /** @var BaseRule $ruleClass */ + $ruleClass = new $className($ruleId, $criteria); + $ruleClass->apply($queryBuilder, $operand); + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Criterion.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Criterion.php new file mode 100755 index 0000000..dc42d91 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Criterion.php @@ -0,0 +1,15 @@ +type = $type; + $this->params = $params; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Exceptions/CriteriaBuilderException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Exceptions/CriteriaBuilderException.php new file mode 100755 index 0000000..9d89672 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Exceptions/CriteriaBuilderException.php @@ -0,0 +1,9 @@ +extractProperties($rule); + } + + public function extractProperties(object $object): array + { + $reflection = new ReflectionClass($object); + $properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC); + + $data = []; + + foreach ($properties as $property) { + $propertyName = $property->getName(); + $propertyValue = $property->getValue($object); + + if (is_object($propertyValue)) { + $data[$propertyName] = $this->extractProperties($propertyValue); + } elseif (is_array($propertyValue)) { + $data[$propertyName] = $this->extractArray($propertyValue); + } else { + $data[$propertyName] = $propertyValue; + } + } + + if ($reflection->hasMethod('metaAttributes')) { + $data['__meta'] = $reflection->getMethod('metaAttributes')->invoke($object); + } + + return $data; + } + + private function extractArray(array $array): array + { + $data = []; + foreach ($array as $key => $value) { + if (is_object($value)) { + $data[$key] = $this->extractProperties($value); + } elseif (is_array($value)) { + $data[$key] = $this->extractArray($value); + } else { + $data[$key] = $value; + } + } + + return $data; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Rules/BaseRule.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Rules/BaseRule.php new file mode 100755 index 0000000..e983e99 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/Rules/BaseRule.php @@ -0,0 +1,105 @@ + 'BETWEEN', + 'equals' => '=', + 'greater' => '>', + 'greater_or_equals' => '>=', + 'less' => '<', + 'less_or_equals' => '<=', + 'not_equals' => '<>', + ]; + + protected static $stringCompareOperators = [ + 'contains' => 'LIKE', + 'not_contains' => 'NOT LIKE', + 'equals' => '=', + 'not_equals' => '<>', + 'is_empty' => 'is_empty', + 'is_not_empty' => 'is_not_empty', + ]; + + /** + * @var string + */ + public $id; + /** + * @var array + */ + public $criteria; + + public function __construct( + string $id, + array $criteria + ) { + $this->id = $id; + $this->criteria = $criteria; + } + + abstract public static function initWithDefaults(): BaseRule; + + public function metaAttributes(): array + { + return [ + 'group' => 'other', + ]; + } + + abstract public function apply(Builder $builder, $operand); + + public function criterionStringCondition( + Builder $builder, + Criterion $criterion, + string $field, + string $operand, + string $joinAlias = '' + ): void { + $operator = static::$stringCompareOperators[$criterion->params['operator']]; + $keyword = $criterion->params['keyword']; + + if ($operator === 'is_empty') { + if ($joinAlias) { + $builder->whereNested(function (Builder $query) use ($field, $joinAlias) { + $query->whereRaw("TRIM($field) = '' OR {$joinAlias}.product_id IS NULL"); + }); + } else { + $builder->whereRaw("TRIM($field) = ''"); + } + + return; + } + + if ($operator === 'is_not_empty') { + $builder->whereRaw("TRIM($field) <> ''"); + return; + } + + if ($operator === 'LIKE' || $operator === 'NOT LIKE') { + $keyword = "%$keyword%"; + } + + $builder->where($field, $operator, $keyword, $operand); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/RulesRegistry.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/RulesRegistry.php new file mode 100755 index 0000000..2567001 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/CriteriaBuilder/RulesRegistry.php @@ -0,0 +1,63 @@ +items); + } + + /** + * @param string|array $ruleId + * @param string|null $class + * @return void + */ + public function register($ruleId, string $class = null): void + { + if (is_array($ruleId)) { + foreach ($ruleId as $key => $value) { + $this->register($key, $value); + } + + return; + } + + if ($this->has($ruleId)) { + throw new InvalidArgumentException("Rule '$ruleId' is already registered"); + } + + $this->items[$ruleId] = $class; + } + + public function get(string $ruleId) + { + if (! $this->has($ruleId)) { + throw new InvalidArgumentException("Rule '$ruleId' is not registered"); + } + + return $this->items[$ruleId]; + } + + public function getItems(): array + { + return $this->items; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Exceptions/EntityNotFoundException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Exceptions/EntityNotFoundException.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Logger/LoggerInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Logger/LoggerInterface.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Logger/OpenCartLogAdapter.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Logger/OpenCartLogAdapter.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php index a1c3f6a..abd0130 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php @@ -176,7 +176,13 @@ class Builder return $this; } - public function whereBetween(string $column, array $values, $boolean = 'and'): Builder + /** + * @param RawExpression|string $column + * @param array $values + * @param string $boolean + * @return $this + */ + public function whereBetween($column, array $values, string $boolean = 'and'): Builder { if (count($values) !== 2) { throw new InvalidArgumentException('Invalid number of values provided.'); diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php index 0459373..c0aac10 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php @@ -102,9 +102,18 @@ abstract class Grammar return $prefix . implode(' ', $compiledConditions); } + private function getRawValue($value): string + { + if ($value instanceof RawExpression) { + return $value->getValue(); + } + + return $value; + } + public function whereBasic($condition): string { - return $condition['column'] . ' ' . $condition['operator'] . ' ?'; + return $this->getRawValue($condition['column']) . ' ' . $condition['operator'] . ' ?'; } public function whereRaw($condition): string @@ -114,12 +123,12 @@ abstract class Grammar public function whereNull($condition): string { - return $condition['column'] . ' IS NULL'; + return $this->getRawValue($condition['column']) . ' IS NULL'; } public function whereNotNull($condition): string { - return $condition['column'] . ' IS NOT NULL'; + return $this->getRawValue($condition['column']) . ' IS NOT NULL'; } public function whereNested($condition): string @@ -129,7 +138,7 @@ abstract class Grammar public function whereBetween($condition): string { - return $condition['column'] . ' BETWEEN ? AND ?'; + return $this->getRawValue($condition['column']) . ' BETWEEN ? AND ?'; } public function compileOrders(Builder $builder, array $orders): string @@ -183,6 +192,6 @@ abstract class Grammar $inValues = str_repeat('?, ', count($condition['value']) - 1) . '?'; $inOperator = $condition['operator']; - return $condition['column'] . " $inOperator (" . $inValues . ')'; + return $this->getRawValue($condition['column']) . " $inOperator (" . $inValues . ')'; } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Commands/ChatIdCommand.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Commands/ChatIdCommand.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Commands/TelegramCommand.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Commands/TelegramCommand.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Contracts/TelegramCommandInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Contracts/TelegramCommandInterface.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Enums/ChatAction.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Enums/ChatAction.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Exceptions/TelegramCommandNotFoundException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/Exceptions/TelegramCommandNotFoundException.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramBotStateManager.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramBotStateManager.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramCommandsRegistry.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramCommandsRegistry.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ErrorBag.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ErrorBag.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRuleNotFoundException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRuleNotFoundException.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/Email.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/Email.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/Required.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/Required.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/ValidationRuleInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidationRules/ValidationRuleInterface.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidatorInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidatorInterface.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidatorServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/ValidatorServiceProvider.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/translations/ru.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/translations/ru.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductAttribute.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductAttribute.php new file mode 100755 index 0000000..18ec048 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductAttribute.php @@ -0,0 +1,70 @@ + new Criterion(static::CRITERIA_OPTION_PRODUCT_ATTRIBUTE, [ + 'attribute_id' => null, + 'operator' => static::CRITERIA_OPERATOR_CONTAINS, + 'keyword' => '', + 'language_id' => config('language_id'), + ]), + ]); + } + + public function apply(Builder $builder, $operand): void + { + foreach ($this->criteria as $criterion) { + if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_ATTRIBUTE) { + $facetHash = md5(serialize($criterion)); + $joinAlias = 'product_attributes_facet_' . $facetHash; + if ($builder->hasJoinAlias($joinAlias)) { + return; + } + + $operator = static::$stringCompareOperators[$criterion->params['operator']]; + + $languageId = $criterion->params['language_id'] ?? null; + if (! $languageId) { + throw new InvalidArgumentException('language_id is required for the product attribute filter'); + } + + $builder->leftJoin( + db_table('product_attribute') . " AS $joinAlias", + function (JoinClause $join) use ($criterion, $joinAlias, $operator, $languageId) { + $join->on('products.product_id', '=', "$joinAlias.product_id") + ->where("$joinAlias.attribute_id", '=', $criterion->params['attribute_id']) + ->where("$joinAlias.language_id", '=', $languageId); + + if ($operator !== 'is_empty' && $operator !== 'is_not_empty') { + $this->criterionStringCondition( + $join, + $criterion, + "$joinAlias.text", + 'and' + ); + } + } + ); + + if ($operator === 'is_empty') { + $builder->whereNull("$joinAlias.product_id", $operand); + } else { + $builder->whereNotNull("$joinAlias.product_id", $operand); + } + } + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductCategories.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductCategories.php new file mode 100755 index 0000000..b0cfcf1 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductCategories.php @@ -0,0 +1,61 @@ + new Criterion(static::CRITERIA_OPTION_PRODUCT_CATEGORIES, [ + 'operator' => static::CRITERIA_OPERATOR_CONTAINS, + 'value' => [], + ]) + ]); + } + + public function apply(Builder $builder, $operand): void + { + /** @var Criterion $criterion */ + foreach ($this->criteria as $criterion) { + if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_CATEGORIES) { + $uniqHash = md5(serialize($criterion)); + $joinAlias = 'product_category_' . $uniqHash; + if ($builder->hasJoinAlias($joinAlias)) { + return; + } + + $operator = $criterion->params['operator']; + $categoryIds = $criterion->params['value']; + + $builder->join( + db_table('product_to_category') . " AS $joinAlias", + function (JoinClause $join) use ($joinAlias, $categoryIds) { + $join->on('products.product_id', '=', "$joinAlias.product_id"); + if ($categoryIds) { + $join->whereIn("$joinAlias.category_id", $categoryIds); + } + }, + 'left' + ); + + if ($operator === 'contains' && ! $categoryIds) { + $builder->whereNull("$joinAlias.product_id", $operand); + } elseif ($operator === 'not_contains' && ! $categoryIds) { + $builder->whereNotNull("$joinAlias.product_id", $operand); + } elseif ($operator === 'contains') { + $builder->whereNotNull("$joinAlias.product_id", $operand); + } else { + $builder->whereNull("$joinAlias.product_id", $operand); + } + } + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductForMainPage.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductForMainPage.php new file mode 100755 index 0000000..259c921 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductForMainPage.php @@ -0,0 +1,51 @@ + new Criterion(static::CRITERIA_OPTION_PRODUCT_MODEL, [ + 'operator' => static::CRITERIA_OPERATOR_EQUALS, + 'value' => false, + ]), + ]); + } + + public function apply(Builder $builder, $operand): void + { + $criterion = $this->criteria[static::CRITERIA_OPTION_PRODUCT_FOR_MAIN_PAGE] ?? false; + if (! $criterion || $criterion->params['value'] === false) { + return; + } + + $featuredProducts = config('featured_products', []); + $mainpageProducts = config('mainpage_products'); + + if ($mainpageProducts === 'featured' && $featuredProducts) { + $builder->whereIn('products.product_id', $featuredProducts); + return; + } + + if ($mainpageProducts === 'latest') { + $builder->orders = []; + $builder->orderBy('products.date_modified', 'DESC'); + return; + } + + if ($mainpageProducts === 'most_viewed') { + $builder->orders = []; + $builder->orderBy('products.viewed', 'DESC'); + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductManufacturer.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductManufacturer.php new file mode 100755 index 0000000..f42d740 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductManufacturer.php @@ -0,0 +1,48 @@ + new Criterion(static::CRITERIA_OPTION_PRODUCT_MANUFACTURER, [ + 'operator' => static::CRITERIA_OPERATOR_CONTAINS, + 'value' => [], + ]) + ]); + } + + public function apply(Builder $builder, $operand): void + { + /** @var Criterion $criterion */ + foreach ($this->criteria as $criterion) { + if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_MANUFACTURER) { + $operator = $criterion->params['operator']; + $ids = $criterion->params['value']; + + if ($ids) { + $builder->whereIn( + 'products.manufacturer_id', + $ids, + $operator === static::CRITERIA_OPERATOR_NOT_CONTAINS + ); + } else { + $builder->where( + 'products.manufacturer_id', + '=', + 0, + $operator === static::CRITERIA_OPERATOR_NOT_CONTAINS + ); + } + } + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductModel.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductModel.php new file mode 100755 index 0000000..afdd1ac --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductModel.php @@ -0,0 +1,45 @@ + new Criterion(static::CRITERIA_OPTION_PRODUCT_MODEL, [ + 'operator' => static::CRITERIA_OPERATOR_CONTAINS, + 'value' => [], + ]) + ]); + } + + public function apply(Builder $builder, $operand): void + { + /** @var Criterion $criterion */ + foreach ($this->criteria as $criterion) { + if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_MODEL) { + $operator = $criterion->params['operator']; + $models = $criterion->params['value'] ?? []; + + if ($models) { + $builder->whereIn( + 'products.model', + $models, + $operator === static::CRITERIA_OPERATOR_NOT_CONTAINS + ); + } else { + $builder->whereRaw('TRUE = FALSE'); + } + } + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductPrice.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductPrice.php new file mode 100755 index 0000000..df3c604 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductPrice.php @@ -0,0 +1,145 @@ + new Criterion(static::CRITERIA_OPTION_NUMBER, [ + 'operator' => static::CRITERIA_OPERATOR_GREATER_OR_EQUAL, + 'value' => [ + 'from' => 0, + 'to' => null, + ], + ]), + 'include_discounts' => new Criterion(static::CRITERIA_OPTION_BOOLEAN, [ + 'value' => true, + ]), + 'include_specials' => new Criterion(static::CRITERIA_OPTION_BOOLEAN, [ + 'value' => true, + ]), + ]); + } + + /** + * @return void + */ + public function apply(Builder $builder, $operand) + { + $includeSpecials = Arr::get($this->criteria, 'include_specials.value', true); + $includeDiscounts = Arr::get($this->criteria, 'include_discounts.value', false); + + /** @var Criterion|null $productPriceCriterion */ + $productPriceCriterion = $this->criteria['product_price'] ?? null; + + if (! $productPriceCriterion) { + throw new RuntimeException('Invalid product price rule format. Criterion is not found. Check filter JSON.'); + } + + if (! isset(static::$numberCompareOperators[$productPriceCriterion->params['operator']])) { + throw new InvalidArgumentException('Invalid operator: ' . $productPriceCriterion->params['operator']); + } + + $column = 'products.price'; + + if ($includeSpecials) { + $specialsFacetHash = md5(serialize($productPriceCriterion) . 'specials'); + $joinAlias = 'product_specials_' . $specialsFacetHash; + if ($builder->hasJoinAlias($joinAlias)) { + return; + } + + $customerGroupId = config('oc_customer_group_id', 1); + + $builder->join( + db_table('product_special') . " AS $joinAlias", + function (JoinClause $join) use ($joinAlias, $customerGroupId) { + $join + ->on('products.product_id', '=', "$joinAlias.product_id") + ->where("$joinAlias.customer_group_id", '=', $customerGroupId) + ->whereRaw(" + ($joinAlias.date_start = '0000-00-00' OR $joinAlias.date_start < NOW()) + AND ($joinAlias.date_end = '0000-00-00' OR $joinAlias.date_end > NOW()) + ") + ->orderBy("$joinAlias.priority", 'ASC') + ->orderBy('products.price', 'ASC') + ->limit(1); + }, + 'left' + ); + + $column = new RawExpression("COALESCE($joinAlias.price, products.price)"); + } + + $numberOperator = static::$numberCompareOperators[$productPriceCriterion->params['operator']]; + $value = $this->prepareValue( + $numberOperator, + $productPriceCriterion->params['value'] + ); + + if ($numberOperator === 'BETWEEN') { + [$min, $max] = $value; // $min = левая, $max = правая граница + + // если обе границы не указаны — фильтр игнорируем + if ($min === null && $max === null) { + return; + } + + // если только правая граница — "меньше или равно" + if ($min === null && $max !== null) { + $builder->where($column, '<=', $max, $operand); + return; + } + + // если только левая граница — "больше или равно" + if ($min !== null && $max === null) { + $builder->where($column, '>=', $min, $operand); + return; + } + + // левая и правая граница равны + if ($min !== null && $max !== null && $min === $max) { + $builder->where($column, '=', $min, $operand); + return; + } + + // если обе границы есть — классический between (min ≤ x ≤ max) + if ($min !== null && $max !== null) { + $builder->whereBetween($column, [$min, $max], $operand); + } + } else { + $builder->where($column, $numberOperator, $value[0], $operand); + } + } + + private function prepareValue($numberOperator, array $value): array + { + $from = null; + $to = null; + + if (is_numeric($value['from'])) { + $from = (int) $value['from']; + } + + if (is_numeric($value['to'])) { + $to = (int) $value['to']; + } + + return [$from, $to]; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductQuantity.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductQuantity.php new file mode 100755 index 0000000..3af725c --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductQuantity.php @@ -0,0 +1,68 @@ + new Criterion(static::CRITERIA_OPTION_NUMBER, [ + 'operator' => static::CRITERIA_OPERATOR_GREATER_OR_EQUAL, + 'value' => [0, null], + ]), + ]); + } + + public function apply(Builder $builder, $operand): void + { + /** @var Criterion|null $productQuantityCriterion */ + $productQuantityCriterion = $this->criteria['product_quantity'] ?? null; + + if (! $productQuantityCriterion) { + throw new RuntimeException('Product Quantity rule criterion is not found.'); + } + + $column = 'products.quantity'; + + if (! isset(static::$numberCompareOperators[$productQuantityCriterion->params['operator']])) { + throw new InvalidArgumentException('Invalid operator: ' . $productQuantityCriterion->params['operator']); + } + + $numberOperator = static::$numberCompareOperators[$productQuantityCriterion->params['operator']]; + + $value = $this->prepareValue( + $numberOperator, + $productQuantityCriterion->params['value'], + ); + + if ($numberOperator === 'BETWEEN') { + $builder->whereBetween($column, $value, $operand); + } else { + $builder->where($column, $numberOperator, $value[0], $operand); + } + } + + private function prepareValue($numberOperator, array $value): array + { + if ( + (isset($value[0]) && ! is_numeric($value[0])) + || ($numberOperator === 'BETWEEN' && ! $value[1]) + ) { + throw new CriteriaBuilderException('Value is required.'); + } + + return array_map('intval', $value); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductStatus.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductStatus.php new file mode 100755 index 0000000..5cd304d --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/FacetSearch/Filters/ProductStatus.php @@ -0,0 +1,35 @@ + new Criterion(static::CRITERIA_OPTION_BOOLEAN, [ + 'operator' => static::CRITERIA_OPERATOR_EQUALS, + 'value' => true, + ]), + ]); + } + + public function apply(Builder $builder, $operand): void + { + /** @var Criterion $criterion */ + foreach ($this->criteria as $criterion) { + if ($criterion->type === static::CRITERIA_OPTION_BOOLEAN) { + $value = $criterion->params['value']; + $builder->where('products.status', '=', $value, $operand); + } + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php index ac2a777..1a5eb1f 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php @@ -29,24 +29,15 @@ class ProductsHandler public function index(Request $request): JsonResponse { - $page = (int) $request->get('page', 1); - $perPage = min((int) $request->get('perPage', 6), 15); - $categoryId = (int) $request->get('categoryId', 0); - $search = trim($request->get('search', '')); - $forMainPage = filter_var($request->get('forMainPage', false), FILTER_VALIDATE_BOOLEAN); - $featuredProducts = $this->settings->get('featured_products'); - $mainpageProducts = $this->settings->get('mainpage_products'); + $page = (int) $request->json('page', 1); + $perPage = min((int) $request->json('perPage', 6), 15); + $search = trim($request->json('search', '')); + $filters = $request->json('filters'); + $languageId = $this->settings->get('language_id'); $response = $this->productsService->getProductsResponse( - compact( - 'page', - 'perPage', - 'categoryId', - 'search', - 'forMainPage', - 'featuredProducts', - 'mainpageProducts' - ) + compact('page', 'perPage', 'search', 'filters'), + $languageId, ); return new JsonResponse($response); diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php index 1165280..940eb49 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php @@ -3,9 +3,18 @@ namespace App\ServiceProviders; use App\Exceptions\CustomExceptionHandler; +use App\FacetSearch\Filters\ProductAttribute; +use App\FacetSearch\Filters\ProductCategories; +use App\FacetSearch\Filters\ProductForMainPage; +use App\FacetSearch\Filters\ProductManufacturer; +use App\FacetSearch\Filters\ProductModel; +use App\FacetSearch\Filters\ProductPrice; +use App\FacetSearch\Filters\ProductQuantity; +use App\FacetSearch\Filters\ProductStatus; use App\Telegram\LinkCommand; use Openguru\OpenCartFramework\Container\ServiceProvider; use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface; +use Openguru\OpenCartFramework\CriteriaBuilder\RulesRegistry; use Openguru\OpenCartFramework\Telegram\Commands\ChatIdCommand; use Openguru\OpenCartFramework\Telegram\TelegramCommandsRegistry; @@ -18,6 +27,7 @@ class AppServiceProvider extends ServiceProvider }); $this->registerTelegramCommands(); + $this->registerFacetFilters(); } private function registerTelegramCommands(): void @@ -31,4 +41,23 @@ class AppServiceProvider extends ServiceProvider $registry->addCommand('id', ChatIdCommand::class, 'Возвращает ChatID текущего чата.'); $registry->addCommand('link', LinkCommand::class, 'Генератор Telegram сообщений с кнопкой'); } + + private function registerFacetFilters(): void + { + $this->container->singleton(RulesRegistry::class, function () { + return new RulesRegistry(); + }); + + $registry = $this->container->get(RulesRegistry::class); + $registry->register([ + ProductAttribute::NAME => ProductAttribute::class, + ProductCategories::NAME => ProductCategories::class, + ProductManufacturer::NAME => ProductManufacturer::class, + ProductModel::NAME => ProductModel::class, + ProductPrice::NAME => ProductPrice::class, + ProductQuantity::NAME => ProductQuantity::class, + ProductStatus::NAME => ProductStatus::class, + ProductForMainPage::NAME => ProductForMainPage::class, + ]); + } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/ProductsService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/ProductsService.php index 0a95c97..b44ed68 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/ProductsService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/ProductsService.php @@ -6,6 +6,7 @@ use Cart\Currency; use Cart\Tax; use Exception; use Openguru\OpenCartFramework\Config\Settings; +use Openguru\OpenCartFramework\CriteriaBuilder\CriteriaBuilder; use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException; use Openguru\OpenCartFramework\ImageTool\ImageToolInterface; use Openguru\OpenCartFramework\Logger\LoggerInterface; @@ -25,6 +26,7 @@ class ProductsService private ImageToolInterface $ocImageTool; private OcRegistryDecorator $oc; private LoggerInterface $logger; + private CriteriaBuilder $criteriaBuilder; public function __construct( Builder $queryBuilder, @@ -33,7 +35,8 @@ class ProductsService Settings $settings, ImageToolInterface $ocImageTool, OcRegistryDecorator $registry, - LoggerInterface $logger + LoggerInterface $logger, + CriteriaBuilder $criteriaBuilder ) { $this->queryBuilder = $queryBuilder; $this->currency = $currency; @@ -42,32 +45,19 @@ class ProductsService $this->ocImageTool = $ocImageTool; $this->oc = $registry; $this->logger = $logger; + $this->criteriaBuilder = $criteriaBuilder; } - public function getProductsResponse(array $params): array + public function getProductsResponse(array $params, int $languageId): array { $page = $params['page']; $perPage = $params['perPage']; - $categoryId = $params['categoryId']; $search = $params['search']; - $forMainPage = $params['forMainPage']; - $featuredProducts = $params['featuredProducts']; - $mainpageProducts = $params['mainpageProducts']; - $status = $params['status'] ?? 1; - $languageId = 1; $categoryName = ''; $imageWidth = 300; $imageHeight = 300; - $maxPages = $params['maxPages'] ?? 10; - - if ($categoryId) { - $categoryName = $this->queryBuilder->newQuery() - ->select(['name']) - ->from(db_table('category_description'), 'category') - ->where('language_id', '=', $languageId) - ->where('category_id', '=', $categoryId) - ->value('name'); - } + $maxPages = $params['maxPages'] ?? 50; + $filters = $params['filters'] ?? []; $customerGroupId = (int) $this->oc->config->get('config_customer_group_id'); $specialPriceSql = "(SELECT price @@ -97,34 +87,21 @@ class ProductsService ->where('product_description.language_id', '=', $languageId); } ) - ->where('products.status', '=', $status) + ->where('products.status', '=', 1) ->whereRaw('products.date_available < NOW()') - ->when($categoryId !== 0, function (Builder $query) use ($categoryId) { - $query->join( - db_table('product_to_category') . ' AS product_to_category', - function (JoinClause $join) use ($categoryId) { - $join->on('product_to_category.product_id', '=', 'products.product_id') - ->where('product_to_category.category_id', '=', $categoryId); - } - ); - }) - ->when( - $forMainPage && $mainpageProducts === 'featured' && $featuredProducts, - function (Builder $query) use ($featuredProducts) { - $query->whereIn('products.product_id', $featuredProducts); - } - ) ->when($search, function (Builder $query) use ($search) { $query->where('product_description.name', 'LIKE', '%' . $search . '%'); }); + $this->criteriaBuilder->apply($productsQuery, $filters); + $total = $productsQuery->count(); $lastPage = min(PaginationHelper::calculateLastPage($total, $perPage), $maxPages); $hasMore = $page + 1 <= $lastPage; $products = $productsQuery ->forPage($page, $perPage) - ->orderBy($mainpageProducts === 'latest' ? 'date_modified' : 'viewed', 'DESC') + ->orderBy('date_modified', 'DESC') ->get(); $productIds = Arr::pluck($products, 'product_id'); @@ -150,6 +127,13 @@ class ProductsService ]; } + $debug = []; + if (env('APP_DEBUG')) { + $debug = [ + 'sql' => $productsQuery->toRawSql(), + ]; + } + return [ 'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight) { $allImages = []; @@ -204,6 +188,7 @@ class ProductsService 'meta' => [ 'currentCategoryName' => $categoryName, 'hasMore' => $hasMore, + 'debug' => $debug, ] ]; } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Telegram/LinkCommand.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Telegram/LinkCommand.php old mode 100644 new mode 100755 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/DatabaseHelpers.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/DatabaseHelpers.php new file mode 100755 index 0000000..933292e --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/DatabaseHelpers.php @@ -0,0 +1,60 @@ +app->get(ConnectionInterface::class); + $databaseName = getenv('DB_DATABASE'); + + $sql = <<select($sql, [$databaseName]); + $tables = array_column($tables, 'TABLE_NAME'); + + foreach ($tables as $table) { + $database->dropTable($table); + } + } + + public function getPdoMock() + { + $pdoMock = Mockery::spy(PDO::class); + $pdoMock->shouldReceive('quote') + ->andReturnUsing(function ($value) { + return "'$value'"; + }); + + return $pdoMock; + } + + public function formatSql(string $query): string + { + return (new SqlFormatter(new NullHighlighter()))->format($query); + } + + public function createRandomTable(): string + { + $randomTableName = 'tbl_random_' . uniqid('', true); + $randomTableName = str_replace('.', '_', $randomTableName); + + /** @var ConnectionInterface $database */ + $database = $this->app->get(ConnectionInterface::class); + $database->statement("CREATE TABLE $randomTableName (id INT AUTO_INCREMENT PRIMARY KEY)"); + + return $randomTableName; + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleDatabaseConnection.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleDatabaseConnection.php new file mode 100755 index 0000000..7b99cbb --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleDatabaseConnection.php @@ -0,0 +1,7 @@ +config = $config; + $this->rateLimit = $rateLimit; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonRepository.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonRepository.php new file mode 100755 index 0000000..2e0b0fc --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonRepository.php @@ -0,0 +1,16 @@ +connection = $connection; + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonService.php new file mode 100755 index 0000000..b53ce5c --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExamplePersonService.php @@ -0,0 +1,24 @@ +personRepository = $personRepository; + $this->smsGateway = $smsGateway; + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleSmsGateway.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleSmsGateway.php new file mode 100755 index 0000000..1dbcb89 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/ExampleSmsGateway.php @@ -0,0 +1,7 @@ +email = $email; + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/FilterDTO.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/FilterDTO.php new file mode 100755 index 0000000..d77e229 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/FilterDTO.php @@ -0,0 +1,25 @@ +field = $field; + $this->operator = $operator; + $this->value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/TestClassWithMethod.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/TestClassWithMethod.php new file mode 100755 index 0000000..3afc776 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Helpers/ExampleClasses/TestClassWithMethod.php @@ -0,0 +1,14 @@ +app = $this->bootstrapApplication(); + } - $this->app = new Application($config); + public static function basePath(): string + { + return __DIR__; + } + + public static function fixturesPath(): string + { + return static::basePath() . DIRECTORY_SEPARATOR . 'fixtures'; + } + + private function bootstrapApplication(): Application + { + $app = ApplicationFactory::create([ + 'db' => [ + 'prefix' => 'oc_', + ], + ]); + + $app->boot(); + + $this->registerDependenciesForTests($app); + + return $app; + } + + /** + * @template T + * @param class-string $factoryClass + * @param array $params + * @return T + */ + public function factory(string $factoryClass, array $params = []) + { + return $this->app->get($factoryClass); + } + + private function registerDependenciesForTests(Container $container): void + { + $url = Mockery::mock('Url'); + $url->shouldReceive('link')->andReturn('http://localhost'); + + $container->bind(\Url::class, function () use ($url) { + return $url; + }); } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ArrTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ArrTest.php new file mode 100755 index 0000000..60695ee --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ArrTest.php @@ -0,0 +1,194 @@ + 1, 'name' => 'Item 1'], + ['id' => 2, 'name' => 'Item 2'], + ]; + + $result = Arr::keyByField($data, 'id'); + + $this->assertArrayHasKey(1, $result); + $this->assertArrayHasKey(2, $result); + $this->assertEquals('Item 1', $result[1]['name']); + $this->assertEquals('Item 2', $result[2]['name']); + + $this->expectException(InvalidArgumentException::class); + Arr::keyByField($data, 'nonexistent'); + } + + public function testGroupByKey(): void + { + $data = [ + ['category' => 'A', 'value' => 10], + ['category' => 'A', 'value' => 20], + ['category' => 'B', 'value' => 30], + ]; + + $result = Arr::groupByKey($data, 'category', 'value'); + + $this->assertEquals(['A' => [10, 20], 'B' => [30]], $result); + } + + public function testGet(): void + { + $data = ['key' => 'value', 'nested' => ['key' => 'nestedValue']]; + + $this->assertEquals('value', Arr::get($data, 'key')); + $this->assertEquals('nestedValue', Arr::get($data, 'nested.key')); + $this->assertNull(Arr::get($data, 'nonexistent')); + $this->assertEquals('default', Arr::get($data, 'nonexistent', 'default')); + } + + public function testSet(): void + { + $data = []; + + Arr::set($data, 'key', 'value'); + $this->assertEquals('value', $data['key']); + + Arr::set($data, 'nested.key', 'nestedValue'); + $this->assertEquals('nestedValue', $data['nested']['key']); + } + + public function testUnset(): void + { + $data = ['key' => 'value', 'nested' => ['key' => 'nestedValue']]; + + Arr::unset($data, 'key'); + $this->assertArrayNotHasKey('key', $data); + + Arr::unset($data, 'nested.key'); + $this->assertArrayNotHasKey('key', $data['nested']); + } + + public function testFind(): void + { + $data = [ + ['id' => 1, 'name' => 'Item 1'], + ['id' => 2, 'name' => 'Item 2'], + ]; + + $result = Arr::find($data, function ($item) { + return $item['id'] === 2; + }); + $this->assertEquals(['id' => 2, 'name' => 'Item 2'], $result); + + $result = Arr::find($data, function ($item) { + return $item['id'] === 3; + }); + $this->assertNull($result); + } + + public function testMergeArraysFlat(): void + { + $base = ['key1' => 'value1', 'key2' => 'value2']; + $override = ['key2' => 'new_value2', 'key3' => 'value3']; + + $expected = [ + 'key1' => 'value1', + 'key2' => 'new_value2', + 'key3' => 'value3', + ]; + + $this->assertSame($expected, Arr::mergeArraysRecursively($base, $override)); + } + + public function testMergeArraysNested(): void + { + $base = [ + 'key1' => [ + 'subkey1' => 'value1', + 'subkey2' => 'value2', + ], + 'key2' => 'value3', + ]; + $override = [ + 'key1' => [ + 'subkey2' => 'new_value2', + 'subkey3' => 'value4', + ], + 'key2' => 'new_value3', + ]; + + $expected = [ + 'key1' => [ + 'subkey1' => 'value1', + 'subkey2' => 'new_value2', + 'subkey3' => 'value4', + ], + 'key2' => 'new_value3', + ]; + + $this->assertSame($expected, Arr::mergeArraysRecursively($base, $override)); + } + + public function testMergeArraysOverrideWithNonArray(): void + { + $base = ['key1' => ['subkey1' => 'value1']]; + $override = ['key1' => 'new_value1']; + + $expected = ['key1' => 'new_value1']; + + $this->assertSame($expected, Arr::mergeArraysRecursively($base, $override)); + } + + public function testMergeArraysEmptyBase(): void + { + $base = []; + $override = ['key1' => 'value1']; + + $expected = ['key1' => 'value1']; + + $this->assertSame($expected, Arr::mergeArraysRecursively($base, $override)); + } + + public function testMergeArraysEmptyOverride(): void + { + $base = ['key1' => 'value1']; + $override = []; + + $expected = ['key1' => 'value1']; + + $this->assertSame($expected, Arr::mergeArraysRecursively($base, $override)); + } + + public function testMergeArraysWithDeepNesting(): void + { + $base = [ + 'key1' => [ + 'subkey1' => [ + 'subsubkey1' => 'value1', + ], + ], + ]; + $override = [ + 'key1' => [ + 'subkey1' => [ + 'subsubkey1' => 'new_value1', + 'subsubkey2' => 'value2', + ], + ], + ]; + + $expected = [ + 'key1' => [ + 'subkey1' => [ + 'subsubkey1' => 'new_value1', + 'subsubkey2' => 'value2', + ], + ], + ]; + + $this->assertSame($expected, Arr::mergeArraysRecursively($base, $override)); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/BuilderTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/BuilderTest.php new file mode 100755 index 0000000..6c11a2c --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/BuilderTest.php @@ -0,0 +1,347 @@ +getPdoMock()); + + $this->builder = new Builder($connection, new MySqlGrammar()); + } + + public function testSelect(): void + { + $sql = $this->builder + ->select([ + 'foo', + 'boo' => 'bar', + ]) + ->from('some_table') + ->toSql(); + + $this->assertEquals(/** @lang text */ 'SELECT foo, boo AS bar FROM some_table', $sql); + } + + public function testSelectRawExpression(): void + { + $sql = $this->builder + ->select([ + 'foo', + new RawExpression('IF (foobar IS NULL, TRUE, FALSE) AS alias'), + ]) + ->from('some_table') + ->toSql(); + + $this->assertEquals( + /** @lang text */ 'SELECT foo, IF (foobar IS NULL, TRUE, FALSE) AS alias FROM some_table', + $sql + ); + } + + public function testFrom(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table AS alias', + $this->builder + ->from('some_table', 'alias') + ->toSql() + ); + + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table', + $this->builder + ->from('some_table') + ->toSql() + ); + } + + public function testWhereNotNull(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo IS NOT NULL', + $this->builder + ->from('some_table') + ->whereNotNull('foo') + ->toSql() + ); + } + + public function testWhereNull(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo IS NULL', + $this->builder + ->from('some_table') + ->whereNull('foo') + ->toSql() + ); + } + + public function testWhere(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo = ?', + $this->builder + ->from('some_table') + ->where('foo', '=', 'bar') + ->toSql() + ); + + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo LIKE ?', + $this->builder + ->newQuery() + ->from('some_table') + ->where('foo', 'LIKE', '%bar%') + ->toSql() + ); + } + + public function testOrWhere(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM t1 WHERE foo = ? OR bar = ?', + $this->builder + ->from('t1') + ->where('foo', '=', 'bar') + ->orWhere('bar', '=', 'boo') + ->toSql() + ); + } + + public function testLimit(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table LIMIT 10', + $this->builder + ->from('some_table') + ->limit(10) + ->toSql() + ); + } + + public function testOffset(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table OFFSET 10', + $this->builder + ->from('some_table') + ->offset(10) + ->toSql() + ); + } + + public function testForPage(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table LIMIT 20 OFFSET 20', + $this->builder + ->from('some_table') + ->forPage(2, 20) + ->toSql() + ); + } + + public function testOrderBy(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table ORDER BY foo ASC, bar DESC', + $this->builder + ->from('some_table') + ->orderBy('foo') + ->orderBy('bar', 'DESC') + ->toSql() + ); + } + + public function testJoin(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM t1 INNER JOIN t2 ON t1.key = t2.key AND t1.foo IS NOT NULL OR t2.bar <> ?', + $this->builder + ->from('t1') + ->join('t2', function (JoinClause $join) { + $join->on('t1.key', '=', 't2.key') + ->whereNotNull('t1.foo') + ->where('t2.bar', '<>', 'value', 'or'); + }) + ->toSql() + ); + } + + public function testLeftJoin(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM t1 LEFT JOIN t2 ON t1.key = t2.key', + $this->builder + ->from('t1') + ->join('t2', function (JoinClause $join) { + $join->on('t1.key', '=', 't2.key'); + }, 'left') + ->toSql() + ); + + $this->assertEquals( + /** @lang text */ 'SELECT * FROM t1 LEFT JOIN t2 ON t1.key = t2.key', + $this->builder->newQuery() + ->from('t1') + ->leftJoin('t2', function (JoinClause $join) { + $join->on('t1.key', '=', 't2.key'); + }) + ->toSql() + ); + } + + public function testWhereNested(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM t1 WHERE (t1.foo = ? AND t2.bar = ?) OR (t1.foo = ? AND t2.bar = ?)', + $this->builder + ->from('t1') + ->whereNested(function (Builder $builder) { + $builder->where('t1.foo', '=', 'bar') + ->where('t2.bar', '=', 'foo'); + }) + ->whereNested(function (Builder $builder) { + $builder->where('t1.foo', '=', 'foo') + ->where('t2.bar', '=', 'bar'); + }, 'or') + ->toSql() + ); + } + + public function testWhereNestedEmpty(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM t1 WHERE ()', + $this->builder + ->from('t1') + ->whereNested(function (Builder $builder) { + }) + ->toSql() + ); + } + + public function testWhereBetween(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo BETWEEN 100 AND 200', + $this->builder + ->from('some_table') + ->whereBetween('foo', [100, 200]) + ->toRawSql() + ); + } + + public function testWhereBetweenWithOperand(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo BETWEEN 100 AND 200 OR bar BETWEEN 10 AND 20', + $this->builder + ->from('some_table') + ->whereBetween('foo', [100, 200]) + ->whereBetween('bar', [10, 20], 'or') + ->toRawSql() + ); + } + + public function testWhereIn(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo IN (1, 2, 3)', + $this->builder + ->from('some_table') + ->whereIn('foo', [1, 2, 3]) + ->toRawSql() + ); + } + + public function testWhereNotIn(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo NOT IN (1, 2, 3)', + $this->builder + ->from('some_table') + ->whereIn('foo', [1, 2, 3], true) + ->toRawSql() + ); + + $this->assertEquals( + /** @lang text */ 'SELECT * FROM some_table WHERE foo NOT IN (1, 2, 3)', + $this->builder + ->newQuery() + ->from('some_table') + ->whereNotIn('foo', [1, 2, 3]) + ->toRawSql() + ); + } + + public function testDistinct(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT DISTINCT id FROM some_table', + $this->builder + ->select(['id']) + ->distinct() + ->from('some_table') + ->toRawSql() + ); + } + + public function testWhenConditionTrue(): void + { + $this->assertEquals( + /** @lang text */ "SELECT id FROM some_table WHERE foo = 'bar'", + $this->builder + ->select(['id']) + ->from('some_table') + ->when(true, function (Builder $query) { + $query->where('foo', '=', 'bar'); + }) + ->toRawSql() + ); + } + + public function testWhenConditionFalseWithDefault(): void + { + $this->assertEquals( + /** @lang text */ "SELECT id FROM some_table WHERE foo <> 'bar'", + $this->builder + ->select(['id']) + ->from('some_table') + ->when(false, function (Builder $query) { + $query->where('foo', '=', 'bar'); + }, function (Builder $query) { + $query->where('foo', '<>', 'bar'); + }) + ->toRawSql() + ); + } + + public function testWhenConditionFalseWithoutDefault(): void + { + $this->assertEquals( + /** @lang text */ "SELECT id FROM some_table", + $this->builder + ->select(['id']) + ->from('some_table') + ->when(false, function (Builder $query) { + $query->where('foo', '=', 'bar'); + }) + ->toRawSql() + ); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ContainerTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ContainerTest.php new file mode 100755 index 0000000..f8b3173 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/ContainerTest.php @@ -0,0 +1,145 @@ + 'value', + ]; + + private $container; + + protected function setUp(): void + { + parent::setUp(); + + $this->container = new Container($this->config); + } + + public function testGetConfigValue() + { + $this->assertEquals('value', $this->container->getConfigValue('key')); + + $this->assertIsArray($this->container->getConfigValue()); + } + + public function testBind(): void + { + $this->container->bind('abstract', function () {}); + + $this->assertTrue($this->container->has('abstract')); + } + + public function testResolve(): void + { + $this->container->bind(ExampleDatabaseConnection::class, function () { + return new ExampleDatabaseConnection(); + }); + + $concrete1 = $this->container->get(ExampleDatabaseConnection::class); + $concrete2 = $this->container->get(ExampleDatabaseConnection::class); + + $this->assertInstanceOf(ExampleDatabaseConnection::class, $concrete1); + $this->assertInstanceOf(ExampleDatabaseConnection::class, $concrete2); + $this->assertNotSame($concrete1, $concrete2); + } + + public function testSingleton(): void + { + $this->container->singleton(ExampleDatabaseConnection::class, function () { + return new ExampleDatabaseConnection(); + }); + + $concrete1 = $this->container->get(ExampleDatabaseConnection::class); + $concrete2 = $this->container->get(ExampleDatabaseConnection::class); + + $this->assertInstanceOf(ExampleDatabaseConnection::class, $concrete1); + $this->assertInstanceOf(ExampleDatabaseConnection::class, $concrete2); + $this->assertSame($concrete1, $concrete2); + } + + public function testDeepResolve(): void + { + $container = new Container([]); + + $container->bind(ExampleSmsGateway::class, function () { return new ExampleSmsGateway(); }); + $container->bind(ExampleDatabaseConnection::class, function () { return new ExampleDatabaseConnection(); }); + $container->bind(ExamplePersonRepository::class, function (Container $container) { + return new ExamplePersonRepository( + $container->get(ExampleDatabaseConnection::class), + ); + }); + $container->bind(ExamplePersonService::class, function (Container $container) { + return new ExamplePersonService( + $container->get(ExamplePersonRepository::class), + $container->get(ExampleSmsGateway::class), + ); + }); + + $personService = $container->get(ExamplePersonService::class); + + $this->assertInstanceOf(ExamplePersonService::class, $personService); + $this->assertInstanceOf(ExamplePersonRepository::class, $personService->personRepository); + $this->assertInstanceOf(ExampleSmsGateway::class, $personService->smsGateway); + } + + public function testAutoResolve(): void + { + $personService = $this->container->get(ExamplePersonService::class); + + $this->assertInstanceOf(ExamplePersonService::class, $personService); + $this->assertInstanceOf(ExamplePersonRepository::class, $personService->personRepository); + $this->assertInstanceOf(ExampleSmsGateway::class, $personService->smsGateway); + } + + public function testAutoResolveFailed(): void + { + $this->expectException(ContainerDependencyResolutionException::class); + + $response = $this->container->get(ExampleEmailWithConfig::class); + + $this->assertInstanceOf(ExampleEmailWithConfig::class, $response); + } + + public function testAutoResolveWithCustomParams(): void + { + $container = new Container([]); + + $container->bind( + ExampleEmailWithConfig::class, + function () { + return new ExampleEmailWithConfig(['foo' => 'bar'], 10); + } + ); + + /** @var ExampleEmailWithConfig $emailWithConfig */ + $emailWithConfig = $container->get(ExampleEmailWithConfig::class); + + $this->assertInstanceOf(ExampleEmailWithConfig::class, $emailWithConfig); + $this->assertEquals(['foo' => 'bar'], $emailWithConfig->config); + $this->assertEquals(10, $emailWithConfig->rateLimit); + } + + public function testCallMethodWithDependencies(): void + { + $this->container->bind(stdClass::class, function () { + return new stdClass(); + }); + + $response = $this->container->call(TestClassWithMethod::class, 'testMethod'); + $this->assertInstanceOf(JsonResponse::class, $response); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/CriteriaBuilderTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/CriteriaBuilderTest.php new file mode 100755 index 0000000..feef062 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/CriteriaBuilderTest.php @@ -0,0 +1,184 @@ +expectNotToPerformAssertions(); + + $input = $this->jsonFileToArray($folder . DIRECTORY_SEPARATOR . 'input.json'); + $outputFilename = $folder . '/output.sql'; + $settingsFilename = $folder . DIRECTORY_SEPARATOR . 'settings_override.json'; + + $settingsOverride = []; + if (file_exists($settingsFilename)) { + $settingsOverride = $this->jsonFileToArray($settingsFilename); + } + + $baseSettings = [ + 'db' => [ + 'prefix' => 'oc_', + ], + ]; + $config = Arr::mergeArraysRecursively($baseSettings, $settingsOverride); + $application = new Application($config); + $mysqlConnection = new MySqlConnection($this->getPdoMock()); + $application->boot(); + + $e = config('mainpage_products'); + + $application->bind(ConnectionInterface::class, function () use ($mysqlConnection) { + return $mysqlConnection; + }); + + $application->bind(Builder::class, function (Container $container) { + return new Builder( + $container->get(ConnectionInterface::class), + $container->get(MySqlGrammar::class), + ); + }); + + $application->singleton(RulesRegistry::class, function () { + return new RulesRegistry(); + }); + + $application->singleton(Settings::class, function () { + return new Settings(); + }); + + /** @var RulesRegistry $rulesRegistry */ + $rulesRegistry = $application->get(RulesRegistry::class); + $rulesRegistry->register(ProductPrice::NAME, ProductPrice::class); + $rulesRegistry->register(ProductStatus::NAME, ProductStatus::class); + $rulesRegistry->register(ProductModel::NAME, ProductModel::class); + $rulesRegistry->register(ProductCategories::NAME, ProductCategories::class); + $rulesRegistry->register(ProductManufacturer::NAME, ProductManufacturer::class); + $rulesRegistry->register(ProductQuantity::NAME, ProductQuantity::class); + $rulesRegistry->register(ProductAttribute::NAME, ProductAttribute::class); + $rulesRegistry->register(ProductForMainPage::NAME, ProductForMainPage::class); + + $this->builder = $application->get(Builder::class); + $this->criteriaBuilder = $application->get(CriteriaBuilder::class); + $this->baseQuery = $this->builder->newQuery() + ->select([ + 'products.product_id' => 'product_id', + 'products.image' => 'image', + 'product_description.name' => 'name', + 'products.model' => 'model', + 'products.price' => 'price', + 'products.quantity' => 'quantity', + 'products.status' => 'status', + 'products.noindex' => 'noindex' + ]) + ->from('oc_product', 'products') + ->join( + 'oc_product_description AS product_description', + function (JoinClause $join) { + return $join->on('products.product_id', '=', 'product_description.product_id') + ->where('product_description.language_id', '=', 1); + } + ); + + $this->criteriaBuilder->apply($this->baseQuery, $input); + + $actual = $this->formatSql($this->baseQuery->toRawSql()); + + if (! file_exists($outputFilename)) { + file_put_contents($outputFilename, $actual); + $this->markTestSkipped('Regenerated fixtures. Rerun the test.'); + } + + if (file_get_contents($outputFilename) !== $actual) { + file_put_contents($outputFilename, $actual); + $this->markTestIncomplete('SQL changed. Please check it and regenerate fixtures.'); + } + } + + public static function CriteriaBuilderFixturesDataProvider(): array + { + return self::findDirectories(static::fixturesPath() . '/criteria_builder'); + } + + private static function findDirectories($rootFolder, $currentLevel = 1, $maxLevel = 2): array + { + $directories = []; + + if ($currentLevel > $maxLevel) { + return $directories; + } + + $iterator = new DirectoryIterator($rootFolder); + foreach ($iterator as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + + if ($fileInfo->isDir()) { + if ($currentLevel === 2) { + $parts = array_reverse(explode('/', $fileInfo->getPathname())); + $key = $parts[1] . '_' . $parts[0]; + $directories[$key] = [ + $fileInfo->getPathname(), + ]; + } + + $subDirectories = self::findDirectories($fileInfo->getPathname(), $currentLevel + 1, $maxLevel); + $directories = array_merge($directories, $subDirectories); + } + } + + return $directories; + } + + /** + * @throws JsonException + */ + private function jsonFileToArray(string $path): array + { + if (! file_exists($path)) { + throw new InvalidArgumentException('Input file not found: ' . $path); + } + + return json_decode( + file_get_contents($path), + true, + 512, + JSON_THROW_ON_ERROR + ); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/GenericCollectionTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/GenericCollectionTest.php new file mode 100755 index 0000000..a2fb6e5 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/GenericCollectionTest.php @@ -0,0 +1,176 @@ +', 100); + $collection->add($filter); + + $this->assertCount(1, $collection); + $this->assertSame($filter, $collection->all()[0]); + } + + public function testThrowsExceptionWhenAddingInvalidItem(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Item must be an instance of'); + + $collection = new Collection([], FilterDTO::class); + $collection->add(new stdClass()); // Invalid item + } + + public function testCanFindItemByProperty(): void + { + $filter1 = new FilterDTO('price', '>', 100); + $filter2 = new FilterDTO('stock', '<', 50); + $collection = new Collection([$filter1, $filter2], FilterDTO::class); + + $result = $collection->findByProperty('field', 'stock'); + + $this->assertNotNull($result); + $this->assertSame($filter2, $result); + } + + public function testReturnsNullWhenItemNotFound(): void + { + $filter = new FilterDTO('price', '>', 100); + $collection = new Collection([$filter], FilterDTO::class); + + $result = $collection->findByProperty('field', 'nonexistent'); + + $this->assertNull($result); + } + + public function testCanCheckIfItemWithSpecificPropertyAndValueExists(): void + { + $filter = new FilterDTO('price', '>', 100); + $collection = new Collection([$filter], FilterDTO::class); + + $exists = $collection->hasValue('field', 'price'); + $notExists = $collection->hasValue('field', 'nonexistent'); + + $this->assertTrue($exists); + $this->assertFalse($notExists); + } + + public function testImplementsCountable(): void + { + $filter1 = new FilterDTO('price', '>', 100); + $filter2 = new FilterDTO('stock', '<', 50); + $collection = new Collection([$filter1, $filter2], FilterDTO::class); + + $this->assertCount(2, $collection); + } + + public function testImplementsIteratorAggregate(): void + { + $filter1 = new FilterDTO('price', '>', 100); + $filter2 = new FilterDTO('stock', '<', 50); + $collection = new Collection([$filter1, $filter2], FilterDTO::class); + + $iterator = $collection->getIterator(); + + $this->assertInstanceOf(ArrayIterator::class, $iterator); + $this->assertCount(2, $iterator); + } + + public function testImplementsArrayAccess(): void + { + $filter1 = new FilterDTO('price', '>', 100); + $filter2 = new FilterDTO('stock', '<', 50); + $collection = new Collection([], FilterDTO::class); + + // Adding items + $collection[] = $filter1; + $collection[] = $filter2; + + $this->assertCount(2, $collection); + $this->assertSame($filter1, $collection[0]); + $this->assertSame($filter2, $collection[1]); + + // Modifying an item + $collection[0] = $filter2; + $this->assertSame($filter2, $collection[0]); + + // Checking existence + $this->assertTrue(isset($collection[0])); + $this->assertFalse(isset($collection[5])); + + // Removing an item + unset($collection[0]); + $this->assertFalse(isset($collection[0])); + } + + public function testArrayAccessThrowsExceptionForInvalidType(): void + { + $collection = new Collection([], FilterDTO::class); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Item must be an instance of'); + + $collection[] = new stdClass(); // Invalid item + } + + public function testConstructorValidatesItems(): void + { + $this->expectException(InvalidArgumentException::class); + + $invalidItems = [new stdClass(), new FilterDTO('price', '>', 100)]; + new Collection($invalidItems, FilterDTO::class); + } + + public function testCanRetrieveAllItems(): void + { + $filter1 = new FilterDTO('price', '>', 100); + $filter2 = new FilterDTO('stock', '<', 50); + $collection = new Collection([$filter1, $filter2], FilterDTO::class); + + $allItems = $collection->all(); + + $this->assertCount(2, $allItems); + $this->assertSame($filter1, $allItems[0]); + $this->assertSame($filter2, $allItems[1]); + } + + public function testCanGetValueByProperty(): void + { + $filter1 = new FilterDTO('price', '>', 100); + $filter2 = new FilterDTO('category', '=', 'electronics'); + $collection = new Collection([$filter1, $filter2], FilterDTO::class); + + $value = $collection->getValueByProperty('field', 'price'); + $this->assertSame(100, $value); + + $value = $collection->getValueByProperty('field', 'category'); + $this->assertSame('electronics', $value); + } + + public function testReturnsNullIfPropertyNotFound(): void + { + $filter = new FilterDTO('price', '>', 100); + $collection = new Collection([$filter], FilterDTO::class); + + $value = $collection->getValueByProperty('field', 'nonexistent'); + $this->assertNull($value); + } + + public function testReturnsNullIfNoItemsInCollection(): void + { + $collection = new Collection([], FilterDTO::class); + + $value = $collection->getValueByProperty('field', 'price'); + $this->assertNull($value); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/HelpersTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/HelpersTest.php new file mode 100755 index 0000000..8c93403 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/HelpersTest.php @@ -0,0 +1,54 @@ +assertEquals('oc_some_table', db_table('some_table')); + } + + public function testDbColumnWithTableAndColumn(): void + { + $column = 'users.email'; + $expected = 'oc_users.email'; + + $this->assertEquals($expected, db_column($column)); + } + + public function testDbColumnWithoutTable(): void + { + $column = 'email'; + $expected = 'email'; + + $this->assertEquals($expected, db_column($column)); + } + + public function testDbColumnWithEmptyTable(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid column reference: '); + + db_column('.email'); + } + + public function testDbColumnWithEmptyColumn(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid column reference: '); + + db_column('users.'); + } + + public function testDbColumnWithEmptyTableAndColumn(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid column reference: '); + + db_column('.'); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/MySqlGrammarTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/MySqlGrammarTest.php new file mode 100755 index 0000000..42e722c --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/MySqlGrammarTest.php @@ -0,0 +1,222 @@ +grammar = new MySqlGrammar(); + } + + public function tearDown(): void + { + parent::tearDown(); + + m::close(); + } + + public function testCompileFrom(): void + { + $this->assertEquals( + 'FROM foobar AS alias', + $this->grammar->compileFrom(m::mock(Builder::class), ['table' => 'foobar', 'as' => 'alias']) + ); + + $this->assertEquals( + 'FROM foobar', + $this->grammar->compileFrom(m::mock(Builder::class), ['table' => 'foobar', 'as' => null]) + ); + } + + public function testCompileLimit(): void + { + $this->assertEquals('LIMIT 10', $this->grammar->compileLimit(m::mock(Builder::class), 10)); + } + + public function testCompileOffset(): void + { + $this->assertEquals('OFFSET 10', $this->grammar->compileOffset(m::mock(Builder::class), 10)); + } + + public function testCompileColumns(): void + { + $this->assertEquals('SELECT *', $this->grammar->compileColumns(m::mock(Builder::class), [])); + $this->assertEquals('SELECT foo, bar', $this->grammar->compileColumns(m::mock(Builder::class), [ + ['column' => 'foo', 'as' => null], + ['column' => 'bar', 'as' => null], + ])); + $this->assertEquals('SELECT foo AS bar, xxx AS yyy', $this->grammar->compileColumns( + m::mock(Builder::class), [ + ['column' => 'foo', 'as' => 'bar'], + ['column' => 'xxx', 'as' => 'yyy'], + ])); + } + + public function testCompileWheres(): void + { + $this->assertEquals('', $this->grammar->compileWheres(m::mock(Builder::class), [])); + $this->assertEquals('WHERE foo IS NOT NULL', $this->grammar->compileWheres(m::mock(Builder::class), [ + [ + 'type' => 'NotNull', + 'column' => 'foo', + 'boolean' => 'and', + ] + ])); + $this->assertEquals('WHERE foo IS NULL', $this->grammar->compileWheres(m::mock(Builder::class), [ + [ + 'type' => 'Null', + 'column' => 'foo', + 'boolean' => 'and', + ] + ])); + + $this->assertEquals('WHERE foo = ? OR bar = ? AND xxx IS NULL', $this->grammar->compileWheres( + m::mock(Builder::class), [ + [ + 'type' => 'Basic', + 'column' => 'foo', + 'operator' => '=', + 'value' => 'val', + 'boolean' => 'and', + ], + [ + 'type' => 'Basic', + 'column' => 'bar', + 'operator' => '=', + 'value' => 'val', + 'boolean' => 'or', + ], + [ + 'type' => 'Null', + 'column' => 'xxx', + 'boolean' => 'and', + ] + ])); + } + + public function testCompileOrders(): void + { + $this->assertEquals('ORDER BY foo DESC, bar ASC', $this->grammar->compileOrders( + m::mock(Builder::class), [ + ['column' => 'foo', 'direction' => 'DESC'], + ['column' => 'bar', 'direction' => 'ASC'], + ])); + } + + public function testWhereNotNull(): void + { + $this->assertEquals('foo IS NOT NULL', $this->grammar->whereNotNull([ + 'type' => 'NotNull', + 'column' => 'foo', + 'boolean' => 'and', + ])); + } + + public function testWhereNull(): void + { + $this->assertEquals('foo IS NULL', $this->grammar->whereNull([ + 'type' => 'Null', + 'column' => 'foo', + 'boolean' => 'and', + ])); + } + + public function testWhereBasic(): void + { + $this->assertEquals('foo = ?', $this->grammar->whereBasic([ + 'type' => 'Basic', + 'column' => 'foo', + 'operator' => '=', + 'value' => 'val', + 'boolean' => 'and', + ])); + } + + public function testConcatCompiled(): void + { + $this->assertEquals( + /** @lang text */ 'SELECT * FROM table WHERE foo = ? LIMIT 10', + $this->grammar->concatCompiled([ + 'wheres' => 'WHERE foo = ?', + 'limit' => 'LIMIT 10', + 'columns' => 'SELECT *', + 'from' => 'FROM table', + ]) + ); + } + + public function testCompileJoins(): void + { + $joinClause = m::mock(JoinClause::class); + $joinClause->table = 'table AS t'; + $joinClause->type = 'inner'; + $joinClause->first = 't.id'; + $joinClause->operator = '='; + $joinClause->second = 'parent.id'; + $joinClause->wheres = [ + [ + 'type' => 'Basic', + 'column' => 't.foo', + 'operator' => '=', + 'value' => 'bar', + 'boolean' => 'and', + ] + ]; + + $builder = m::mock(Builder::class); + + $this->assertEquals( + 'INNER JOIN table AS t ON t.id = parent.id AND t.foo = ?', + $this->grammar->compileJoins( + $builder, + [ + $joinClause, + ] + ) + ); + } + + public function testCompileWhereIn(): void + { + $this->assertEquals('foo IN (?, ?, ?, ?)', $this->grammar->whereIn([ + 'type' => 'In', + 'column' => 'foo', + 'operator' => 'IN', + 'value' => [1, 2, 3, 4], + 'boolean' => 'and', + ])); + } + + public function testCompileWhereNotIn(): void + { + $this->assertEquals('foo NOT IN (?, ?, ?, ?)', $this->grammar->whereIn([ + 'type' => 'In', + 'column' => 'foo', + 'operator' => 'NOT IN', + 'value' => [1, 2, 3, 4], + 'boolean' => 'and', + ])); + } + + public function testCompileDistinctColumns(): void + { + $mock = m::mock(Builder::class); + $mock->distinct = true; + + $this->assertEquals('SELECT DISTINCT foo, bar', $this->grammar->compileColumns($mock, [ + ['column' => 'foo', 'as' => null], + ['column' => 'bar', 'as' => null], + ])); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/RuleSerializerTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/RuleSerializerTest.php new file mode 100755 index 0000000..e58880b --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/RuleSerializerTest.php @@ -0,0 +1,46 @@ + new Criterion('string', [ + 'operator' => 'contains', + 'keyword' => 'foobar', + ]) + ] + ); + + $expected = [ + 'id' => 'RULE_PRODUCT_MODEL', + 'criteria' => [ + 'product_model' => + [ + 'type' => 'string', + 'params' => [ + 'operator' => 'contains', + 'keyword' => 'foobar', + ] + ] + ], + '__meta' => [ + 'group' => 'other', + ], + ]; + $actual = $rulesSerializer->toArray($productName); + + $this->assertEquals($expected, $actual); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/SettingsTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/SettingsTest.php new file mode 100755 index 0000000..63c109e --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/SettingsTest.php @@ -0,0 +1,119 @@ +settings = new Settings([ + 'app_name' => 'MyApp', + 'debug_mode' => true, + 'default_language' => 'en', + 'database' => [ + 'host' => 'localhost', + 'port' => 3306, + ], + ]); + } + + public function testGet(): void + { + $this->assertEquals('MyApp', $this->settings->get('app_name')); + $this->assertTrue($this->settings->get('debug_mode')); + $this->assertEquals('en', $this->settings->get('default_language')); + $this->assertEquals('localhost', $this->settings->get('database.host')); + } + + public function testGetDefault(): void + { + $this->assertNull($this->settings->get('non_existent_key')); + $this->assertEquals('default_value', $this->settings->get('non_existent_key', 'default_value')); + $this->assertEquals('default_host', $this->settings->get('database.non_existent', 'default_host')); + } + + public function testSet(): void + { + $this->settings->set('app_name', 'NewApp'); + $this->assertEquals('NewApp', $this->settings->get('app_name')); + + $this->settings->set('new_setting', 'new_value'); + $this->assertEquals('new_value', $this->settings->get('new_setting')); + + $this->settings->set('database.host', '127.0.0.1'); + $this->assertEquals('127.0.0.1', $this->settings->get('database.host')); + } + + public function testHas(): void + { + $this->assertTrue($this->settings->has('app_name')); + $this->assertTrue($this->settings->has('debug_mode')); + $this->assertFalse($this->settings->has('non_existent_key')); + $this->assertTrue($this->settings->has('database.host')); + $this->assertFalse($this->settings->has('database.non_existent')); + } + + public function testRemove(): void + { + $this->settings->remove('debug_mode'); + $this->assertFalse($this->settings->has('debug_mode')); + + $this->settings->remove('database.host'); + $this->assertFalse($this->settings->has('database.host')); + } + + public function testGetAll(): void + { + $expected = [ + 'app_name' => 'MyApp', + 'debug_mode' => true, + 'default_language' => 'en', + 'database' => [ + 'host' => 'localhost', + 'port' => 3306, + ], + ]; + + $this->assertEquals($expected, $this->settings->getAll()); + } + + public function testSetAll(): void + { + $newSettings = [ + 'app_name' => 'NewApp', + 'theme' => 'dark', + ]; + $this->settings->setAll($newSettings); + $this->assertEquals($newSettings, $this->settings->getAll()); + } + + public function testDotNotationGetAndSet(): void + { + $this->settings->set('database.username', 'root'); + $this->assertEquals('root', $this->settings->get('database.username')); + + $this->settings->set('app.env', 'production'); + $this->assertEquals('production', $this->settings->get('app.env')); + } + + public function testDotNotationHas(): void + { + $this->settings->set('cache.enabled', true); + $this->assertTrue($this->settings->has('cache.enabled')); + $this->assertFalse($this->settings->has('cache.non_existent')); + } + + public function testDotNotationRemove(): void + { + $this->settings->set('logging.level', 'debug'); + $this->assertTrue($this->settings->has('logging.level')); + + $this->settings->remove('logging.level'); + $this->assertFalse($this->settings->has('logging.level')); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/TranslatorTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/TranslatorTest.php new file mode 100755 index 0000000..14c292d --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/TranslatorTest.php @@ -0,0 +1,31 @@ + 'bar']); + + $this->assertEquals('bar', $translator->translate('foo')); + } + + public function testTranslateWithParams(): void + { + $translator = new Translator('ru', [ + 'example' => '{placeholder_1} foo {placeholder_2} bar {placeholder_3}', + ]); + + $placeholders = ['placeholder_1' => 'a', 'placeholder_2' => 'b', 'placeholder_3' => 'c']; + $expected = 'a foo b bar c'; + + $this->assertEquals( + $expected, + $translator->translate('example', $placeholders) + ); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Validator/ErrorBagTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Validator/ErrorBagTest.php old mode 100644 new mode 100755 similarity index 93% rename from module/oc_telegram_shop/upload/oc_telegram_shop/tests/Validator/ErrorBagTest.php rename to module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Validator/ErrorBagTest.php index de57183..34896ca --- a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Validator/ErrorBagTest.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Validator/ErrorBagTest.php @@ -1,6 +1,6 @@ NOW() + ) +WHERE + COALESCE( + product_specials_4bf5415bae7c037731801559d0410697.price, + products.price + ) BETWEEN 100 + AND 200 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/input.json new file mode 100755 index 0000000..77dd2dd --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "between", + "value": { + "from": 100, + "to": 100 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/output.sql new file mode 100755 index 0000000..762e530 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_equals/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_f1ca65a396fc2e41a23a3661b84d1519 ON products.product_id = product_specials_f1ca65a396fc2e41a23a3661b84d1519.product_id + AND product_specials_f1ca65a396fc2e41a23a3661b84d1519.customer_group_id = 1 + AND ( + product_specials_f1ca65a396fc2e41a23a3661b84d1519.date_start = '0000-00-00' + OR product_specials_f1ca65a396fc2e41a23a3661b84d1519.date_start < NOW() + ) + AND ( + product_specials_f1ca65a396fc2e41a23a3661b84d1519.date_end = '0000-00-00' + OR product_specials_f1ca65a396fc2e41a23a3661b84d1519.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_f1ca65a396fc2e41a23a3661b84d1519.price, + products.price + ) = 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/input.json new file mode 100755 index 0000000..96dd61e --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "between", + "value": { + "from": "", + "to": 200 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/output.sql new file mode 100755 index 0000000..6e5aa63 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_empty_string/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_f0c4ece67502916e844720b12abc54a9 ON products.product_id = product_specials_f0c4ece67502916e844720b12abc54a9.product_id + AND product_specials_f0c4ece67502916e844720b12abc54a9.customer_group_id = 1 + AND ( + product_specials_f0c4ece67502916e844720b12abc54a9.date_start = '0000-00-00' + OR product_specials_f0c4ece67502916e844720b12abc54a9.date_start < NOW() + ) + AND ( + product_specials_f0c4ece67502916e844720b12abc54a9.date_end = '0000-00-00' + OR product_specials_f0c4ece67502916e844720b12abc54a9.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_f0c4ece67502916e844720b12abc54a9.price, + products.price + ) <= 200 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/input.json new file mode 100755 index 0000000..194a326 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "between", + "value": { + "from": null, + "to": 200 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/output.sql new file mode 100755 index 0000000..37ce826 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_from_null/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_5173bd7b8335ab5a9f4e37227d0c317f ON products.product_id = product_specials_5173bd7b8335ab5a9f4e37227d0c317f.product_id + AND product_specials_5173bd7b8335ab5a9f4e37227d0c317f.customer_group_id = 1 + AND ( + product_specials_5173bd7b8335ab5a9f4e37227d0c317f.date_start = '0000-00-00' + OR product_specials_5173bd7b8335ab5a9f4e37227d0c317f.date_start < NOW() + ) + AND ( + product_specials_5173bd7b8335ab5a9f4e37227d0c317f.date_end = '0000-00-00' + OR product_specials_5173bd7b8335ab5a9f4e37227d0c317f.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_5173bd7b8335ab5a9f4e37227d0c317f.price, + products.price + ) <= 200 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/input.json new file mode 100755 index 0000000..8cd2573 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "between", + "value": { + "from": "100", + "to": "200" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/output.sql new file mode 100755 index 0000000..bf57b81 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/between_num_as_string/output.sql @@ -0,0 +1,29 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_7b934dfdfc5b809270875934c10acc06 ON products.product_id = product_specials_7b934dfdfc5b809270875934c10acc06.product_id + AND product_specials_7b934dfdfc5b809270875934c10acc06.customer_group_id = 1 + AND ( + product_specials_7b934dfdfc5b809270875934c10acc06.date_start = '0000-00-00' + OR product_specials_7b934dfdfc5b809270875934c10acc06.date_start < NOW() + ) + AND ( + product_specials_7b934dfdfc5b809270875934c10acc06.date_end = '0000-00-00' + OR product_specials_7b934dfdfc5b809270875934c10acc06.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_7b934dfdfc5b809270875934c10acc06.price, + products.price + ) BETWEEN 100 + AND 200 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/input.json new file mode 100755 index 0000000..88a0577 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "equals", + "value": { + "from": 100, + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/output.sql new file mode 100755 index 0000000..d8b44f3 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_51a33d8bcfeece60de797c18e3800075 ON products.product_id = product_specials_51a33d8bcfeece60de797c18e3800075.product_id + AND product_specials_51a33d8bcfeece60de797c18e3800075.customer_group_id = 1 + AND ( + product_specials_51a33d8bcfeece60de797c18e3800075.date_start = '0000-00-00' + OR product_specials_51a33d8bcfeece60de797c18e3800075.date_start < NOW() + ) + AND ( + product_specials_51a33d8bcfeece60de797c18e3800075.date_end = '0000-00-00' + OR product_specials_51a33d8bcfeece60de797c18e3800075.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_51a33d8bcfeece60de797c18e3800075.price, + products.price + ) = 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/input.json new file mode 100755 index 0000000..49a4d46 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "equals", + "value": { + "from": "100", + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/output.sql new file mode 100755 index 0000000..cbb55bb --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/equals_num_as_string/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_7de8997be994ada47d62cdfe2b7369c9 ON products.product_id = product_specials_7de8997be994ada47d62cdfe2b7369c9.product_id + AND product_specials_7de8997be994ada47d62cdfe2b7369c9.customer_group_id = 1 + AND ( + product_specials_7de8997be994ada47d62cdfe2b7369c9.date_start = '0000-00-00' + OR product_specials_7de8997be994ada47d62cdfe2b7369c9.date_start < NOW() + ) + AND ( + product_specials_7de8997be994ada47d62cdfe2b7369c9.date_end = '0000-00-00' + OR product_specials_7de8997be994ada47d62cdfe2b7369c9.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_7de8997be994ada47d62cdfe2b7369c9.price, + products.price + ) = 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/input.json new file mode 100755 index 0000000..632138b --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "greater", + "value": { + "from": 100, + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/output.sql new file mode 100755 index 0000000..2201a9b --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_78ccac1dc1d86997e6eac0613f2eb5f3 ON products.product_id = product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.product_id + AND product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.customer_group_id = 1 + AND ( + product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.date_start = '0000-00-00' + OR product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.date_start < NOW() + ) + AND ( + product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.date_end = '0000-00-00' + OR product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_78ccac1dc1d86997e6eac0613f2eb5f3.price, + products.price + ) > 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/input.json new file mode 100755 index 0000000..f102762 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "greater_or_equals", + "value": { + "from": 100, + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/output.sql new file mode 100755 index 0000000..2ff0599 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/greater_or_equals/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_5816f1e65a3d296a26635f070d0439bc ON products.product_id = product_specials_5816f1e65a3d296a26635f070d0439bc.product_id + AND product_specials_5816f1e65a3d296a26635f070d0439bc.customer_group_id = 1 + AND ( + product_specials_5816f1e65a3d296a26635f070d0439bc.date_start = '0000-00-00' + OR product_specials_5816f1e65a3d296a26635f070d0439bc.date_start < NOW() + ) + AND ( + product_specials_5816f1e65a3d296a26635f070d0439bc.date_end = '0000-00-00' + OR product_specials_5816f1e65a3d296a26635f070d0439bc.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_5816f1e65a3d296a26635f070d0439bc.price, + products.price + ) >= 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/input.json new file mode 100755 index 0000000..82533f3 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/input.json @@ -0,0 +1,25 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "equals", + "value": { + "from": 100, + "to": null + } + } + }, + "include_discounts": { + "type": "boolean", + "params": { + "value": true + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/output.sql new file mode 100755 index 0000000..d8b44f3 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_discounts/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_51a33d8bcfeece60de797c18e3800075 ON products.product_id = product_specials_51a33d8bcfeece60de797c18e3800075.product_id + AND product_specials_51a33d8bcfeece60de797c18e3800075.customer_group_id = 1 + AND ( + product_specials_51a33d8bcfeece60de797c18e3800075.date_start = '0000-00-00' + OR product_specials_51a33d8bcfeece60de797c18e3800075.date_start < NOW() + ) + AND ( + product_specials_51a33d8bcfeece60de797c18e3800075.date_end = '0000-00-00' + OR product_specials_51a33d8bcfeece60de797c18e3800075.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_51a33d8bcfeece60de797c18e3800075.price, + products.price + ) = 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/input.json new file mode 100755 index 0000000..dea639f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/input.json @@ -0,0 +1,25 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "equals", + "value": { + "from": 100, + "to": null + } + } + }, + "include_specials": { + "type": "boolean", + "params": { + "value": true + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/output.sql new file mode 100755 index 0000000..d8b44f3 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/include_specials/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_51a33d8bcfeece60de797c18e3800075 ON products.product_id = product_specials_51a33d8bcfeece60de797c18e3800075.product_id + AND product_specials_51a33d8bcfeece60de797c18e3800075.customer_group_id = 1 + AND ( + product_specials_51a33d8bcfeece60de797c18e3800075.date_start = '0000-00-00' + OR product_specials_51a33d8bcfeece60de797c18e3800075.date_start < NOW() + ) + AND ( + product_specials_51a33d8bcfeece60de797c18e3800075.date_end = '0000-00-00' + OR product_specials_51a33d8bcfeece60de797c18e3800075.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_51a33d8bcfeece60de797c18e3800075.price, + products.price + ) = 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/input.json new file mode 100755 index 0000000..b041812 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "less", + "value": { + "from": 100, + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/output.sql new file mode 100755 index 0000000..61224d0 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_cf0f856fa900f18f5c8c0a57357532f5 ON products.product_id = product_specials_cf0f856fa900f18f5c8c0a57357532f5.product_id + AND product_specials_cf0f856fa900f18f5c8c0a57357532f5.customer_group_id = 1 + AND ( + product_specials_cf0f856fa900f18f5c8c0a57357532f5.date_start = '0000-00-00' + OR product_specials_cf0f856fa900f18f5c8c0a57357532f5.date_start < NOW() + ) + AND ( + product_specials_cf0f856fa900f18f5c8c0a57357532f5.date_end = '0000-00-00' + OR product_specials_cf0f856fa900f18f5c8c0a57357532f5.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_cf0f856fa900f18f5c8c0a57357532f5.price, + products.price + ) < 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/input.json new file mode 100755 index 0000000..e5d001e --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "less_or_equals", + "value": { + "from": 100, + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/output.sql new file mode 100755 index 0000000..3269ce2 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/less_or_equals/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_08e0a0248dc92591ae55fdd966728ba0 ON products.product_id = product_specials_08e0a0248dc92591ae55fdd966728ba0.product_id + AND product_specials_08e0a0248dc92591ae55fdd966728ba0.customer_group_id = 1 + AND ( + product_specials_08e0a0248dc92591ae55fdd966728ba0.date_start = '0000-00-00' + OR product_specials_08e0a0248dc92591ae55fdd966728ba0.date_start < NOW() + ) + AND ( + product_specials_08e0a0248dc92591ae55fdd966728ba0.date_end = '0000-00-00' + OR product_specials_08e0a0248dc92591ae55fdd966728ba0.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_08e0a0248dc92591ae55fdd966728ba0.price, + products.price + ) <= 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/input.json new file mode 100755 index 0000000..40fb22f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_PRICE": { + "criteria": { + "product_price": { + "type": "number", + "params": { + "operator": "not_equals", + "value": { + "from": 100, + "to": null + } + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/output.sql new file mode 100755 index 0000000..2ae0cc8 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_price/not_equals/output.sql @@ -0,0 +1,28 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 + LEFT JOIN oc_product_special AS product_specials_47d71067324729b89b13414a6ea869e0 ON products.product_id = product_specials_47d71067324729b89b13414a6ea869e0.product_id + AND product_specials_47d71067324729b89b13414a6ea869e0.customer_group_id = 1 + AND ( + product_specials_47d71067324729b89b13414a6ea869e0.date_start = '0000-00-00' + OR product_specials_47d71067324729b89b13414a6ea869e0.date_start < NOW() + ) + AND ( + product_specials_47d71067324729b89b13414a6ea869e0.date_end = '0000-00-00' + OR product_specials_47d71067324729b89b13414a6ea869e0.date_end > NOW() + ) +WHERE + COALESCE( + product_specials_47d71067324729b89b13414a6ea869e0.price, + products.price + ) <> 100 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/input.json new file mode 100755 index 0000000..5d3fae1 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "between", + "value": [ + 100, + 200 + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/output.sql new file mode 100755 index 0000000..236953a --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between/output.sql @@ -0,0 +1,16 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity BETWEEN 100 + AND 200 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/input.json new file mode 100755 index 0000000..b652584 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "between", + "value": [ + "100", + "200" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/output.sql new file mode 100755 index 0000000..236953a --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/output.sql @@ -0,0 +1,16 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity BETWEEN 100 + AND 200 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/input.json new file mode 100755 index 0000000..aaa29e0 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "equals", + "value": [ + 10, + null + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/output.sql new file mode 100755 index 0000000..ce0d49f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/equals/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity = 10 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/input.json new file mode 100755 index 0000000..dbd1159 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "greater", + "value": [ + 10, + null + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/output.sql new file mode 100755 index 0000000..679f082 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity > 10 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/input.json new file mode 100755 index 0000000..08c2fbb --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "greater_or_equals", + "value": [ + 10, + null + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/output.sql new file mode 100755 index 0000000..9bc83b4 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity >= 10 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/input.json new file mode 100755 index 0000000..6068cd6 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "less", + "value": [ + 10, + null + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/output.sql new file mode 100755 index 0000000..97f6205 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity < 10 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/input.json new file mode 100755 index 0000000..6fe2390 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "less_or_equals", + "value": [ + 10, + null + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/output.sql new file mode 100755 index 0000000..0995029 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/less_or_equals/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity <= 10 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/input.json new file mode 100755 index 0000000..0f9aeff --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/input.json @@ -0,0 +1,19 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_QUANTITY": { + "criteria": { + "product_quantity": { + "type": "number", + "params": { + "operator": "not_equals", + "value": [ + 10, + null + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/output.sql new file mode 100755 index 0000000..fb2ecbc --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_quantity/not_equals/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.quantity <> 10 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/input.json new file mode 100755 index 0000000..f5f1f3d --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/input.json @@ -0,0 +1,16 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_STATUS": { + "criteria": { + "product_status": { + "type": "boolean", + "params": { + "operator": "equals", + "value": false + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/output.sql new file mode 100755 index 0000000..7eb1912 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_false/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.status = 0 \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/input.json b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/input.json new file mode 100755 index 0000000..2934c0c --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/input.json @@ -0,0 +1,16 @@ +{ + "operand": "AND", + "rules": { + "RULE_PRODUCT_STATUS": { + "criteria": { + "product_status": { + "type": "boolean", + "params": { + "operator": "equals", + "value": true + } + } + } + } + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/output.sql b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/output.sql new file mode 100755 index 0000000..b43d034 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/fixtures/criteria_builder/product_status/equals_true/output.sql @@ -0,0 +1,15 @@ +SELECT + products.product_id AS product_id, + products.image AS image, + product_description.name AS name, + products.model AS model, + products.price AS price, + products.quantity AS quantity, + products.status AS STATUS, + products.noindex AS noindex +FROM + oc_product AS products + INNER JOIN oc_product_description AS product_description ON products.product_id = product_description.product_id + AND product_description.language_id = 1 +WHERE + products.status = 1 \ No newline at end of file diff --git a/spa/index.html b/spa/index.html index 6d38fb5..9738b41 100644 --- a/spa/index.html +++ b/spa/index.html @@ -3,7 +3,7 @@ - + OpenCart Telegram Mini App diff --git a/spa/package-lock.json b/spa/package-lock.json index 3cb80e5..e1679e6 100644 --- a/spa/package-lock.json +++ b/spa/package-lock.json @@ -12,6 +12,7 @@ "@tailwindcss/vite": "^4.1.11", "@vueuse/core": "^13.5.0", "crypto-js": "^4.2.0", + "js-md5": "^0.8.3", "ofetch": "^1.4.1", "pinia": "^3.0.3", "swiper": "^11.2.10", @@ -26,20 +27,7 @@ "daisyui": "^5.0.46", "postcss": "^8.5.6", "tailwindcss": "^4.1.11", - "vite": "^7.0.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" + "vite": "^7.1.7" } }, "node_modules/@babel/helper-string-parser": { @@ -61,12 +49,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -76,9 +64,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.3.tgz", - "integrity": "sha512-LKYxD2CIfocUFNREQ1yk+dW+8OH8CRqmgatBZYXb+XhuObO8wsDpEoCNri5bKld9cnj8xukqZjxSX8p1YiRF8Q==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.4.tgz", + "integrity": "sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==", "license": "MIT", "dependencies": { "core-js-pure": "^3.43.0" @@ -88,9 +76,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -101,9 +89,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", "cpu": [ "ppc64" ], @@ -117,9 +105,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", "cpu": [ "arm" ], @@ -133,9 +121,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", "cpu": [ "arm64" ], @@ -149,9 +137,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", "cpu": [ "x64" ], @@ -165,9 +153,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", "cpu": [ "arm64" ], @@ -181,9 +169,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", "cpu": [ "x64" ], @@ -197,9 +185,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", "cpu": [ "arm64" ], @@ -213,9 +201,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", "cpu": [ "x64" ], @@ -229,9 +217,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", "cpu": [ "arm" ], @@ -245,9 +233,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", "cpu": [ "arm64" ], @@ -261,9 +249,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", "cpu": [ "ia32" ], @@ -277,9 +265,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", "cpu": [ "loong64" ], @@ -293,9 +281,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", "cpu": [ "mips64el" ], @@ -309,9 +297,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", "cpu": [ "ppc64" ], @@ -325,9 +313,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", "cpu": [ "riscv64" ], @@ -341,9 +329,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", "cpu": [ "s390x" ], @@ -357,9 +345,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", "cpu": [ "x64" ], @@ -373,9 +361,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", "cpu": [ "arm64" ], @@ -389,9 +377,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", "cpu": [ "x64" ], @@ -405,9 +393,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", "cpu": [ "arm64" ], @@ -421,9 +409,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", "cpu": [ "x64" ], @@ -437,9 +425,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", "cpu": [ "arm64" ], @@ -453,9 +441,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", "cpu": [ "x64" ], @@ -469,9 +457,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", "cpu": [ "arm64" ], @@ -485,9 +473,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", "cpu": [ "ia32" ], @@ -501,9 +489,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", "cpu": [ "x64" ], @@ -538,15 +526,25 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -557,15 +555,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -573,16 +571,16 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.19", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", - "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", - "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", "cpu": [ "arm" ], @@ -593,9 +591,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", - "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", "cpu": [ "arm64" ], @@ -606,9 +604,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", - "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", "cpu": [ "arm64" ], @@ -619,9 +617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", - "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", "cpu": [ "x64" ], @@ -632,9 +630,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", - "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", "cpu": [ "arm64" ], @@ -645,9 +643,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", - "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", "cpu": [ "x64" ], @@ -658,9 +656,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", - "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", "cpu": [ "arm" ], @@ -671,9 +669,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", - "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", "cpu": [ "arm" ], @@ -684,9 +682,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", - "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", "cpu": [ "arm64" ], @@ -697,9 +695,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", - "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", "cpu": [ "arm64" ], @@ -709,10 +707,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", - "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", "cpu": [ "loong64" ], @@ -722,10 +720,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", - "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", "cpu": [ "ppc64" ], @@ -736,9 +734,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", - "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", "cpu": [ "riscv64" ], @@ -749,9 +747,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", - "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", "cpu": [ "riscv64" ], @@ -762,9 +760,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", - "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", "cpu": [ "s390x" ], @@ -775,9 +773,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", - "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", "cpu": [ "x64" ], @@ -788,9 +786,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", - "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", "cpu": [ "x64" ], @@ -800,10 +798,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", - "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", "cpu": [ "arm64" ], @@ -814,9 +825,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", - "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", "cpu": [ "ia32" ], @@ -826,10 +837,23 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", - "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", "cpu": [ "x64" ], @@ -840,24 +864,24 @@ ] }, "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", "lightningcss": "1.30.1", - "magic-string": "^0.30.17", + "magic-string": "^0.30.18", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" + "tailwindcss": "4.1.13" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -868,24 +892,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + "@tailwindcss/oxide-android-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-x64": "4.1.13", + "@tailwindcss/oxide-freebsd-x64": "4.1.13", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", "cpu": [ "arm64" ], @@ -899,9 +923,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", "cpu": [ "arm64" ], @@ -915,9 +939,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", "cpu": [ "x64" ], @@ -931,9 +955,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", "cpu": [ "x64" ], @@ -947,9 +971,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", "cpu": [ "arm" ], @@ -963,9 +987,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", "cpu": [ "arm64" ], @@ -979,9 +1003,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", "cpu": [ "arm64" ], @@ -995,9 +1019,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", "cpu": [ "x64" ], @@ -1011,9 +1035,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", "cpu": [ "x64" ], @@ -1027,9 +1051,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1044,11 +1068,11 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "engines": { @@ -1056,9 +1080,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", "cpu": [ "arm64" ], @@ -1072,9 +1096,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", "cpu": [ "x64" ], @@ -1088,14 +1112,14 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", - "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz", + "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "tailwindcss": "4.1.11" + "@tailwindcss/node": "4.1.13", + "@tailwindcss/oxide": "4.1.13", + "tailwindcss": "4.1.13" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" @@ -1114,13 +1138,13 @@ "license": "MIT" }, "node_modules/@vitejs/plugin-vue": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz", - "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.19" + "@rolldown/pluginutils": "1.0.0-beta.29" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -1131,60 +1155,63 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz", - "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.5", - "@vue/shared": "3.5.17", + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", - "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", - "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.5", - "@vue/compiler-core": "3.5.17", - "@vue/compiler-dom": "3.5.17", - "@vue/compiler-ssr": "3.5.17", - "@vue/shared": "3.5.17", + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", "estree-walker": "^2.0.2", - "magic-string": "^0.30.17", + "magic-string": "^0.30.19", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", - "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/devtools-api": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", - "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", - "license": "MIT" + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } }, "node_modules/@vue/devtools-kit": { "version": "7.7.7", @@ -1211,64 +1238,64 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz", - "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", + "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.17" + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz", - "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", + "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz", - "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", + "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.17", - "@vue/runtime-core": "3.5.17", - "@vue/shared": "3.5.17", + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz", - "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", + "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { - "vue": "3.5.17" + "vue": "3.5.22" } }, "node_modules/@vue/shared": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz", - "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", "license": "MIT" }, "node_modules/@vueuse/core": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.5.0.tgz", - "integrity": "sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.9.0.tgz", + "integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==", "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "13.5.0", - "@vueuse/shared": "13.5.0" + "@vueuse/metadata": "13.9.0", + "@vueuse/shared": "13.9.0" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -1278,18 +1305,18 @@ } }, "node_modules/@vueuse/metadata": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.5.0.tgz", - "integrity": "sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.9.0.tgz", + "integrity": "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.5.0.tgz", - "integrity": "sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.9.0.tgz", + "integrity": "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" @@ -1336,19 +1363,29 @@ "postcss": "^8.1.0" } }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/birpc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", - "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz", + "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", "dev": true, "funding": [ { @@ -1366,9 +1403,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { @@ -1379,9 +1417,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", "dev": true, "funding": [ { @@ -1424,9 +1462,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.45.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.45.0.tgz", - "integrity": "sha512-OtwjqcDpY2X/eIIg1ol/n0y/X8A9foliaNt1dSK0gV3J2/zw+89FcNG3mPK+N8YWts4ZFUPxnrAzsxs/lf8yDA==", + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.45.1.tgz", + "integrity": "sha512-OHnWFKgTUshEU8MK+lOs1H8kC8GkTi9Z1tvNkxrCcw9wl3MJIO7q2ld77wjWn4/xuGrVu2X+nME1iIIPBSdyEQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -1447,9 +1485,9 @@ "license": "MIT" }, "node_modules/daisyui": { - "version": "5.0.46", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.46.tgz", - "integrity": "sha512-vMDZK1tI/bOb2Mc3Mk5WpquBG3ZqBz1YKZ0xDlvpOvey60dOS4/5Qhdowq1HndbQl7PgDLDYysxAjjUjwR7/eQ==", + "version": "5.1.26", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.1.26.tgz", + "integrity": "sha512-keoOGnZ7aLHWlUBUfyCj7tIGkXXTFAO4U6BFBMBSSyGYHY6aP+onIuBQNTwRD09g3yg5aNLxvIY3pNS8bwPUgQ==", "dev": true, "license": "MIT", "funding": { @@ -1463,25 +1501,25 @@ "license": "MIT" }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", + "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/electron-to-chromium": { - "version": "1.5.180", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz", - "integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==", + "version": "1.5.228", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", + "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", "dev": true, "license": "ISC" }, "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -1504,9 +1542,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -1516,32 +1554,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" } }, "node_modules/escalade": { @@ -1561,10 +1599,13 @@ "license": "MIT" }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -1639,14 +1680,20 @@ } }, "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==", + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", @@ -1876,12 +1923,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/minipass": { @@ -1894,9 +1941,9 @@ } }, "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -1911,21 +1958,6 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1945,15 +1977,15 @@ } }, "node_modules/node-fetch-native": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", - "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", "dev": true, "license": "MIT" }, @@ -1991,9 +2023,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -2023,15 +2055,6 @@ } } }, - "node_modules/pinia/node_modules/@vue/devtools-api": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", - "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^7.7.7" - } - }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -2074,9 +2097,9 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", - "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -2089,26 +2112,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.2", - "@rollup/rollup-android-arm64": "4.44.2", - "@rollup/rollup-darwin-arm64": "4.44.2", - "@rollup/rollup-darwin-x64": "4.44.2", - "@rollup/rollup-freebsd-arm64": "4.44.2", - "@rollup/rollup-freebsd-x64": "4.44.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", - "@rollup/rollup-linux-arm-musleabihf": "4.44.2", - "@rollup/rollup-linux-arm64-gnu": "4.44.2", - "@rollup/rollup-linux-arm64-musl": "4.44.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", - "@rollup/rollup-linux-riscv64-gnu": "4.44.2", - "@rollup/rollup-linux-riscv64-musl": "4.44.2", - "@rollup/rollup-linux-s390x-gnu": "4.44.2", - "@rollup/rollup-linux-x64-gnu": "4.44.2", - "@rollup/rollup-linux-x64-musl": "4.44.2", - "@rollup/rollup-win32-arm64-msvc": "4.44.2", - "@rollup/rollup-win32-ia32-msvc": "4.44.2", - "@rollup/rollup-win32-x64-msvc": "4.44.2", + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" } }, @@ -2162,31 +2187,34 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -2194,13 +2222,13 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -2247,17 +2275,17 @@ } }, "node_modules/vite": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.3.tgz", - "integrity": "sha512-y2L5oJZF7bj4c0jgGYgBNSdIu+5HF+m68rn2cQXFbGoShdhV1phX9rbnxy9YXj82aS8MMsCLAAFkRxZeWdldrQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -2321,16 +2349,16 @@ } }, "node_modules/vue": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz", - "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", + "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.17", - "@vue/compiler-sfc": "3.5.17", - "@vue/runtime-dom": "3.5.17", - "@vue/server-renderer": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { "typescript": "*" @@ -2395,10 +2423,16 @@ "vue": "^3.2.0" } }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, "node_modules/vue-tg": { - "version": "0.9.0-beta.10", - "resolved": "https://registry.npmjs.org/vue-tg/-/vue-tg-0.9.0-beta.10.tgz", - "integrity": "sha512-ymcOZ4bUa1x+2nveAeNaVWiFsl8E7Itudyz4DKxYVwpE7ApZYwO/go9dchTKP4NIUaUDSeRqYro6Cmlh8xhjYA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/vue-tg/-/vue-tg-0.9.0.tgz", + "integrity": "sha512-CqIpKFj8/KPBSecsdyuaLm3pA09YCpjHhe/rM7tDEAa8dpLYo1zTULDMczlh2JgVcX06xzJEbSe/OAcoW1Tl7Q==", "license": "MIT", "peerDependencies": { "vue": "^3" diff --git a/spa/package.json b/spa/package.json index 7cd2e12..204f8c7 100644 --- a/spa/package.json +++ b/spa/package.json @@ -13,6 +13,7 @@ "@tailwindcss/vite": "^4.1.11", "@vueuse/core": "^13.5.0", "crypto-js": "^4.2.0", + "js-md5": "^0.8.3", "ofetch": "^1.4.1", "pinia": "^3.0.3", "swiper": "^11.2.10", @@ -27,6 +28,6 @@ "daisyui": "^5.0.46", "postcss": "^8.5.6", "tailwindcss": "^4.1.11", - "vite": "^7.0.3" + "vite": "^7.1.7" } } diff --git a/spa/src/App.vue b/spa/src/App.vue index 1c0d747..e3f2f9c 100644 --- a/spa/src/App.vue +++ b/spa/src/App.vue @@ -2,8 +2,8 @@
- - + + @@ -11,8 +11,8 @@ - - - diff --git a/spa/src/components/BottomPanel.vue b/spa/src/components/BottomPanel.vue new file mode 100644 index 0000000..0206d1b --- /dev/null +++ b/spa/src/components/BottomPanel.vue @@ -0,0 +1,5 @@ + diff --git a/spa/src/components/Icons/IconFunnel.vue b/spa/src/components/Icons/IconFunnel.vue new file mode 100644 index 0000000..c707170 --- /dev/null +++ b/spa/src/components/Icons/IconFunnel.vue @@ -0,0 +1,7 @@ + diff --git a/spa/src/components/ProductFilters/Components/ForMainPage.vue b/spa/src/components/ProductFilters/Components/ForMainPage.vue new file mode 100644 index 0000000..706ae4c --- /dev/null +++ b/spa/src/components/ProductFilters/Components/ForMainPage.vue @@ -0,0 +1,21 @@ + + + diff --git a/spa/src/components/ProductFilters/Components/ProductPrice.vue b/spa/src/components/ProductFilters/Components/ProductPrice.vue new file mode 100644 index 0000000..72d1486 --- /dev/null +++ b/spa/src/components/ProductFilters/Components/ProductPrice.vue @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/spa/src/components/ProductFilters/Filters.vue b/spa/src/components/ProductFilters/Filters.vue new file mode 100644 index 0000000..a051fd7 --- /dev/null +++ b/spa/src/components/ProductFilters/Filters.vue @@ -0,0 +1,105 @@ + + + diff --git a/spa/src/components/ProductFilters/filters.js b/spa/src/components/ProductFilters/filters.js new file mode 100644 index 0000000..4f60b56 --- /dev/null +++ b/spa/src/components/ProductFilters/filters.js @@ -0,0 +1,31 @@ +export const FILTERS_MAIN_PAGE_DEFAULT = { + operand: "AND", + rules: { + RULE_PRODUCT_PRICE: { + criteria: { + "product_price": + { + "type": "number", + "params": { + "operator": "between", + "value": { + "from": "", + "to": "" + }, + }, + }, + }, + }, + RULE_PRODUCT_FOR_MAIN_PAGE: { + "criteria": { + "product_for_main_page": { + "type": "boolean", + "params": { + "operator": "equals", + "value": true + } + } + } + }, + }, +}; diff --git a/spa/src/components/ProductsList.vue b/spa/src/components/ProductsList.vue index 7331985..e880060 100644 --- a/spa/src/components/ProductsList.vue +++ b/spa/src/components/ProductsList.vue @@ -31,7 +31,7 @@
{{ settings.noMoreProductsMessage }} @@ -56,89 +56,24 @@ import NoProducts from "@/components/NoProducts.vue"; import ProductImageSwiper from "@/components/ProductImageSwiper.vue"; import {useProductsStore} from "@/stores/ProductsStore.js"; import {useInfiniteScroll} from '@vueuse/core'; -import {useRoute} from "vue-router"; import {useSettingsStore} from "@/stores/SettingsStore.js"; -import {nextTick, onMounted, onUnmounted, ref, watch} from "vue"; +import {ref} from "vue"; -const route = useRoute(); -const categoryId = route.params.category_id ?? null; const productsStore = useProductsStore(); const settings = useSettingsStore(); const bottom = ref(null); function haptic() { window.Telegram.WebApp.HapticFeedback.selectionChanged(); - productsStore.savedScrollY = window.scrollY; - console.log("Store scrollY: ", productsStore.savedScrollY); -} - -async function loadMore() { - if (productsStore.isLoading || productsStore.hasMore === false) return; - - console.debug("Loading more..."); - console.debug("Page: ", productsStore.page); - - productsStore.isLoading = true; - try { - const response = await productsStore.fetchProducts(categoryId, productsStore.page, true); - productsStore.hasMore = response.meta.hasMore ?? false; - productsStore.products.data.push(...response.data); - productsStore.products.meta.currentCategoryName = response.meta.currentCategoryName; - productsStore.page++; - - console.log("Loaded products: ", productsStore.products.data.length); - console.log("Has More? ", productsStore.hasMore); - } catch (e) { - console.error('Ошибка загрузки', e) - } finally { - productsStore.isLoading = false; - productsStore.loadFinished = true; - } + // productsStore.savedScrollY = window.scrollY; + // console.log("Store scrollY: ", productsStore.savedScrollY); } useInfiniteScroll( bottom, - loadMore, + async () => await productsStore.loadMore(), {distance: 1000} ) - -watch(() => route.params.id, async newId => { - if (newId !== productsStore.savedCategoryId) { - productsStore.reset() - productsStore.savedCategoryId = newId - await loadMore() - } -}); - -function handleClickOutside(e) { - if (!e.target.closest('input, textarea')) { - document.activeElement?.blur() - } -} - -onUnmounted(() => { - document.removeEventListener('click', handleClickOutside); -}); - -onMounted(async () => { - document.addEventListener('click', handleClickOutside); - const saved = productsStore.savedCategoryId === categoryId; - if (saved && productsStore.products.data.length > 0) { - await nextTick(); - // повторяем до тех пор, пока высота не станет больше savedScrollY - const interval = setInterval(() => { - const maxScroll = document.documentElement.scrollHeight - window.innerHeight - if (maxScroll >= productsStore.savedScrollY) { - window.scrollTo(0, productsStore.savedScrollY) - clearInterval(interval); - } - }, 50); - } else { - productsStore.reset(); - productsStore.savedCategoryId = categoryId; - await loadMore(); - } -})