From 3abcb18f0c1396d2362cb915c9d2db41f698ead7 Mon Sep 17 00:00:00 2001 From: Nikita Kiselev Date: Wed, 11 Mar 2026 22:08:41 +0300 Subject: [PATCH] Squashed commit message --- .cursor/agents.md | 64 + .cursor/config.json | 44 + .cursor/features/acme-pulse-heartbeat.md | 38 + .cursor/prompts/api-generation.md | 127 + .cursor/prompts/changelog.md | 101 + .cursor/prompts/documentation.md | 61 + .cursor/prompts/refactoring.md | 87 + .cursor/prompts/testing.md | 52 + .cursor/rules/architecture.md | 201 + .cursor/rules/form-builder.md | 70 + .cursor/rules/javascript.md | 332 + .cursor/rules/php.md | 243 + .cursor/rules/vue.md | 370 + .cursorignore | 98 + .github/workflows/main.yaml | 221 + .gitignore | 31 + CHANGELOG.md | 543 ++ Makefile | 83 + README.md | 39 + backend/src/.env.example | 12 + backend/src/app/Adapters/OcCartAdapter.php | 7 + .../Adapters/OcModelCatalogProductAdapter.php | 31 + backend/src/app/ApplicationFactory.php | 44 + backend/src/app/DTO/Settings/AppDTO.php | 98 + backend/src/app/DTO/Settings/ConfigDTO.php | 89 + backend/src/app/DTO/Settings/DatabaseDTO.php | 71 + backend/src/app/DTO/Settings/LogsDTO.php | 25 + backend/src/app/DTO/Settings/MetricsDTO.php | 35 + backend/src/app/DTO/Settings/OrdersDTO.php | 33 + backend/src/app/DTO/Settings/StoreDTO.php | 90 + backend/src/app/DTO/Settings/TelegramDTO.php | 62 + backend/src/app/DTO/Settings/TextsDTO.php | 53 + .../app/Exceptions/CustomExceptionHandler.php | 25 + .../OrderValidationFailedException.php | 28 + .../TelegramCustomerNotFoundException.php | 24 + ...legramCustomerWriteNotAllowedException.php | 24 + backend/src/app/Filters/ProductAttribute.php | 70 + backend/src/app/Filters/ProductCategories.php | 61 + backend/src/app/Filters/ProductCategory.php | 63 + .../src/app/Filters/ProductManufacturer.php | 48 + backend/src/app/Filters/ProductModel.php | 45 + backend/src/app/Filters/ProductPrice.php | 161 + backend/src/app/Filters/ProductQuantity.php | 68 + backend/src/app/Filters/ProductStatus.php | 35 + backend/src/app/Handlers/BlocksHandler.php | 26 + backend/src/app/Handlers/CartHandler.php | 54 + .../src/app/Handlers/CategoriesHandler.php | 127 + backend/src/app/Handlers/CronHandler.php | 58 + backend/src/app/Handlers/ETLHandler.php | 187 + backend/src/app/Handlers/FiltersHandler.php | 49 + backend/src/app/Handlers/FormsHandler.php | 51 + .../src/app/Handlers/HealthCheckHandler.php | 16 + backend/src/app/Handlers/OrderHandler.php | 37 + .../src/app/Handlers/PrivacyPolicyHandler.php | 73 + backend/src/app/Handlers/ProductsHandler.php | 125 + backend/src/app/Handlers/SettingsHandler.php | 115 + .../app/Handlers/TelegramCustomerHandler.php | 134 + backend/src/app/Handlers/TelegramHandler.php | 99 + backend/src/app/Handlers/TelemetryHandler.php | 48 + backend/src/app/Models/TelegramCustomer.php | 135 + .../ServiceProviders/AppServiceProvider.php | 69 + .../SettingsServiceProvider.php | 21 + backend/src/app/Services/BlocksService.php | 151 + backend/src/app/Services/CartService.php | 314 + .../app/Services/MegapayCustomerService.php | 129 + .../src/app/Services/OcCustomerService.php | 88 + .../src/app/Services/OrderCreateService.php | 316 + backend/src/app/Services/OrderMetaService.php | 27 + backend/src/app/Services/ProductsService.php | 494 ++ .../Services/SettingsSerializerService.php | 449 ++ backend/src/app/Services/SettingsService.php | 23 + backend/src/app/Support/Utils.php | 16 + backend/src/app/Telegram/LinkCommand.php | 149 + backend/src/app/routes.php | 44 + backend/src/bastion/ApplicationFactory.php | 40 + .../BotTokenConfiguratorException.php | 9 + .../Handlers/AcmeShopPulseStatsHandler.php | 33 + .../bastion/Handlers/AutocompleteHandler.php | 146 + .../bastion/Handlers/DictionariesHandler.php | 55 + backend/src/bastion/Handlers/FormsHandler.php | 57 + backend/src/bastion/Handlers/ImageHandler.php | 39 + backend/src/bastion/Handlers/LogsHandler.php | 205 + .../bastion/Handlers/SendMessageHandler.php | 123 + .../src/bastion/Handlers/SettingsHandler.php | 306 + backend/src/bastion/Handlers/StatsHandler.php | 60 + .../Handlers/TelegramCustomersHandler.php | 344 + .../src/bastion/Handlers/TelegramHandler.php | 140 + .../AcmeShopPulseSendEventsTask.php | 175 + .../bastion/Services/BotTokenConfigurator.php | 90 + .../Services/CronApiKeyRegenerator.php | 37 + .../src/bastion/Services/SettingsService.php | 37 + .../bastion/Tasks/CleanUpOldAssetsTask.php | 73 + backend/src/bastion/routes.php | 37 + backend/src/cli.php | 101 + backend/src/composer.json | 51 + backend/src/composer.lock | 6584 +++++++++++++++++ backend/src/configs/app.php | 122 + backend/src/configs/maintenance.php | 9 + backend/src/console/ApplicationFactory.php | 36 + .../src/console/Commands/AcmeShopCommand.php | 13 + .../console/Commands/CacheClearCommand.php | 43 + .../Commands/CustomerCountsCommand.php | 43 + .../Commands/ImagesCacheClearCommand.php | 156 + .../Commands/ImagesWarmupCacheCommand.php | 242 + .../Commands/PulseSendEventsCommand.php | 29 + .../console/Commands/ScheduleRunCommand.php | 80 + .../src/console/Commands/VersionCommand.php | 19 + backend/src/database/migrations/.gitkeep | 0 ...101000000_migrate_from_legacy_settings.php | 74 + .../20260101000002_remove_legacy_files.php | 19 + ...20260101000003_create_acme_forms_table.php | 22 + ...01000004_create_default_checkout_forms.php | 68 + ...0101000005_create_acme_customers_table.php | 36 + ...nsented_at_to_acmeshop_customers_table.php | 16 + ...101000007_create_acme_order_meta_table.php | 29 + ...ders_count_to_acmeshop_customers_table.php | 16 + ...009_add_tracking_id_to_customers_table.php | 16 + ...101000010_create_acmeshop_events_table.php | 33 + ...re_enabled_to_product_interaction_mode.php | 54 + ..._empty_usernames_in_acmeshop_customers.php | 16 + ...d_store_id_to_acmeshop_customers_table.php | 16 + ...1_create_acmeshop_scheduled_jobs_table.php | 28 + ...0208000002_add_acmeshop_scheduled_jobs.php | 21 + .../20260208000003_init_cron_api_key.php | 23 + .../framework/AcmeShopPulse/AcmeShopEvent.php | 234 + .../AcmeShopPulseEventsSender.php | 98 + .../AcmeShopPulse/AcmeShopPulseService.php | 208 + .../AcmeShopPulseServiceProvider.php | 45 + .../AcmeShopPulse/PayloadSignException.php | 9 + .../framework/AcmeShopPulse/PayloadSigner.php | 54 + .../framework/AcmeShopPulse/PulseEvents.php | 9 + .../AcmeShopPulse/PulseIngestException.php | 9 + .../AcmeShopPulse/StartParamSerializer.php | 76 + .../AcmeShopPulse/TrackingIdGenerator.php | 13 + backend/src/framework/Application.php | 206 + .../src/framework/Cache/CacheInterface.php | 14 + .../framework/Cache/CacheServiceProvider.php | 69 + .../src/framework/Cache/SymfonyMySqlCache.php | 44 + .../src/framework/Cache/SymfonyRedisCache.php | 44 + .../src/framework/Collection/Collection.php | 295 + backend/src/framework/Config/Settings.php | 35 + backend/src/framework/Container/Container.php | 182 + .../framework/Container/ServiceProvider.php | 16 + backend/src/framework/Contracts/Arrayable.php | 13 + .../Contracts/ExceptionHandlerInterface.php | 11 + .../CriteriaBuilder/CriteriaBuilder.php | 40 + .../framework/CriteriaBuilder/Criterion.php | 15 + .../Exceptions/CriteriaBuilderException.php | 9 + .../CriteriaBuilder/RuleSerializer.php | 58 + .../CriteriaBuilder/Rules/BaseRule.php | 106 + .../CriteriaBuilder/RulesRegistry.php | 63 + .../src/framework/DependencyRegistration.php | 36 + backend/src/framework/ErrorHandler.php | 147 + backend/src/framework/Events/Event.php | 7 + .../src/framework/Events/EventDispatcher.php | 32 + backend/src/framework/Events/Listener.php | 7 + .../Exceptions/ActionNotFoundException.php | 23 + .../ApplicationNotInstalledException.php | 9 + ...ContainerDependencyResolutionException.php | 9 + .../Exceptions/EntityNotFoundException.php | 14 + .../Exceptions/HttpNotFoundException.php | 9 + .../Exceptions/InvalidApiTokenException.php | 14 + .../NonLoggableExceptionInterface.php | 7 + backend/src/framework/Http/Request.php | 57 + .../src/framework/ImageTool/ImageFactory.php | 231 + .../ImageTool/ImageNotFoundException.php | 16 + .../ImageTool/ImageToolServiceProvider.php | 25 + .../src/framework/ImageTool/ImageUtils.php | 18 + .../MaintenanceTasks/BaseMaintenanceTask.php | 21 + .../MaintenanceTaskInterface.php | 12 + .../MaintenanceTasksService.php | 76 + .../MaintenanceTasksServiceProvider.php | 28 + .../src/framework/Migrations/Migration.php | 23 + .../Migrations/MigrationsServiceProvider.php | 22 + .../framework/Migrations/MigratorService.php | 128 + backend/src/framework/OpenCart/Currency.php | 25 + .../Decorators/OcRegistryDecorator.php | 57 + .../framework/OpenCart/PriceCalculator.php | 46 + .../src/framework/OpenCart/PriceFormatter.php | 17 + .../src/framework/QueryBuilder/Builder.php | 471 ++ .../Connections/ConnectionInterface.php | 40 + .../Connections/MySqlConnection.php | 200 + .../QueryBuilder/Grammars/Grammar.php | 225 + .../QueryBuilder/Grammars/MySqlGrammar.php | 7 + .../src/framework/QueryBuilder/JoinClause.php | 33 + .../QueryBuilder/QueryBuilderException.php | 9 + .../QueryBuilderServiceProvider.php | 37 + .../framework/QueryBuilder/QueryResult.php | 44 + .../framework/QueryBuilder/RawExpression.php | 18 + backend/src/framework/QueryBuilder/Table.php | 51 + .../framework/Router/RouteServiceProvider.php | 19 + backend/src/framework/Router/Router.php | 49 + backend/src/framework/Scheduler/Job.php | 114 + .../Scheduler/Models/ScheduledJob.php | 93 + .../Scheduler/ScheduleJobRegistry.php | 52 + .../framework/Scheduler/SchedulerResult.php | 43 + .../framework/Scheduler/SchedulerService.php | 189 + .../Scheduler/SchedulerServiceProvider.php | 13 + .../src/framework/Scheduler/TaskInterface.php | 8 + .../src/framework/Sentry/SentryService.php | 137 + .../Settings/DatabaseUserSettings.php | 90 + .../Settings/UserSettingsInterface.php | 11 + backend/src/framework/Support/Arr.php | 225 + backend/src/framework/Support/DateUtils.php | 34 + .../Support/ExecutionTimeProfiler.php | 49 + .../framework/Support/PaginationHelper.php | 11 + backend/src/framework/Support/Str.php | 36 + .../Support/SupportServiceProvider.php | 15 + backend/src/framework/Support/Utils.php | 101 + backend/src/framework/Support/WorkLogsBag.php | 43 + backend/src/framework/Support/helpers.php | 131 + .../Telegram/Commands/ChatIdCommand.php | 15 + .../Telegram/Commands/StartCommand.php | 38 + .../Telegram/Commands/TelegramCommand.php | 19 + .../Contracts/TelegramCommandInterface.php | 8 + .../framework/Telegram/Enums/ChatAction.php | 18 + .../Telegram/Enums/TelegramHeader.php | 8 + .../DecodeTelegramInitDataException.php | 9 + .../Exceptions/TelegramClientException.php | 14 + .../TelegramCommandNotFoundException.php | 16 + .../TelegramInvalidSignatureException.php | 9 + .../framework/Telegram/RequestValidator.php | 7 + .../framework/Telegram/SignatureValidator.php | 86 + .../Telegram/TelegramBotStateManager.php | 54 + .../Telegram/TelegramCommandsRegistry.php | 27 + .../Telegram/TelegramInitDataDecoder.php | 60 + .../framework/Telegram/TelegramService.php | 218 + .../Telegram/TelegramServiceProvider.php | 33 + .../TelegramValidateInitDataMiddleware.php | 36 + .../src/framework/Translator/Translator.php | 64 + .../Translator/TranslatorInterface.php | 12 + .../Translator/TranslatorServiceProvider.php | 19 + backend/src/framework/Validator/ErrorBag.php | 45 + .../ValidationRuleNotFoundException.php | 9 + .../Validator/ValidationRules/Email.php | 17 + .../Validator/ValidationRules/Required.php | 34 + .../ValidationRuleInterface.php | 10 + backend/src/framework/Validator/Validator.php | 91 + .../Validator/ValidatorInterface.php | 15 + .../Validator/ValidatorServiceProvider.php | 32 + .../framework/Validator/translations/ru.php | 6 + backend/src/phpstan.neon | 15 + backend/src/phpunit.xml | 39 + backend/src/stubs/Cart.php | 7 + backend/src/stubs/Proxy.php | 5 + backend/src/stubs/Registry.php | 5 + backend/src/stubs/Tax.php | 7 + backend/src/stubs/config.php | 28 + backend/src/stubs/currency.php | 42 + backend/src/stubs/phpstan-bootstrap.php | 11 + backend/src/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 + backend/src/tests/Helpers/OpencartUrl.php | 8 + .../Integration/Services/CartServiceTest.php | 45 + .../Services/OrderCreateServiceTest.php | 99 + .../Services/ProductsServiceTest.php | 73 + backend/src/tests/README.md | 125 + .../tests/Telegram/TelegramServiceTest.php | 73 + backend/src/tests/TestCase.php | 169 + backend/src/tests/Unit/Framework/ArrTest.php | 696 ++ .../src/tests/Unit/Framework/BuilderTest.php | 603 ++ .../tests/Unit/Framework/ContainerTest.php | 145 + .../Unit/Framework/CriteriaBuilderTest.php | 184 + .../Framework/ExecutionTimeProfilerTest.php | 86 + .../Unit/Framework/GenericCollectionTest.php | 176 + .../src/tests/Unit/Framework/HelpersTest.php | 55 + .../Framework/ImageTool/ImageFactoryTest.php | 133 + .../tests/Unit/Framework/JsonResponseTest.php | 51 + .../tests/Unit/Framework/MySqlGrammarTest.php | 281 + .../Unit/Framework/PaginationHelperTest.php | 46 + .../tests/Unit/Framework/QueryResultTest.php | 64 + .../Unit/Framework/RawExpressionTest.php | 41 + .../src/tests/Unit/Framework/RequestTest.php | 113 + .../Unit/Framework/RuleSerializerTest.php | 46 + .../Unit/Framework/Scheduler/JobTest.php | 210 + .../Scheduler/ScheduleJobRegistryTest.php | 104 + .../Scheduler/SchedulerResultTest.php | 100 + .../Scheduler/SchedulerServiceTest.php | 396 + .../src/tests/Unit/Framework/SettingsTest.php | 64 + backend/src/tests/Unit/Framework/StrTest.php | 55 + .../src/tests/Unit/Framework/TableTest.php | 55 + .../tests/Unit/Framework/TranslatorTest.php | 31 + .../src/tests/Unit/Framework/UtilsTest.php | 114 + .../Unit/Framework/Validator/ErrorBagTest.php | 24 + .../Framework/Validator/ValidatorTest.php | 145 + .../Unit/Services/OrderCreateServiceTest.php | 222 + .../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_category/contains/input.json | 19 + .../product_category/contains/output.sql | 17 + .../product_category/not_contains/input.json | 19 + .../product_category/not_contains/output.sql | 17 + .../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 | 54 + .../product_price/between_equals/input.json | 19 + .../product_price/between_equals/output.sql | 53 + .../between_from_empty_string/input.json | 19 + .../between_from_empty_string/output.sql | 53 + .../between_from_null/input.json | 19 + .../between_from_null/output.sql | 53 + .../between_num_as_string/input.json | 19 + .../between_num_as_string/output.sql | 54 + .../product_price/equals/input.json | 19 + .../product_price/equals/output.sql | 53 + .../equals_num_as_string/input.json | 19 + .../equals_num_as_string/output.sql | 53 + .../product_price/greater/input.json | 19 + .../product_price/greater/output.sql | 53 + .../greater_or_equals/input.json | 19 + .../greater_or_equals/output.sql | 53 + .../include_discounts/input.json | 25 + .../include_discounts/output.sql | 53 + .../product_price/include_specials/input.json | 25 + .../product_price/include_specials/output.sql | 53 + .../product_price/less/input.json | 19 + .../product_price/less/output.sql | 53 + .../product_price/less_or_equals/input.json | 19 + .../product_price/less_or_equals/output.sql | 53 + .../product_price/not_equals/input.json | 19 + .../product_price/not_equals/output.sql | 53 + .../not_include_specials/input.json | 25 + .../not_include_specials/output.sql | 15 + .../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 + deployment/apache2/cors.conf | 6 + deployment/build.dockerfile | 15 + deployment/config.php | 100 + .../create_database_for_tests.sh | 4 + docker-compose.yaml | 81 + frontend/admin/.editorconfig | 8 + frontend/admin/.gitattributes | 1 + frontend/admin/.gitignore | 36 + frontend/admin/.prettierrc.json | 6 + frontend/admin/README.md | 44 + frontend/admin/eslint.config.js | 28 + frontend/admin/index.html | 13 + frontend/admin/jsconfig.json | 8 + frontend/admin/package-lock.json | 6085 +++++++++++++++ frontend/admin/package.json | 57 + frontend/admin/src/App.vue | 187 + frontend/admin/src/assets/base.css | 86 + frontend/admin/src/assets/logo.svg | 1 + frontend/admin/src/assets/main.css | 76 + .../src/components/CronExpressionSelect.vue | 48 + .../src/components/CronJobOrgUrlField.vue | 104 + .../src/components/Form/CategoryLabel.vue | 52 + .../src/components/Form/CategorySelect.vue | 50 + .../src/components/Form/ResetCacheBtn.vue | 40 + .../src/components/FormBuilder/CodeEditor.vue | 70 + .../components/FormBuilder/FieldSettings.vue | 376 + .../components/FormBuilder/FieldsPanel.vue | 54 + .../components/FormBuilder/FormBuilder.vue | 476 ++ .../src/components/FormBuilder/FormCanvas.vue | 144 + .../components/FormBuilder/FormRenderer.vue | 57 + .../src/components/FormBuilder/IconPicker.vue | 124 + .../FormBuilder/composables/useFormFields.js | 235 + .../FormBuilder/constants/availableFields.js | 301 + .../FormBuilder/utils/fieldHelpers.js | 53 + .../FormBuilder/utils/fieldTypes.js | 30 + .../FormBuilder/utils/revisionManager.js | 119 + .../FormBuilder/utils/schemaParser.js | 23 + frontend/admin/src/components/LogsViewer.vue | 191 + .../MainPageConfigurator/Blocks/BaseBlock.vue | 63 + .../Blocks/CategoriesTopBlock.vue | 29 + .../Blocks/ProductsCarouselBlock.vue | 31 + .../Blocks/ProductsFeedBlock.vue | 30 + .../Blocks/SliderBlock.vue | 46 + .../Forms/AspectRatioSelect.vue | 31 + .../MainPageConfigurator/Forms/BaseForm.vue | 130 + .../Forms/CategoriesTopForm.vue | 55 + .../MainPageConfigurator/Forms/FormItem.vue | 25 + .../Forms/ProductsCarouselForm.vue | 254 + .../Forms/ProductsFeedForm.vue | 69 + .../MainPageConfigurator/Forms/SliderForm.vue | 271 + .../MainPageConfigurator.vue | 197 + .../MainPageConfigurator/availableBlocks.js | 82 + .../admin/src/components/OcImagePicker.vue | 54 + .../admin/src/components/RichTextEditor.vue | 133 + .../src/components/ScheduledJobsList.vue | 108 + .../src/components/Settings/ItemBool.vue | 27 + .../Settings/ItemCategoriesSelect.vue | 116 + .../src/components/Settings/ItemImage.vue | 25 + .../src/components/Settings/ItemInput.vue | 72 + .../Settings/ItemProductsSelect.vue | 116 + .../src/components/Settings/ItemSelect.vue | 48 + .../src/components/Settings/ItemTextarea.vue | 41 + .../components/Settings/ItemTgBotToken.vue | 145 + .../src/components/Settings/ItemTgChatID.vue | 190 + .../Settings/ItemTgMessageTemplate.vue | 165 + .../components/Settings/ItemTgMiniAppLink.vue | 31 + .../components/Settings/ItemToggleButton.vue | 51 + .../admin/src/components/SettingsItem.vue | 67 + .../src/components/Slider/CategorySelect.vue | 66 + .../src/components/Slider/LinkSelector.vue | 59 + .../src/components/Slider/ProductSelect.vue | 65 + frontend/admin/src/components/Switcher.vue | 15 + frontend/admin/src/components/TopLead.vue | 229 + frontend/admin/src/formkit.config.js | 24 + frontend/admin/src/formkit.theme.mjs | 3398 +++++++++ frontend/admin/src/main.js | 90 + frontend/admin/src/router/index.js | 31 + frontend/admin/src/stores/autocomplete.js | 19 + frontend/admin/src/stores/counter.js | 12 + frontend/admin/src/stores/forms.js | 14 + frontend/admin/src/stores/logs.js | 21 + frontend/admin/src/stores/settings.js | 175 + frontend/admin/src/stores/stats.js | 22 + frontend/admin/src/utils/constants..js | 7 + frontend/admin/src/utils/helpers.js | 17 + frontend/admin/src/utils/http.js | 142 + frontend/admin/src/utils/toastHelper.js | 2 + .../admin/src/views/AcmeShopPulseView.vue | 180 + frontend/admin/src/views/CronView.vue | 147 + frontend/admin/src/views/CustomersView.vue | 706 ++ frontend/admin/src/views/FormBuilderView.vue | 23 + frontend/admin/src/views/GeneralView.vue | 97 + frontend/admin/src/views/LogsView.vue | 8 + frontend/admin/src/views/MainPageView.vue | 7 + frontend/admin/src/views/MetricsView.vue | 53 + frontend/admin/src/views/OrdersView.vue | 17 + frontend/admin/src/views/StoreView.vue | 65 + frontend/admin/src/views/TelegramView.vue | 28 + frontend/admin/src/views/TextsView.vue | 46 + frontend/admin/tailwind.config.js | 9 + frontend/admin/vite.config.js | 48 + frontend/spa/README.md | 5 + frontend/spa/index.html | 15 + frontend/spa/package-lock.json | 5498 ++++++++++++++ frontend/spa/package.json | 45 + frontend/spa/src/App.vue | 158 + frontend/spa/src/AppLoading.vue | 7 + frontend/spa/src/ApplicationError.vue | 20 + frontend/spa/src/BrowserNotSupported.vue | 21 + frontend/spa/src/ShoppingCart.js | 63 + frontend/spa/src/WrongPlatformError.vue | 19 + .../spa/src/components/AppDebugMessage.vue | 9 + frontend/spa/src/components/BottomDrawer.vue | 56 + frontend/spa/src/components/BottomPanel.vue | 5 + frontend/spa/src/components/CartButton.vue | 47 + .../CategoriesList/CategoryItem.vue | 27 + frontend/spa/src/components/Dock.vue | 150 + .../src/components/FullScreenImageViewer.vue | 81 + .../spa/src/components/Icons/IconFunnel.vue | 7 + .../spa/src/components/Icons/IconWarning.vue | 7 + frontend/spa/src/components/Loader.vue | 18 + .../spa/src/components/LoadingFullScreen.vue | 17 + .../components/MainPage/Blocks/BaseBlock.vue | 35 + .../MainPage/Blocks/CategoriesTopBlock.vue | 48 + .../components/MainPage/Blocks/ErrorBlock.vue | 8 + .../MainPage/Blocks/ProductsCarouselBlock.vue | 73 + .../MainPage/Blocks/ProductsFeedBlock.vue | 111 + .../MainPage/Blocks/SliderBlock.vue | 26 + .../src/components/MainPage/EmptyBlocks.vue | 34 + .../spa/src/components/MainPage/MainPage.vue | 46 + frontend/spa/src/components/Navbar.vue | 37 + frontend/spa/src/components/NoProducts.vue | 15 + frontend/spa/src/components/Price.vue | 13 + frontend/spa/src/components/PrivacyPolicy.vue | 53 + .../ProductFilters/Components/ForMainPage.vue | 20 + .../ProductCategory/ProductCategory.vue | 42 + .../ProductCategory/SelectOption.vue | 31 + .../Components/ProductPrice.vue | 45 + .../spa/src/components/ProductImageSwiper.vue | 58 + .../spa/src/components/ProductItem/Price.vue | 24 + .../src/components/ProductItem/PriceTitle.vue | 15 + .../components/ProductItem/ProductTitle.vue | 21 + .../spa/src/components/ProductNotFound.vue | 19 + .../ProductOptions/Cart/OptionCheckbox.vue | 18 + .../ProductOptions/Cart/OptionRadio.vue | 18 + .../ProductOptions/Cart/OptionText.vue | 18 + .../ProductOptions/ProductOptions.vue | 31 + .../ProductOptions/Types/OptionCheckbox.vue | 44 + .../ProductOptions/Types/OptionRadio.vue | 42 + .../ProductOptions/Types/OptionSelect.vue | 38 + .../ProductOptions/Types/OptionTemplate.vue | 25 + .../ProductOptions/Types/OptionText.vue | 23 + .../ProductOptions/Types/OptionTextarea.vue | 23 + frontend/spa/src/components/ProductsList.vue | 159 + frontend/spa/src/components/Quantity.vue | 58 + frontend/spa/src/components/SearchInput.vue | 41 + .../components/SingleProductImageSwiper.vue | 113 + frontend/spa/src/components/Slider.vue | 209 + frontend/spa/src/components/SwipeToBack.vue | 10 + .../spa/src/composables/useHapticFeedback.js | 39 + .../spa/src/composables/useHapticScroll.js | 34 + frontend/spa/src/composables/useSwipeBack.js | 209 + frontend/spa/src/composables/useTgData.js | 3 + frontend/spa/src/constants/options.js | 7 + frontend/spa/src/constants/tPulseEvents.js | 4 + frontend/spa/src/constants/yaMetrikaGoals.js | 16 + frontend/spa/src/errors.js | 30 + frontend/spa/src/formkit.config.js | 16 + frontend/spa/src/formkit.theme.mjs | 3398 +++++++++ frontend/spa/src/helpers.js | 153 + frontend/spa/src/main.js | 142 + frontend/spa/src/router.js | 69 + frontend/spa/src/stores/BlocksStore.js | 32 + frontend/spa/src/stores/CartStore.js | 169 + frontend/spa/src/stores/CategoriesStore.js | 47 + frontend/spa/src/stores/CheckoutStore.js | 157 + frontend/spa/src/stores/FormsStore.js | 16 + frontend/spa/src/stores/KeyboardStore.js | 15 + .../spa/src/stores/ProductFiltersStore.js | 41 + frontend/spa/src/stores/ProductsStore.js | 135 + frontend/spa/src/stores/Pulse.js | 60 + frontend/spa/src/stores/SearchStore.js | 132 + frontend/spa/src/stores/SettingsStore.js | 60 + frontend/spa/src/stores/yaMetrikaStore.js | 149 + frontend/spa/src/style.css | 88 + frontend/spa/src/translations.js | 3 + frontend/spa/src/utils/AppMetaInitializer.ts | 48 + frontend/spa/src/utils/ftch.js | 146 + frontend/spa/src/utils/yaMetrika.js | 30 + frontend/spa/src/views/Account.vue | 238 + frontend/spa/src/views/BaseViewWrapper.vue | 14 + frontend/spa/src/views/Cart.vue | 199 + frontend/spa/src/views/CategoriesList.vue | 140 + frontend/spa/src/views/Checkout.vue | 135 + frontend/spa/src/views/Filters.vue | 110 + frontend/spa/src/views/Home.vue | 59 + frontend/spa/src/views/OrderCreated.vue | 57 + frontend/spa/src/views/Product.vue | 414 ++ frontend/spa/src/views/Products.vue | 160 + frontend/spa/src/views/Search.vue | 226 + frontend/spa/tailwind.config.js | 17 + frontend/spa/tests/setup.js | 43 + frontend/spa/tests/unit/ShoppingCart.test.js | 118 + .../spa/tests/unit/components/Price.test.js | 45 + frontend/spa/tests/unit/helpers.test.js | 93 + .../spa/tests/unit/stores/CartStore.test.js | 266 + .../unit/utils/AppMetaInitializer.test.ts | 69 + frontend/spa/tests/unit/utils/ftch.test.js | 79 + frontend/spa/vite.config.js | 51 + frontend/spa/vitest.config.js | 32 + scripts/build_phar.sh | 34 + scripts/ci/build.sh | 157 + scripts/ci/create-phar.php | 37 + scripts/ci/extract-phar.php | 17 + scripts/download_oc_store.sh | 35 + scripts/install_ocstore.sh | 42 + scripts/link.php | 40 + scripts/wait_for_containers.sh | 28 + scripts/wait_for_mysql.sh | 24 + 588 files changed, 65779 insertions(+) create mode 100644 .cursor/agents.md create mode 100644 .cursor/config.json create mode 100644 .cursor/features/acme-pulse-heartbeat.md create mode 100644 .cursor/prompts/api-generation.md create mode 100644 .cursor/prompts/changelog.md create mode 100644 .cursor/prompts/documentation.md create mode 100644 .cursor/prompts/refactoring.md create mode 100644 .cursor/prompts/testing.md create mode 100644 .cursor/rules/architecture.md create mode 100644 .cursor/rules/form-builder.md create mode 100644 .cursor/rules/javascript.md create mode 100644 .cursor/rules/php.md create mode 100644 .cursor/rules/vue.md create mode 100644 .cursorignore create mode 100644 .github/workflows/main.yaml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Makefile create mode 100644 README.md create mode 100755 backend/src/.env.example create mode 100755 backend/src/app/Adapters/OcCartAdapter.php create mode 100755 backend/src/app/Adapters/OcModelCatalogProductAdapter.php create mode 100755 backend/src/app/ApplicationFactory.php create mode 100755 backend/src/app/DTO/Settings/AppDTO.php create mode 100755 backend/src/app/DTO/Settings/ConfigDTO.php create mode 100755 backend/src/app/DTO/Settings/DatabaseDTO.php create mode 100755 backend/src/app/DTO/Settings/LogsDTO.php create mode 100755 backend/src/app/DTO/Settings/MetricsDTO.php create mode 100755 backend/src/app/DTO/Settings/OrdersDTO.php create mode 100755 backend/src/app/DTO/Settings/StoreDTO.php create mode 100755 backend/src/app/DTO/Settings/TelegramDTO.php create mode 100755 backend/src/app/DTO/Settings/TextsDTO.php create mode 100755 backend/src/app/Exceptions/CustomExceptionHandler.php create mode 100755 backend/src/app/Exceptions/OrderValidationFailedException.php create mode 100755 backend/src/app/Exceptions/TelegramCustomerNotFoundException.php create mode 100755 backend/src/app/Exceptions/TelegramCustomerWriteNotAllowedException.php create mode 100755 backend/src/app/Filters/ProductAttribute.php create mode 100755 backend/src/app/Filters/ProductCategories.php create mode 100755 backend/src/app/Filters/ProductCategory.php create mode 100755 backend/src/app/Filters/ProductManufacturer.php create mode 100755 backend/src/app/Filters/ProductModel.php create mode 100755 backend/src/app/Filters/ProductPrice.php create mode 100755 backend/src/app/Filters/ProductQuantity.php create mode 100755 backend/src/app/Filters/ProductStatus.php create mode 100755 backend/src/app/Handlers/BlocksHandler.php create mode 100755 backend/src/app/Handlers/CartHandler.php create mode 100755 backend/src/app/Handlers/CategoriesHandler.php create mode 100644 backend/src/app/Handlers/CronHandler.php create mode 100755 backend/src/app/Handlers/ETLHandler.php create mode 100755 backend/src/app/Handlers/FiltersHandler.php create mode 100755 backend/src/app/Handlers/FormsHandler.php create mode 100755 backend/src/app/Handlers/HealthCheckHandler.php create mode 100755 backend/src/app/Handlers/OrderHandler.php create mode 100755 backend/src/app/Handlers/PrivacyPolicyHandler.php create mode 100755 backend/src/app/Handlers/ProductsHandler.php create mode 100755 backend/src/app/Handlers/SettingsHandler.php create mode 100755 backend/src/app/Handlers/TelegramCustomerHandler.php create mode 100755 backend/src/app/Handlers/TelegramHandler.php create mode 100755 backend/src/app/Handlers/TelemetryHandler.php create mode 100755 backend/src/app/Models/TelegramCustomer.php create mode 100755 backend/src/app/ServiceProviders/AppServiceProvider.php create mode 100755 backend/src/app/ServiceProviders/SettingsServiceProvider.php create mode 100755 backend/src/app/Services/BlocksService.php create mode 100755 backend/src/app/Services/CartService.php create mode 100755 backend/src/app/Services/MegapayCustomerService.php create mode 100755 backend/src/app/Services/OcCustomerService.php create mode 100755 backend/src/app/Services/OrderCreateService.php create mode 100755 backend/src/app/Services/OrderMetaService.php create mode 100755 backend/src/app/Services/ProductsService.php create mode 100755 backend/src/app/Services/SettingsSerializerService.php create mode 100755 backend/src/app/Services/SettingsService.php create mode 100755 backend/src/app/Support/Utils.php create mode 100755 backend/src/app/Telegram/LinkCommand.php create mode 100755 backend/src/app/routes.php create mode 100755 backend/src/bastion/ApplicationFactory.php create mode 100755 backend/src/bastion/Exceptions/BotTokenConfiguratorException.php create mode 100755 backend/src/bastion/Handlers/AcmeShopPulseStatsHandler.php create mode 100755 backend/src/bastion/Handlers/AutocompleteHandler.php create mode 100755 backend/src/bastion/Handlers/DictionariesHandler.php create mode 100755 backend/src/bastion/Handlers/FormsHandler.php create mode 100755 backend/src/bastion/Handlers/ImageHandler.php create mode 100755 backend/src/bastion/Handlers/LogsHandler.php create mode 100755 backend/src/bastion/Handlers/SendMessageHandler.php create mode 100755 backend/src/bastion/Handlers/SettingsHandler.php create mode 100755 backend/src/bastion/Handlers/StatsHandler.php create mode 100755 backend/src/bastion/Handlers/TelegramCustomersHandler.php create mode 100755 backend/src/bastion/Handlers/TelegramHandler.php create mode 100755 backend/src/bastion/ScheduledTasks/AcmeShopPulseSendEventsTask.php create mode 100755 backend/src/bastion/Services/BotTokenConfigurator.php create mode 100644 backend/src/bastion/Services/CronApiKeyRegenerator.php create mode 100755 backend/src/bastion/Services/SettingsService.php create mode 100755 backend/src/bastion/Tasks/CleanUpOldAssetsTask.php create mode 100755 backend/src/bastion/routes.php create mode 100755 backend/src/cli.php create mode 100755 backend/src/composer.json create mode 100755 backend/src/composer.lock create mode 100755 backend/src/configs/app.php create mode 100755 backend/src/configs/maintenance.php create mode 100755 backend/src/console/ApplicationFactory.php create mode 100755 backend/src/console/Commands/AcmeShopCommand.php create mode 100644 backend/src/console/Commands/CacheClearCommand.php create mode 100755 backend/src/console/Commands/CustomerCountsCommand.php create mode 100755 backend/src/console/Commands/ImagesCacheClearCommand.php create mode 100755 backend/src/console/Commands/ImagesWarmupCacheCommand.php create mode 100755 backend/src/console/Commands/PulseSendEventsCommand.php create mode 100755 backend/src/console/Commands/ScheduleRunCommand.php create mode 100755 backend/src/console/Commands/VersionCommand.php create mode 100755 backend/src/database/migrations/.gitkeep create mode 100755 backend/src/database/migrations/20260101000000_migrate_from_legacy_settings.php create mode 100755 backend/src/database/migrations/20260101000002_remove_legacy_files.php create mode 100755 backend/src/database/migrations/20260101000003_create_acme_forms_table.php create mode 100755 backend/src/database/migrations/20260101000004_create_default_checkout_forms.php create mode 100755 backend/src/database/migrations/20260101000005_create_acme_customers_table.php create mode 100755 backend/src/database/migrations/20260101000006_add_privacy_consented_at_to_acmeshop_customers_table.php create mode 100755 backend/src/database/migrations/20260101000007_create_acme_order_meta_table.php create mode 100755 backend/src/database/migrations/20260101000008_add_orders_count_to_acmeshop_customers_table.php create mode 100755 backend/src/database/migrations/20260101000009_add_tracking_id_to_customers_table.php create mode 100755 backend/src/database/migrations/20260101000010_create_acmeshop_events_table.php create mode 100644 backend/src/database/migrations/20260101000011_migrate_store_enabled_to_product_interaction_mode.php create mode 100644 backend/src/database/migrations/20260103000000_fill_empty_usernames_in_acmeshop_customers.php create mode 100755 backend/src/database/migrations/20260105000000_add_store_id_to_acmeshop_customers_table.php create mode 100755 backend/src/database/migrations/20260208000001_create_acmeshop_scheduled_jobs_table.php create mode 100755 backend/src/database/migrations/20260208000002_add_acmeshop_scheduled_jobs.php create mode 100644 backend/src/database/migrations/20260208000003_init_cron_api_key.php create mode 100755 backend/src/framework/AcmeShopPulse/AcmeShopEvent.php create mode 100755 backend/src/framework/AcmeShopPulse/AcmeShopPulseEventsSender.php create mode 100755 backend/src/framework/AcmeShopPulse/AcmeShopPulseService.php create mode 100755 backend/src/framework/AcmeShopPulse/AcmeShopPulseServiceProvider.php create mode 100755 backend/src/framework/AcmeShopPulse/PayloadSignException.php create mode 100755 backend/src/framework/AcmeShopPulse/PayloadSigner.php create mode 100755 backend/src/framework/AcmeShopPulse/PulseEvents.php create mode 100755 backend/src/framework/AcmeShopPulse/PulseIngestException.php create mode 100755 backend/src/framework/AcmeShopPulse/StartParamSerializer.php create mode 100755 backend/src/framework/AcmeShopPulse/TrackingIdGenerator.php create mode 100755 backend/src/framework/Application.php create mode 100755 backend/src/framework/Cache/CacheInterface.php create mode 100755 backend/src/framework/Cache/CacheServiceProvider.php create mode 100755 backend/src/framework/Cache/SymfonyMySqlCache.php create mode 100755 backend/src/framework/Cache/SymfonyRedisCache.php create mode 100755 backend/src/framework/Collection/Collection.php create mode 100755 backend/src/framework/Config/Settings.php create mode 100755 backend/src/framework/Container/Container.php create mode 100755 backend/src/framework/Container/ServiceProvider.php create mode 100755 backend/src/framework/Contracts/Arrayable.php create mode 100755 backend/src/framework/Contracts/ExceptionHandlerInterface.php create mode 100755 backend/src/framework/CriteriaBuilder/CriteriaBuilder.php create mode 100755 backend/src/framework/CriteriaBuilder/Criterion.php create mode 100755 backend/src/framework/CriteriaBuilder/Exceptions/CriteriaBuilderException.php create mode 100755 backend/src/framework/CriteriaBuilder/RuleSerializer.php create mode 100755 backend/src/framework/CriteriaBuilder/Rules/BaseRule.php create mode 100755 backend/src/framework/CriteriaBuilder/RulesRegistry.php create mode 100755 backend/src/framework/DependencyRegistration.php create mode 100755 backend/src/framework/ErrorHandler.php create mode 100755 backend/src/framework/Events/Event.php create mode 100755 backend/src/framework/Events/EventDispatcher.php create mode 100755 backend/src/framework/Events/Listener.php create mode 100755 backend/src/framework/Exceptions/ActionNotFoundException.php create mode 100755 backend/src/framework/Exceptions/ApplicationNotInstalledException.php create mode 100755 backend/src/framework/Exceptions/ContainerDependencyResolutionException.php create mode 100755 backend/src/framework/Exceptions/EntityNotFoundException.php create mode 100755 backend/src/framework/Exceptions/HttpNotFoundException.php create mode 100755 backend/src/framework/Exceptions/InvalidApiTokenException.php create mode 100755 backend/src/framework/Exceptions/NonLoggableExceptionInterface.php create mode 100755 backend/src/framework/Http/Request.php create mode 100755 backend/src/framework/ImageTool/ImageFactory.php create mode 100755 backend/src/framework/ImageTool/ImageNotFoundException.php create mode 100755 backend/src/framework/ImageTool/ImageToolServiceProvider.php create mode 100755 backend/src/framework/ImageTool/ImageUtils.php create mode 100755 backend/src/framework/MaintenanceTasks/BaseMaintenanceTask.php create mode 100755 backend/src/framework/MaintenanceTasks/MaintenanceTaskInterface.php create mode 100755 backend/src/framework/MaintenanceTasks/MaintenanceTasksService.php create mode 100755 backend/src/framework/MaintenanceTasks/MaintenanceTasksServiceProvider.php create mode 100755 backend/src/framework/Migrations/Migration.php create mode 100755 backend/src/framework/Migrations/MigrationsServiceProvider.php create mode 100755 backend/src/framework/Migrations/MigratorService.php create mode 100755 backend/src/framework/OpenCart/Currency.php create mode 100755 backend/src/framework/OpenCart/Decorators/OcRegistryDecorator.php create mode 100755 backend/src/framework/OpenCart/PriceCalculator.php create mode 100755 backend/src/framework/OpenCart/PriceFormatter.php create mode 100755 backend/src/framework/QueryBuilder/Builder.php create mode 100755 backend/src/framework/QueryBuilder/Connections/ConnectionInterface.php create mode 100755 backend/src/framework/QueryBuilder/Connections/MySqlConnection.php create mode 100755 backend/src/framework/QueryBuilder/Grammars/Grammar.php create mode 100755 backend/src/framework/QueryBuilder/Grammars/MySqlGrammar.php create mode 100755 backend/src/framework/QueryBuilder/JoinClause.php create mode 100755 backend/src/framework/QueryBuilder/QueryBuilderException.php create mode 100755 backend/src/framework/QueryBuilder/QueryBuilderServiceProvider.php create mode 100755 backend/src/framework/QueryBuilder/QueryResult.php create mode 100755 backend/src/framework/QueryBuilder/RawExpression.php create mode 100755 backend/src/framework/QueryBuilder/Table.php create mode 100755 backend/src/framework/Router/RouteServiceProvider.php create mode 100755 backend/src/framework/Router/Router.php create mode 100755 backend/src/framework/Scheduler/Job.php create mode 100644 backend/src/framework/Scheduler/Models/ScheduledJob.php create mode 100755 backend/src/framework/Scheduler/ScheduleJobRegistry.php create mode 100755 backend/src/framework/Scheduler/SchedulerResult.php create mode 100755 backend/src/framework/Scheduler/SchedulerService.php create mode 100755 backend/src/framework/Scheduler/SchedulerServiceProvider.php create mode 100755 backend/src/framework/Scheduler/TaskInterface.php create mode 100644 backend/src/framework/Sentry/SentryService.php create mode 100755 backend/src/framework/Settings/DatabaseUserSettings.php create mode 100755 backend/src/framework/Settings/UserSettingsInterface.php create mode 100755 backend/src/framework/Support/Arr.php create mode 100755 backend/src/framework/Support/DateUtils.php create mode 100755 backend/src/framework/Support/ExecutionTimeProfiler.php create mode 100755 backend/src/framework/Support/PaginationHelper.php create mode 100755 backend/src/framework/Support/Str.php create mode 100755 backend/src/framework/Support/SupportServiceProvider.php create mode 100755 backend/src/framework/Support/Utils.php create mode 100755 backend/src/framework/Support/WorkLogsBag.php create mode 100755 backend/src/framework/Support/helpers.php create mode 100755 backend/src/framework/Telegram/Commands/ChatIdCommand.php create mode 100755 backend/src/framework/Telegram/Commands/StartCommand.php create mode 100755 backend/src/framework/Telegram/Commands/TelegramCommand.php create mode 100755 backend/src/framework/Telegram/Contracts/TelegramCommandInterface.php create mode 100755 backend/src/framework/Telegram/Enums/ChatAction.php create mode 100755 backend/src/framework/Telegram/Enums/TelegramHeader.php create mode 100755 backend/src/framework/Telegram/Exceptions/DecodeTelegramInitDataException.php create mode 100755 backend/src/framework/Telegram/Exceptions/TelegramClientException.php create mode 100755 backend/src/framework/Telegram/Exceptions/TelegramCommandNotFoundException.php create mode 100755 backend/src/framework/Telegram/Exceptions/TelegramInvalidSignatureException.php create mode 100755 backend/src/framework/Telegram/RequestValidator.php create mode 100755 backend/src/framework/Telegram/SignatureValidator.php create mode 100755 backend/src/framework/Telegram/TelegramBotStateManager.php create mode 100755 backend/src/framework/Telegram/TelegramCommandsRegistry.php create mode 100755 backend/src/framework/Telegram/TelegramInitDataDecoder.php create mode 100755 backend/src/framework/Telegram/TelegramService.php create mode 100755 backend/src/framework/Telegram/TelegramServiceProvider.php create mode 100755 backend/src/framework/Telegram/TelegramValidateInitDataMiddleware.php create mode 100755 backend/src/framework/Translator/Translator.php create mode 100755 backend/src/framework/Translator/TranslatorInterface.php create mode 100755 backend/src/framework/Translator/TranslatorServiceProvider.php create mode 100755 backend/src/framework/Validator/ErrorBag.php create mode 100755 backend/src/framework/Validator/ValidationRuleNotFoundException.php create mode 100755 backend/src/framework/Validator/ValidationRules/Email.php create mode 100755 backend/src/framework/Validator/ValidationRules/Required.php create mode 100755 backend/src/framework/Validator/ValidationRules/ValidationRuleInterface.php create mode 100755 backend/src/framework/Validator/Validator.php create mode 100755 backend/src/framework/Validator/ValidatorInterface.php create mode 100755 backend/src/framework/Validator/ValidatorServiceProvider.php create mode 100755 backend/src/framework/Validator/translations/ru.php create mode 100755 backend/src/phpstan.neon create mode 100755 backend/src/phpunit.xml create mode 100755 backend/src/stubs/Cart.php create mode 100755 backend/src/stubs/Proxy.php create mode 100755 backend/src/stubs/Registry.php create mode 100755 backend/src/stubs/Tax.php create mode 100755 backend/src/stubs/config.php create mode 100755 backend/src/stubs/currency.php create mode 100755 backend/src/stubs/phpstan-bootstrap.php create mode 100755 backend/src/tests/Helpers/DatabaseHelpers.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/ExampleDatabaseConnection.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/ExampleEmailWithConfig.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/ExamplePersonRepository.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/ExamplePersonService.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/ExampleSmsGateway.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/ExampleUserService.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/FilterDTO.php create mode 100755 backend/src/tests/Helpers/ExampleClasses/TestClassWithMethod.php create mode 100755 backend/src/tests/Helpers/OpencartUrl.php create mode 100755 backend/src/tests/Integration/Services/CartServiceTest.php create mode 100755 backend/src/tests/Integration/Services/OrderCreateServiceTest.php create mode 100755 backend/src/tests/Integration/Services/ProductsServiceTest.php create mode 100755 backend/src/tests/README.md create mode 100755 backend/src/tests/Telegram/TelegramServiceTest.php create mode 100755 backend/src/tests/TestCase.php create mode 100755 backend/src/tests/Unit/Framework/ArrTest.php create mode 100755 backend/src/tests/Unit/Framework/BuilderTest.php create mode 100755 backend/src/tests/Unit/Framework/ContainerTest.php create mode 100755 backend/src/tests/Unit/Framework/CriteriaBuilderTest.php create mode 100755 backend/src/tests/Unit/Framework/ExecutionTimeProfilerTest.php create mode 100755 backend/src/tests/Unit/Framework/GenericCollectionTest.php create mode 100755 backend/src/tests/Unit/Framework/HelpersTest.php create mode 100755 backend/src/tests/Unit/Framework/ImageTool/ImageFactoryTest.php create mode 100755 backend/src/tests/Unit/Framework/JsonResponseTest.php create mode 100755 backend/src/tests/Unit/Framework/MySqlGrammarTest.php create mode 100755 backend/src/tests/Unit/Framework/PaginationHelperTest.php create mode 100755 backend/src/tests/Unit/Framework/QueryResultTest.php create mode 100755 backend/src/tests/Unit/Framework/RawExpressionTest.php create mode 100755 backend/src/tests/Unit/Framework/RequestTest.php create mode 100755 backend/src/tests/Unit/Framework/RuleSerializerTest.php create mode 100755 backend/src/tests/Unit/Framework/Scheduler/JobTest.php create mode 100755 backend/src/tests/Unit/Framework/Scheduler/ScheduleJobRegistryTest.php create mode 100755 backend/src/tests/Unit/Framework/Scheduler/SchedulerResultTest.php create mode 100755 backend/src/tests/Unit/Framework/Scheduler/SchedulerServiceTest.php create mode 100755 backend/src/tests/Unit/Framework/SettingsTest.php create mode 100755 backend/src/tests/Unit/Framework/StrTest.php create mode 100755 backend/src/tests/Unit/Framework/TableTest.php create mode 100755 backend/src/tests/Unit/Framework/TranslatorTest.php create mode 100755 backend/src/tests/Unit/Framework/UtilsTest.php create mode 100755 backend/src/tests/Unit/Framework/Validator/ErrorBagTest.php create mode 100755 backend/src/tests/Unit/Framework/Validator/ValidatorTest.php create mode 100755 backend/src/tests/Unit/Services/OrderCreateServiceTest.php create mode 100755 backend/src/tests/fixtures/criteria_builder/product_attribute/contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_attribute/contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_categories/contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_categories/contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_categories/not_contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_categories/not_contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_category/contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_category/contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_category/not_contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_category/not_contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_manufacturer/contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_manufacturer/contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_manufacturer/not_contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_manufacturer/not_contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/is_empty/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/is_empty/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/is_not_empty/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/is_not_empty/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/not_contains/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/not_contains/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/not_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_model/not_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_from_empty_string/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_from_empty_string/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_from_null/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_from_null/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_num_as_string/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/between_num_as_string/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/equals_num_as_string/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/equals_num_as_string/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/greater/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/greater/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/greater_or_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/greater_or_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/include_discounts/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/include_discounts/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/include_specials/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/include_specials/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/less/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/less/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/less_or_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/less_or_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/not_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/not_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/not_include_specials/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_price/not_include_specials/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/between/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/between/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/between_with_number_as_string/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/greater/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/greater/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/greater_or_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/less/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/less/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/less_or_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/less_or_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/not_equals/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_quantity/not_equals/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_status/equals_false/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_status/equals_false/output.sql create mode 100755 backend/src/tests/fixtures/criteria_builder/product_status/equals_true/input.json create mode 100755 backend/src/tests/fixtures/criteria_builder/product_status/equals_true/output.sql create mode 100644 deployment/apache2/cors.conf create mode 100644 deployment/build.dockerfile create mode 100644 deployment/config.php create mode 100644 deployment/mysql/docker-entrypoint-initdb.d/create_database_for_tests.sh create mode 100644 docker-compose.yaml create mode 100644 frontend/admin/.editorconfig create mode 100644 frontend/admin/.gitattributes create mode 100644 frontend/admin/.gitignore create mode 100644 frontend/admin/.prettierrc.json create mode 100644 frontend/admin/README.md create mode 100644 frontend/admin/eslint.config.js create mode 100644 frontend/admin/index.html create mode 100644 frontend/admin/jsconfig.json create mode 100644 frontend/admin/package-lock.json create mode 100644 frontend/admin/package.json create mode 100644 frontend/admin/src/App.vue create mode 100644 frontend/admin/src/assets/base.css create mode 100644 frontend/admin/src/assets/logo.svg create mode 100644 frontend/admin/src/assets/main.css create mode 100644 frontend/admin/src/components/CronExpressionSelect.vue create mode 100644 frontend/admin/src/components/CronJobOrgUrlField.vue create mode 100644 frontend/admin/src/components/Form/CategoryLabel.vue create mode 100644 frontend/admin/src/components/Form/CategorySelect.vue create mode 100644 frontend/admin/src/components/Form/ResetCacheBtn.vue create mode 100644 frontend/admin/src/components/FormBuilder/CodeEditor.vue create mode 100644 frontend/admin/src/components/FormBuilder/FieldSettings.vue create mode 100644 frontend/admin/src/components/FormBuilder/FieldsPanel.vue create mode 100644 frontend/admin/src/components/FormBuilder/FormBuilder.vue create mode 100644 frontend/admin/src/components/FormBuilder/FormCanvas.vue create mode 100644 frontend/admin/src/components/FormBuilder/FormRenderer.vue create mode 100644 frontend/admin/src/components/FormBuilder/IconPicker.vue create mode 100644 frontend/admin/src/components/FormBuilder/composables/useFormFields.js create mode 100644 frontend/admin/src/components/FormBuilder/constants/availableFields.js create mode 100644 frontend/admin/src/components/FormBuilder/utils/fieldHelpers.js create mode 100644 frontend/admin/src/components/FormBuilder/utils/fieldTypes.js create mode 100644 frontend/admin/src/components/FormBuilder/utils/revisionManager.js create mode 100644 frontend/admin/src/components/FormBuilder/utils/schemaParser.js create mode 100644 frontend/admin/src/components/LogsViewer.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Blocks/BaseBlock.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Blocks/CategoriesTopBlock.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Blocks/ProductsCarouselBlock.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Blocks/ProductsFeedBlock.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Blocks/SliderBlock.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/AspectRatioSelect.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/BaseForm.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/CategoriesTopForm.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/FormItem.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/ProductsCarouselForm.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/ProductsFeedForm.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/Forms/SliderForm.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/MainPageConfigurator.vue create mode 100644 frontend/admin/src/components/MainPageConfigurator/availableBlocks.js create mode 100644 frontend/admin/src/components/OcImagePicker.vue create mode 100644 frontend/admin/src/components/RichTextEditor.vue create mode 100644 frontend/admin/src/components/ScheduledJobsList.vue create mode 100644 frontend/admin/src/components/Settings/ItemBool.vue create mode 100644 frontend/admin/src/components/Settings/ItemCategoriesSelect.vue create mode 100644 frontend/admin/src/components/Settings/ItemImage.vue create mode 100644 frontend/admin/src/components/Settings/ItemInput.vue create mode 100644 frontend/admin/src/components/Settings/ItemProductsSelect.vue create mode 100644 frontend/admin/src/components/Settings/ItemSelect.vue create mode 100644 frontend/admin/src/components/Settings/ItemTextarea.vue create mode 100644 frontend/admin/src/components/Settings/ItemTgBotToken.vue create mode 100644 frontend/admin/src/components/Settings/ItemTgChatID.vue create mode 100644 frontend/admin/src/components/Settings/ItemTgMessageTemplate.vue create mode 100644 frontend/admin/src/components/Settings/ItemTgMiniAppLink.vue create mode 100644 frontend/admin/src/components/Settings/ItemToggleButton.vue create mode 100644 frontend/admin/src/components/SettingsItem.vue create mode 100644 frontend/admin/src/components/Slider/CategorySelect.vue create mode 100644 frontend/admin/src/components/Slider/LinkSelector.vue create mode 100644 frontend/admin/src/components/Slider/ProductSelect.vue create mode 100644 frontend/admin/src/components/Switcher.vue create mode 100644 frontend/admin/src/components/TopLead.vue create mode 100644 frontend/admin/src/formkit.config.js create mode 100644 frontend/admin/src/formkit.theme.mjs create mode 100644 frontend/admin/src/main.js create mode 100644 frontend/admin/src/router/index.js create mode 100644 frontend/admin/src/stores/autocomplete.js create mode 100644 frontend/admin/src/stores/counter.js create mode 100644 frontend/admin/src/stores/forms.js create mode 100644 frontend/admin/src/stores/logs.js create mode 100644 frontend/admin/src/stores/settings.js create mode 100644 frontend/admin/src/stores/stats.js create mode 100644 frontend/admin/src/utils/constants..js create mode 100644 frontend/admin/src/utils/helpers.js create mode 100644 frontend/admin/src/utils/http.js create mode 100644 frontend/admin/src/utils/toastHelper.js create mode 100644 frontend/admin/src/views/AcmeShopPulseView.vue create mode 100644 frontend/admin/src/views/CronView.vue create mode 100644 frontend/admin/src/views/CustomersView.vue create mode 100644 frontend/admin/src/views/FormBuilderView.vue create mode 100644 frontend/admin/src/views/GeneralView.vue create mode 100644 frontend/admin/src/views/LogsView.vue create mode 100644 frontend/admin/src/views/MainPageView.vue create mode 100644 frontend/admin/src/views/MetricsView.vue create mode 100644 frontend/admin/src/views/OrdersView.vue create mode 100644 frontend/admin/src/views/StoreView.vue create mode 100644 frontend/admin/src/views/TelegramView.vue create mode 100644 frontend/admin/src/views/TextsView.vue create mode 100644 frontend/admin/tailwind.config.js create mode 100644 frontend/admin/vite.config.js create mode 100644 frontend/spa/README.md create mode 100644 frontend/spa/index.html create mode 100644 frontend/spa/package-lock.json create mode 100644 frontend/spa/package.json create mode 100644 frontend/spa/src/App.vue create mode 100644 frontend/spa/src/AppLoading.vue create mode 100644 frontend/spa/src/ApplicationError.vue create mode 100644 frontend/spa/src/BrowserNotSupported.vue create mode 100644 frontend/spa/src/ShoppingCart.js create mode 100644 frontend/spa/src/WrongPlatformError.vue create mode 100644 frontend/spa/src/components/AppDebugMessage.vue create mode 100644 frontend/spa/src/components/BottomDrawer.vue create mode 100644 frontend/spa/src/components/BottomPanel.vue create mode 100644 frontend/spa/src/components/CartButton.vue create mode 100644 frontend/spa/src/components/CategoriesList/CategoryItem.vue create mode 100644 frontend/spa/src/components/Dock.vue create mode 100644 frontend/spa/src/components/FullScreenImageViewer.vue create mode 100644 frontend/spa/src/components/Icons/IconFunnel.vue create mode 100644 frontend/spa/src/components/Icons/IconWarning.vue create mode 100644 frontend/spa/src/components/Loader.vue create mode 100644 frontend/spa/src/components/LoadingFullScreen.vue create mode 100644 frontend/spa/src/components/MainPage/Blocks/BaseBlock.vue create mode 100644 frontend/spa/src/components/MainPage/Blocks/CategoriesTopBlock.vue create mode 100644 frontend/spa/src/components/MainPage/Blocks/ErrorBlock.vue create mode 100644 frontend/spa/src/components/MainPage/Blocks/ProductsCarouselBlock.vue create mode 100644 frontend/spa/src/components/MainPage/Blocks/ProductsFeedBlock.vue create mode 100644 frontend/spa/src/components/MainPage/Blocks/SliderBlock.vue create mode 100644 frontend/spa/src/components/MainPage/EmptyBlocks.vue create mode 100644 frontend/spa/src/components/MainPage/MainPage.vue create mode 100644 frontend/spa/src/components/Navbar.vue create mode 100644 frontend/spa/src/components/NoProducts.vue create mode 100644 frontend/spa/src/components/Price.vue create mode 100644 frontend/spa/src/components/PrivacyPolicy.vue create mode 100644 frontend/spa/src/components/ProductFilters/Components/ForMainPage.vue create mode 100644 frontend/spa/src/components/ProductFilters/Components/ProductCategory/ProductCategory.vue create mode 100644 frontend/spa/src/components/ProductFilters/Components/ProductCategory/SelectOption.vue create mode 100644 frontend/spa/src/components/ProductFilters/Components/ProductPrice.vue create mode 100644 frontend/spa/src/components/ProductImageSwiper.vue create mode 100644 frontend/spa/src/components/ProductItem/Price.vue create mode 100644 frontend/spa/src/components/ProductItem/PriceTitle.vue create mode 100644 frontend/spa/src/components/ProductItem/ProductTitle.vue create mode 100644 frontend/spa/src/components/ProductNotFound.vue create mode 100644 frontend/spa/src/components/ProductOptions/Cart/OptionCheckbox.vue create mode 100644 frontend/spa/src/components/ProductOptions/Cart/OptionRadio.vue create mode 100644 frontend/spa/src/components/ProductOptions/Cart/OptionText.vue create mode 100644 frontend/spa/src/components/ProductOptions/ProductOptions.vue create mode 100644 frontend/spa/src/components/ProductOptions/Types/OptionCheckbox.vue create mode 100644 frontend/spa/src/components/ProductOptions/Types/OptionRadio.vue create mode 100644 frontend/spa/src/components/ProductOptions/Types/OptionSelect.vue create mode 100644 frontend/spa/src/components/ProductOptions/Types/OptionTemplate.vue create mode 100644 frontend/spa/src/components/ProductOptions/Types/OptionText.vue create mode 100644 frontend/spa/src/components/ProductOptions/Types/OptionTextarea.vue create mode 100644 frontend/spa/src/components/ProductsList.vue create mode 100644 frontend/spa/src/components/Quantity.vue create mode 100644 frontend/spa/src/components/SearchInput.vue create mode 100644 frontend/spa/src/components/SingleProductImageSwiper.vue create mode 100644 frontend/spa/src/components/Slider.vue create mode 100644 frontend/spa/src/components/SwipeToBack.vue create mode 100644 frontend/spa/src/composables/useHapticFeedback.js create mode 100644 frontend/spa/src/composables/useHapticScroll.js create mode 100644 frontend/spa/src/composables/useSwipeBack.js create mode 100644 frontend/spa/src/composables/useTgData.js create mode 100644 frontend/spa/src/constants/options.js create mode 100644 frontend/spa/src/constants/tPulseEvents.js create mode 100644 frontend/spa/src/constants/yaMetrikaGoals.js create mode 100644 frontend/spa/src/errors.js create mode 100644 frontend/spa/src/formkit.config.js create mode 100644 frontend/spa/src/formkit.theme.mjs create mode 100644 frontend/spa/src/helpers.js create mode 100644 frontend/spa/src/main.js create mode 100644 frontend/spa/src/router.js create mode 100644 frontend/spa/src/stores/BlocksStore.js create mode 100644 frontend/spa/src/stores/CartStore.js create mode 100644 frontend/spa/src/stores/CategoriesStore.js create mode 100644 frontend/spa/src/stores/CheckoutStore.js create mode 100644 frontend/spa/src/stores/FormsStore.js create mode 100644 frontend/spa/src/stores/KeyboardStore.js create mode 100644 frontend/spa/src/stores/ProductFiltersStore.js create mode 100644 frontend/spa/src/stores/ProductsStore.js create mode 100644 frontend/spa/src/stores/Pulse.js create mode 100644 frontend/spa/src/stores/SearchStore.js create mode 100644 frontend/spa/src/stores/SettingsStore.js create mode 100644 frontend/spa/src/stores/yaMetrikaStore.js create mode 100644 frontend/spa/src/style.css create mode 100644 frontend/spa/src/translations.js create mode 100644 frontend/spa/src/utils/AppMetaInitializer.ts create mode 100644 frontend/spa/src/utils/ftch.js create mode 100644 frontend/spa/src/utils/yaMetrika.js create mode 100644 frontend/spa/src/views/Account.vue create mode 100644 frontend/spa/src/views/BaseViewWrapper.vue create mode 100644 frontend/spa/src/views/Cart.vue create mode 100644 frontend/spa/src/views/CategoriesList.vue create mode 100644 frontend/spa/src/views/Checkout.vue create mode 100644 frontend/spa/src/views/Filters.vue create mode 100644 frontend/spa/src/views/Home.vue create mode 100644 frontend/spa/src/views/OrderCreated.vue create mode 100644 frontend/spa/src/views/Product.vue create mode 100644 frontend/spa/src/views/Products.vue create mode 100644 frontend/spa/src/views/Search.vue create mode 100644 frontend/spa/tailwind.config.js create mode 100644 frontend/spa/tests/setup.js create mode 100644 frontend/spa/tests/unit/ShoppingCart.test.js create mode 100644 frontend/spa/tests/unit/components/Price.test.js create mode 100644 frontend/spa/tests/unit/helpers.test.js create mode 100644 frontend/spa/tests/unit/stores/CartStore.test.js create mode 100644 frontend/spa/tests/unit/utils/AppMetaInitializer.test.ts create mode 100644 frontend/spa/tests/unit/utils/ftch.test.js create mode 100644 frontend/spa/vite.config.js create mode 100644 frontend/spa/vitest.config.js create mode 100755 scripts/build_phar.sh create mode 100755 scripts/ci/build.sh create mode 100644 scripts/ci/create-phar.php create mode 100644 scripts/ci/extract-phar.php create mode 100755 scripts/download_oc_store.sh create mode 100755 scripts/install_ocstore.sh create mode 100644 scripts/link.php create mode 100755 scripts/wait_for_containers.sh create mode 100644 scripts/wait_for_mysql.sh diff --git a/.cursor/agents.md b/.cursor/agents.md new file mode 100644 index 0000000..5ee02db --- /dev/null +++ b/.cursor/agents.md @@ -0,0 +1,64 @@ +# Cursor AI Agents Configuration + +## AI Roles and Behavior Rules + +### Primary Role: Senior Full-Stack Developer + +You are an experienced full-stack developer specializing in: + +- Modular ECommerce development +- Custom frameworks (ECommerce Framework) +- PHP 7.4+ with modern best practices +- Vue.js 3 (Composition API) +- Telegram Mini App development + +### Coding Rules + +1. **Always follow existing project patterns** +2. **Do not create duplicates – reuse existing utilities** +3. **Follow project naming conventions** +4. **Test changes before committing** +5. **Document public APIs** +6. **Write comments only in English and only when truly justified** + +### Commit Rules + +1. **Follow Conventional Commits** + - Use prefixes: `feat:`, `fix:`, `chore:`, `refactor:`, `style:`, `test:`, `docs:` + - Format: `: ` (first line up to 72 characters) + - After an empty line – detailed description of changes + +2. **Commit language** + - All commits must be in **English** + - Provide detailed description of changes in the commit body + - List all changed files and key changes + +3. **Examples of good commits** + ``` + feat: add setting to control category products button visibility + + - Add show_category_products_button field to StoreDTO + - Update SettingsSerializerService to support new field + - Add setting in admin panel on 'Store' tab with toggle + - Pass setting to SPA through SettingsHandler + - Button displays only for categories with child categories + - Add default value true to configuration + ``` + +### Forbidden + +- Hardcoding values (use configs/settings instead) +- Ignoring error handling +- Creating circular dependencies + +For frontend development use: + +- Vue.js 3 (Composition API) +- Avoid using `watch` where a cleaner solution is possible +- For `frontend/admin` use Tailwind 4 with the `tw:` prefix +- For `frontend/spa` use Tailwind 4 without a prefix +- For `frontend/admin` use FontAwesome 4 icons, because it is already bundled with ECommerce 3 +- For `frontend/admin` use VuePrime 4 components +- For `frontend/spa` use Daisy UI +- To get the standard ECommerce table name, use the `db_table` helper or add the `DB_PREFIX` constant before the table name. This way you will get the table name with prefix. +- All tables of my `AcmeShop` module start with the `acmeshop_` prefix. Migration examples are located in `module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations` diff --git a/.cursor/config.json b/.cursor/config.json new file mode 100644 index 0000000..4be6904 --- /dev/null +++ b/.cursor/config.json @@ -0,0 +1,44 @@ +{ + "rules": { + "preferCompositionAPI": true, + "strictTypes": true, + "noHardcodedValues": true, + "useDependencyInjection": true + }, + "paths": { + "acmeshop_module": "module/oc_telegram_shop/upload/oc_telegram_shop", + "frontendAdmin": "frontend/admin", + "telegramShopSpa": "frontend/spa", + "migrations": "module/oc_telegram_shop/upload/oc_telegram_shop/database/migrations", + "acmeshopHandlers": "module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers", + "adminHandlers": "module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers", + "models": "module/oc_telegram_shop/upload/oc_telegram_shop/src/Models", + "framework": "module/oc_telegram_shop/upload/oc_telegram_shop/framework" + }, + "naming": { + "classes": "PascalCase", + "methods": "camelCase", + "variables": "camelCase", + "constants": "UPPER_SNAKE_CASE", + "files": "PascalCase for classes, kebab-case for others", + "tables": "snake_case with acmeshop_ prefix" + }, + "php": { + "version": "7.4+", + "preferVersion": "7.4+", + "psr12": true + }, + "javascript": { + "version": "ES2020+", + "framework": "Vue 3 Composition API", + "stateManagement": "Pinia", + "uiLibrary": "PrimeVue (admin), Tailwind (spa)" + }, + "database": { + "queryBuilder": true, + "migrations": true, + "tablePrefix": "acmeshop_", + "noForeignKeys": true + } +} + diff --git a/.cursor/features/acme-pulse-heartbeat.md b/.cursor/features/acme-pulse-heartbeat.md new file mode 100644 index 0000000..7ffe52a --- /dev/null +++ b/.cursor/features/acme-pulse-heartbeat.md @@ -0,0 +1,38 @@ +## AcmeShop Pulse Heartbeat Telemetry + +### Goal +Send heartbeat telemetry to AcmeShop Pulse once per hour to capture store state and environment versions without any user interaction. + +### Backend (`module/oc_telegram_shop/upload/oc_telegram_shop`) +- `framework/AcmeShopPulse/AcmeShopPulseService.php` + - New method `handleHeartbeat()` collects data: domain (via `Utils::getCurrentDomain()`), bot username (via `TelegramService::getMe()`), PHP version, module version (from `composer.json`), ECommerce versions (`VERSION` and `VERSION_CORE`), current UTC timestamp. + - The last successful ping is cached (key `acmeshop_pulse_heartbeat`, TTL 1 hour) via existing `CacheInterface`. + - Heartbeat signature is created via a dedicated `PayloadSigner` that uses `pulse.heartbeat_secret`/`PULSE_HEARTBEAT_SECRET`. Warnings are logged on cache/bot/signature failures. + - Request is sent to the `heartbeat` endpoint with a 2‑second timeout and `X-MEGAPAY-VERSION` header taken from `composer.json`. +- `framework/AcmeShopPulse/AcmeShopPulseServiceProvider.php` + - Registers main `PayloadSigner` (by `pulse.api_key`) and a separate heartbeat signer (by `pulse.heartbeat_secret` or `PULSE_HEARTBEAT_SECRET`), injects `LoggerInterface`. +- `src/Handlers/TelemetryHandler.php` + `src/routes.php` + - Adds `heartbeat` route that calls `handleHeartbeat()` and returns `{ status: "ok" }`. Logger writes warnings on problems. + +### Frontend (`frontend/spa`) +- `src/utils/ftch.js`: new `heartbeat()` function calls `api_action=heartbeat`. +- `src/stores/Pulse.js`: new `heartbeat` action uses the API function and logs the result. +- `src/main.js`: after `pulse.ingest(...)` the code calls `pulse.heartbeat()` without blocking the chain. + +### Configuration / ENV +- `PULSE_API_HOST` — base URL of AcmeShop Pulse (used for both events and heartbeat). +- `PULSE_TIMEOUT` — global HTTP timeout (for heartbeat forced to 2 seconds). +- `PULSE_HEARTBEAT_SECRET` (or `pulse.heartbeat_secret` in settings) — shared secret for signing heartbeat. Required; otherwise heartbeat will not be sent. +- `pulse.api_key` — legacy API key, used only for event ingest. + +### Behavior +1. Frontend (SPA) calls `heartbeat` on app initialization (fire-and-forget). +2. Backend checks the cache. If one hour has not passed yet, `handleHeartbeat()` returns without any requests. +3. When needed, data is collected, signed via heartbeat signer, and sent as a POST request to `/heartbeat`. +4. Any failures (bot info, signature, HTTP) are logged as warnings so they do not affect end users. + +### TODO / Possible improvements +- Optionally move heartbeat triggering to cron/CLI so it does not depend on frontend. +- Add heartbeat success metrics to the admin panel. + + diff --git a/.cursor/prompts/api-generation.md b/.cursor/prompts/api-generation.md new file mode 100644 index 0000000..f5a1943 --- /dev/null +++ b/.cursor/prompts/api-generation.md @@ -0,0 +1,127 @@ +# Prompts for API generation + +## Creating a new API endpoint + +``` +Create a new API endpoint [ENDPOINT_NAME] for [DESCRIPTION]: + +1. Handler in [HANDLER_PATH]: + - Method handle() accepts Request + - Validate input data + - Use a Service for business logic + - Return JsonResponse with correct structure + - Handle errors with logging + +2. Service in [SERVICE_PATH]: + - Business logic + - Work with Model + - Data validation + - Exception handling + +3. Model in [MODEL_PATH] (if needed): + - Methods for working with the database + - Use Query Builder + - Proper method typing + +4. Route in routes.php: + - Add a route with the correct name + +5. Migration (if a new table is needed): + - Create a migration in database/migrations/ + - Use fixed acmeshop_ prefix for the table + - Add indexes where necessary + +Follow the project MVC-L architecture and reuse existing patterns. +``` + +## Creating a CRUD API + +``` +Create a full CRUD API for entity [ENTITY_NAME]: + +1. Handler with methods: + - list() – list with pagination and filtering + - get() – fetch a single record + - create() – create + - update() – update + - delete() – delete + +2. Service with business logic for all operations + +3. Model with methods: + - findAll() – list + - findById() – by ID + - create() – create + - update() – update + - delete() – delete + +4. DTO for data validation + +5. Migration for table [TABLE_NAME] + +6. Routes for all endpoints + +Use server-side pagination, filtering, and sorting for list(). +``` + +## Creating an Admin API endpoint + +``` +Create an Admin API endpoint [ENDPOINT_NAME] in bastion/Handlers/: + +1. Handler in bastion/Handlers/[HANDLER_NAME].php: + - Use Request to read parameters + - Validate data + - Call a Service for business logic + - Return JsonResponse with structure { data: { data: [...], totalRecords: ... } } + - Handle errors + +2. Service in bastion/Services/ (if needed): + - Admin-specific business logic + - Work with Models + +3. Route in bastion/routes.php + +4. Frontend component (if UI is needed): + - Vue component in frontend/admin/src/views/ + - Use PrimeVue components + - Server-side pagination/filtering + - Error handling with toast notifications + +Follow existing project patterns. +``` + +## Creating a Frontend API client + +``` +Create a function for working with API endpoint [ENDPOINT_NAME]: + +1. In frontend/[admin|spa]/src/utils/http.js: + - api[Method] function to call the endpoint + - Proper error handling + - Return a structured response + +2. Usage: + - Import in components + - Handle loading states + - Show toast notifications on errors + +Follow existing patterns in http.js. +``` + +## Creating a migration + +``` +Create a migration for table [TABLE_NAME]: + +1. File: database/migrations/[TIMESTAMP]_[DESCRIPTION].php +2. Use fixed acmeshop_ prefix for the table +3. Add all required fields with correct types +4. Add indexes for frequently used fields +5. Use utf8mb4_unicode_ci collation +6. Use InnoDB engine +7. Add created_at and updated_at timestamps +8. Do not create foreign keys (use indexes only) + +Follow the structure of existing migrations. +``` diff --git a/.cursor/prompts/changelog.md b/.cursor/prompts/changelog.md new file mode 100644 index 0000000..6d805dd --- /dev/null +++ b/.cursor/prompts/changelog.md @@ -0,0 +1,101 @@ +# Changelog Documentation Rules + +## General requirements + +- **Format**: Markdown +- **Structure**: One version = one page +- **Style**: Professional, concise, without marketing slogans +- **Language**: English +- **Target audience**: Developers and store owners +- **Content**: Only key changes, without unnecessary technical details + +## Page structure + +### Intro paragraph +- Short release description (1–2 sentences) +- Add 1–2 sentences about key changes +- Mention the main features and improvements + +### Sections (strictly in this order) + +1. **🚀 Added** – new features and capabilities +2. **✨ Improved** – improvements to existing features +3. **🐞 Fixed** – bug fixes +4. **⚠️ Breaking changes** – non‑backward compatible changes +5. **🔧 Technical changes** – technical changes (separate section) + +### Section rules + +- **Do NOT** add a section if it has no items +- Use markdown bullet lists, no numbering +- Do not add links unless they are explicitly specified +- Do not add extra explanations, conclusions, or summaries +- Output only the content of the Markdown file, without comments + +## Separating changes + +### Business logic and processes +Sections "Added", "Improved", "Fixed", "Breaking changes" contain only changes related to: +- Store business processes +- User experience +- Functionality for store owners +- Selling / value‑driven features + +### Technical changes +All technical changes go into a separate **🔧 Technical changes** section: +- No subsections, everything in a single list +- Only the most important technical changes +- Do not describe details that are not interesting to end users + +## Writing style + +### Terminology +- Use English terms (e.g. "navbar", not a localized variant) +- Avoid low‑level technical terms in business sections (e.g. "customer_id" → "automatic customer linking") + +### Descriptions + +**For key selling features:** +- Provide detailed descriptions +- Highlight capabilities and advantages +- Emphasize value for the user + +**For technical changes:** +- Be brief and to the point +- Avoid unnecessary details + +**For regular features:** +- Be concise but informative +- Focus on user benefit + +## Ordering + +### In the "Added" section +Place key selling features **first**: +1. Main page configuration system based on blocks +2. Form builder based on FormKit +3. Yandex.Metrica integration +4. Privacy policy +5. Support for coupons and gift certificates +6. Other features + +## Examples of good wording + +### ✅ Good +- "Automatic linking of Telegram customers with ECommerce customers: the system automatically finds and links customers from the Telegram shop with existing customers in ECommerce by email or phone number, creating a unified customer base" + +### ❌ Bad +- "Saving customer_id with the order to link with ECommerce customers: automatic linking of orders from Telegram with customers in ECommerce, unified customer base" + +### ✅ Good +- "Navbar with logo and application name" + +### ❌ Bad +- "Navigation bar with logo and application name" (too verbose, no added value) + +## What not to include + +- Internal development details (tests, static analysis, obfuscation) +- Technical details not interesting to users +- Excessively detailed technical descriptions +- Marketing slogans and calls to action diff --git a/.cursor/prompts/documentation.md b/.cursor/prompts/documentation.md new file mode 100644 index 0000000..9299de5 --- /dev/null +++ b/.cursor/prompts/documentation.md @@ -0,0 +1,61 @@ +# Prompts for documentation + +## Documenting a class + +``` +Add PHPDoc documentation for class [CLASS_NAME]: +1. Description of the class and its purpose +2. @package tag +3. @author tag +4. Documentation for all public methods +5. Documentation for public properties +6. Usage examples where appropriate +``` + +## Documenting a method + +``` +Add PHPDoc for method [METHOD_NAME]: +1. Method description +2. @param for all parameters with types +3. @return with the return type +4. @throws for all exceptions +5. Usage examples if the logic is complex +``` + +## Documenting an API endpoint + +``` +Create documentation for API endpoint [ENDPOINT_NAME]: +1. Description of purpose +2. HTTP method and path +3. Request parameters (query/body) +4. Response format (JSON structure) +5. Error codes +6. Request/response examples +7. Authorization requirements +``` + +## Documenting a Vue component + +``` +Add documentation for Vue component [COMPONENT_NAME]: +1. Component description +2. Props with types and descriptions +3. Emits with descriptions +4. Slots if present +5. Usage examples +6. Dependencies on other components +``` + +## Creating a README + +``` +Create README.md for [MODULE/COMPONENT]: +1. Purpose description +2. Installation/configuration +3. Usage with examples +4. API documentation +5. Configuration options +6. Troubleshooting +``` diff --git a/.cursor/prompts/refactoring.md b/.cursor/prompts/refactoring.md new file mode 100644 index 0000000..559c70e --- /dev/null +++ b/.cursor/prompts/refactoring.md @@ -0,0 +1,87 @@ +# Prompts for refactoring + +## General refactoring + +``` +Analyze the code in file [FILE_PATH] and perform refactoring: +1. Remove duplicated code +2. Improve readability +3. Apply SOLID principles +4. Add error handling where necessary +5. Improve typing +6. Add documentation for public methods +7. Ensure the code follows the project's MVC-L architecture +8. Use existing utilities and services instead of creating new ones +``` + +## Refactoring a Handler + +``` +Refactor Handler [HANDLER_NAME]: +1. Extract business logic into a separate Service +2. Add validation for input data +3. Improve error handling with logging +4. Use DTOs for data transfer +5. Add PHPDoc comments +6. Ensure Dependency Injection is used +7. Optimize database queries if needed +``` + +## Refactoring a Model + +``` +Refactor Model [MODEL_NAME]: +1. Ensure all queries use the Query Builder +2. Add methods for common operations (findBy, findAll, create, update) +3. Add data validation before saving +4. Improve method typing +5. Add PHPDoc comments +6. Use transactions for complex operations +``` + +## Refactoring a Vue component + +``` +Refactor Vue component [COMPONENT_NAME]: +1. Extract logic into composable functions +2. Improve typing for props and emits +3. Optimize computed properties +4. Add error handling +5. Improve template structure +6. Add loading states +7. Use existing project utilities +``` + +## Removing duplication + +``` +Find and remove code duplication in: +- [FILE_PATH_1] +- [FILE_PATH_2] +- [FILE_PATH_3] + +Create shared utilities/services where appropriate, following the project architecture. +``` + +## Improving performance + +``` +Analyze performance of the code in [FILE_PATH]: +1. Optimize database queries (use indexes, avoid N+1) +2. Add caching where appropriate +3. Optimize algorithms +4. Reduce the number of API calls +5. Use lazy loading on the frontend +``` + +## Improving security + +``` +Improve security of the code in [FILE_PATH]: +1. Add validation for all input data +2. Use prepared statements (Query Builder) +3. Add CSRF protection where necessary +4. Validate access rights +5. Sanitize output data +6. Add rate limiting where necessary +``` diff --git a/.cursor/prompts/testing.md b/.cursor/prompts/testing.md new file mode 100644 index 0000000..140f7fb --- /dev/null +++ b/.cursor/prompts/testing.md @@ -0,0 +1,52 @@ +# Prompts for testing + +## Creating a unit test + +``` +Create a unit test for [CLASS_NAME] in tests/Unit/: + +1. Use PHPUnit +2. Cover all public methods +3. Test successful scenarios +4. Test error handling +5. Use mocks for dependencies +6. Follow the structure of existing tests +7. Use the project's base TestCase class +``` + +## Creating an integration test + +``` +Create an integration test for [FEATURE_NAME] in tests/Integration/: + +1. Test the full flow from request to response +2. Use a test database +3. Clean up data after tests +4. Test real-life usage scenarios +5. Verify data validation +6. Verify error handling +``` + +## Creating a Vue component test + +``` +Create a test for Vue component [COMPONENT_NAME] in frontend/[admin|spa]/tests/: + +1. Use Vitest +2. Test component rendering +3. Test props +4. Test events (emits) +5. Test user interactions +6. Use mocks for API calls +7. Follow the structure of existing tests +``` + +## Test coverage + +``` +Analyze test coverage for [FILE_PATH]: +1. Determine which methods are not covered by tests +2. Create tests for critical methods +3. Ensure edge cases are tested +4. Add tests for error handling +``` diff --git a/.cursor/rules/architecture.md b/.cursor/rules/architecture.md new file mode 100644 index 0000000..9d782de --- /dev/null +++ b/.cursor/rules/architecture.md @@ -0,0 +1,201 @@ +# Architectural Rules + +## ECommerce Framework Architecture + +### MVC-L Pattern + +The project uses a modified MVC-L pattern (Model-View-Controller-Language): + +- **Model**: Classes in `src/Models/` – data access and database operations +- **View**: Vue components on the frontend, JSON responses on the backend +- **Controller**: Handlers in `src/Handlers/` and `bastion/Handlers/` +- **Language**: Translator in `framework/Translator/` + +### Dependency Injection + +All dependencies are injected via the Container: + +```php +// ✅ Correct +public function __construct( + private Builder $builder, + private TelegramCustomer $telegramCustomerModel +) {} + +// ❌ Incorrect +public function __construct() { + $this->builder = new Builder(...); +} +``` + +### Service Providers + +Services are registered via Service Providers: + +```php +class MyServiceProvider extends ServiceProvider +{ + public function register(): void + { + $this->app->singleton(MyService::class, function ($app) { + return new MyService($app->get(Dependency::class)); + }); + } +} +``` + +### Routes + +Routes are defined in `routes.php`: + +```php +return [ + 'actionName' => [HandlerClass::class, 'methodName'], +]; +``` + +### Handlers (Controllers) + +Handlers process HTTP requests: + +```php +class MyHandler +{ + public function handle(Request $request): JsonResponse + { + // Validation + // Business logic via Services + // Return JsonResponse + } +} +``` + +### Models + +Models work with data: + +```php +class MyModel +{ + public function __construct( + private ConnectionInterface $database, + private Builder $builder + ) {} + + public function findById(int $id): ?array + { + return $this->builder->newQuery() + ->from($this->tableName) + ->where('id', '=', $id) + ->firstOrNull(); + } +} +``` + +### Services + +Services contain business logic: + +```php +class MyService +{ + public function __construct( + private MyModel $model + ) {} + + public function doSomething(array $data): array + { + // Business logic + return $this->model->create($data); + } +} +``` + +### Migrations + +Migrations live in `database/migrations/`: + +```php +return new class extends Migration { + public function up(): void + { + $this->database->statement('CREATE TABLE ...'); + } +}; +``` + +### Query Builder + +Always use Query Builder instead of raw SQL: + +```php +// ✅ Correct +$query = $this->builder->newQuery() + ->select(['id', 'name']) + ->from('table_name') + ->where('status', '=', 'active') + ->get(); + +// ❌ Incorrect +$result = $this->database->query("SELECT * FROM table_name WHERE status = 'active'"); +``` + +### Frontend Architecture + +#### Admin Panel (Vue 3) + +- Composition API +- Pinia for state management +- PrimeVue for UI components +- Axios for HTTP requests +- Vue Router for navigation + +#### SPA (Telegram Mini App) + +- Composition API +- Pinia stores +- Tailwind CSS for styles +- Telegram WebApp API +- Vue Router + +### Naming Conventions + +- **Classes**: PascalCase (`TelegramCustomerService`) +- **Methods**: camelCase (`getCustomers`) +- **Variables**: camelCase (`$customerData`) +- **Constants**: UPPER_SNAKE_CASE (`MAX_RETRIES`) +- **Files**: PascalCase for classes, kebab-case for everything else +- **Tables**: snake_case with `acmeshop_` prefix + +### Error Handling + +Always handle errors: + +```php +try { + $result = $this->service->doSomething(); +} catch (SpecificException $e) { + $this->logger->error('Error message', ['exception' => $e]); + throw new UserFriendlyException('User message'); +} +``` + +### Configuration + +Use configuration files in `configs/`: + +```php +$config = $this->app->getConfigValue('app.setting_name'); +``` + +### Caching + +Use Cache Service for caching: + +```php +$cache = $this->app->get(CacheInterface::class); +$value = $cache->get('key', function() { + return expensiveOperation(); +}); +``` + diff --git a/.cursor/rules/form-builder.md b/.cursor/rules/form-builder.md new file mode 100644 index 0000000..31b13cf --- /dev/null +++ b/.cursor/rules/form-builder.md @@ -0,0 +1,70 @@ +# FormBuilder System Context + +## Architectural Overview +The FormBuilder ecosystem is a strictly typed Vue 3 application module designed to generate standard FormKit Schema JSON. It eschews internal DTOs in favor of direct schema manipulation. + +### Core Components +1. **FormBuilderView (`views/FormBuilderView.vue`)**: + * **Role**: Smart container / Data fetcher. + * **Responsibility**: Fetches form data from API (`GET /api/admin/forms/{alias}`), handles loading states, and passes data to `FormBuilder`. + * **Contract**: Expects API response `{ data: { schema: Array, is_custom: Boolean, ... } }`. + +2. **FormBuilder (`components/FormBuilder/FormBuilder.vue`)**: + * **Role**: Main Orchestrator / State Owner. + * **Responsibility**: Manages `v-model` (schema), mode switching (Visual/Code/Preview), and provides state to children. + * **State Management**: Uses `defineModel` for `formFields` (schema) and `isCustom` (mode flag). Uses `provide('formFields')` and `provide('selectedFieldId')` for deep dependency injection. + * **Modes**: + * **Visual**: Drag-and-drop interface using `vuedraggable`. + * **Code**: Direct JSON editing of the FormKit schema. Sets `isCustom = true`. + * **Preview**: Renders the current schema using `FormKit`. + +3. **FormCanvas (`components/FormBuilder/FormCanvas.vue`)**: + * **Role**: Visual Editor Surface. + * **Responsibility**: Renders the draggable list of fields. + * **Implementation**: Uses `vuedraggable` bound to `formFields`. + * **UX**: Implements "Ghost" and "Drag" classes for visual feedback. Handles selection logic. + +4. **FieldsPanel (`components/FormBuilder/FieldsPanel.vue`)**: + * **Role**: Component Palette. + * **Responsibility**: Source of truth for available field types. + * **Implementation**: Uses `vuedraggable` with `pull: 'clone', put: false` to spawn new fields. + +5. **FieldSettings (`components/FormBuilder/FieldSettings.vue`)**: + * **Role**: Property Editor. + * **Responsibility**: Edits properties of the `selectedFieldId`. + * **Constraint**: Must use PrimeVue components for all inputs. + +## Data Flow & Invariants +1. **Schema Authority**: The FormKit Schema JSON is the single source of truth. There is no "internal model" separate from the schema. +2. **Reactivity**: + * `formFields` is an Array of Objects. + * Mutations must preserve reactivity. When using `v-model` or `provide/inject`, ensure array methods (splice, push, filter) are used correctly or replace the entire array reference if needed to trigger watchers. + * **Immutability**: `useFormFields` composable uses immutable patterns (returning new array references) to ensure `defineModel` in parent detects changes. +3. **Mode Logic**: + * Switching to **Code** mode sets `isCustom = true`. + * Switching to **Visual** mode sets `isCustom = false`. + * **Safety**: Switching modes triggers JSON validation. Invalid JSON prevents mode switch. +4. **Drag and Drop**: + * Powered by `vuedraggable` (Sortable.js). + * **Clone Logic**: `FieldsPanel` clones from `availableFields`. `FormCanvas` receives the clone. + * **ID Generation**: Unique IDs are generated upon cloning/addition to ensure key stability. + +## Naming & Conventions +* **Tailwind**: Use `tw:` prefix for all utility classes (e.g., `tw:flex`, `tw:p-4`). +* **Components**: PrimeVue components are the standard UI kit (Button, Panel, InputText, etc.). +* **Icons**: FontAwesome (`fa fa-*`). +* **Files**: PascalCase for components (`FormBuilder.vue`), camelCase for logic (`useFormFields.js`). + +## Integration Rules +* **Backend**: The backend stores the JSON blob directly. `FormBuilder` does not transform data before save; it emits the raw schema. +* **API**: `useFormsStore` handles API communication. + +## Pitfalls & Warnings +* **vuedraggable vs @formkit/drag-and-drop**: We strictly use `vuedraggable`. Do not re-introduce `@formkit/drag-and-drop`. +* **Watchers**: Avoid `watch` where `computed` or event handlers suffice, to prevent infinite loops in bidirectional data flow. +* **Tailwind Config**: Do not use `@apply` with `tw:` prefixed classes in ` +``` + +## Component Naming + +```vue + + + + + + + +``` + +## Props + +```vue + +``` + +## Emits + +```vue + +``` + +## Reactive State + +```vue + +``` + +## Computed Properties + +```vue + +``` + +## Event Handlers + +```vue + + + +``` + +## Conditional Rendering + +```vue + +``` + +## Lists + +```vue + +``` + +## Form Handling + +```vue + + + + diff --git a/frontend/admin/src/components/ScheduledJobsList.vue b/frontend/admin/src/components/ScheduledJobsList.vue new file mode 100644 index 0000000..66cc25d --- /dev/null +++ b/frontend/admin/src/components/ScheduledJobsList.vue @@ -0,0 +1,108 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemBool.vue b/frontend/admin/src/components/Settings/ItemBool.vue new file mode 100644 index 0000000..018c9f3 --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemBool.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/frontend/admin/src/components/Settings/ItemCategoriesSelect.vue b/frontend/admin/src/components/Settings/ItemCategoriesSelect.vue new file mode 100644 index 0000000..1686230 --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemCategoriesSelect.vue @@ -0,0 +1,116 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemImage.vue b/frontend/admin/src/components/Settings/ItemImage.vue new file mode 100644 index 0000000..8539edf --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemImage.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/frontend/admin/src/components/Settings/ItemInput.vue b/frontend/admin/src/components/Settings/ItemInput.vue new file mode 100644 index 0000000..5eb03df --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemInput.vue @@ -0,0 +1,72 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemProductsSelect.vue b/frontend/admin/src/components/Settings/ItemProductsSelect.vue new file mode 100644 index 0000000..8ddb3ca --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemProductsSelect.vue @@ -0,0 +1,116 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemSelect.vue b/frontend/admin/src/components/Settings/ItemSelect.vue new file mode 100644 index 0000000..7819f2d --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemSelect.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/frontend/admin/src/components/Settings/ItemTextarea.vue b/frontend/admin/src/components/Settings/ItemTextarea.vue new file mode 100644 index 0000000..72b7dc9 --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemTextarea.vue @@ -0,0 +1,41 @@ +