feat: add product interaction mode selector with three scenarios
- Add ItemToggleButton component for 3-way toggle in admin panel - Add product_interaction_mode setting with options: order, manager, browser - Add manager_username setting for Telegram manager contact - Remove store_enabled setting, replaced with product_interaction_mode - Create migration to automatically migrate store_enabled to product_interaction_mode - Update Product.vue to handle all three interaction modes - Update Dock.vue to show cart button only when product_interaction_mode is 'order' - Rename 'Магазин' tab to 'Витрина' in admin panel - Remove 'Разрешить покупки' option (now controlled via product_interaction_mode) - Set default product_interaction_mode to 'browser' - Update StoreDTO to remove enableStore field - Update SettingsHandler to return product_interaction_mode instead of store_enabled
This commit is contained in:
@@ -52,10 +52,11 @@ HTML,
|
||||
],
|
||||
|
||||
'store' => [
|
||||
'enable_store' => true,
|
||||
'feature_coupons' => true,
|
||||
'feature_vouchers' => true,
|
||||
'show_category_products_button' => true,
|
||||
'product_interaction_mode' => 'browser',
|
||||
'manager_username' => null,
|
||||
],
|
||||
|
||||
'texts' => [
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
use Openguru\OpenCartFramework\Migrations\Migration;
|
||||
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
$opencart = $this->app->get(OcRegistryDecorator::class);
|
||||
$opencart->load->model('setting/setting');
|
||||
|
||||
$currentSettings = $opencart->model_setting_setting->getSetting('module_telecart');
|
||||
|
||||
if (! $currentSettings || ! isset($currentSettings['module_telecart_settings'])) {
|
||||
$this->logger->info("Settings not found in database, migration skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
$allSettings = $currentSettings['module_telecart_settings'];
|
||||
|
||||
// Проверяем наличие store.enable_store
|
||||
$enableStore = Arr::get($allSettings, 'store.enable_store');
|
||||
|
||||
if ($enableStore !== null) {
|
||||
// Определяем значение product_interaction_mode на основе store.enable_store
|
||||
$productInteractionMode = filter_var($enableStore, FILTER_VALIDATE_BOOLEAN)
|
||||
? 'order'
|
||||
: 'browser';
|
||||
|
||||
// Устанавливаем product_interaction_mode, если его еще нет
|
||||
if (!isset($allSettings['store']['product_interaction_mode'])) {
|
||||
Arr::set($allSettings, 'store.product_interaction_mode', $productInteractionMode);
|
||||
$this->logger->info("Migrated store.enable_store to product_interaction_mode: {$enableStore} -> {$productInteractionMode}");
|
||||
} else {
|
||||
$this->logger->info("product_interaction_mode already exists, skipping migration but removing store.enable_store");
|
||||
}
|
||||
|
||||
// Удаляем store.enable_store из настроек
|
||||
Arr::unset($allSettings, 'store.enable_store');
|
||||
$this->logger->info("Removed store.enable_store from settings");
|
||||
|
||||
// Сохраняем обновленные настройки через OpenCart модель
|
||||
$opencart->model_setting_setting->editSetting('module_telecart', [
|
||||
'module_telecart_settings' => $allSettings,
|
||||
]);
|
||||
|
||||
$this->logger->info("Successfully migrated store.enable_store to product_interaction_mode and removed store.enable_store from settings");
|
||||
} else {
|
||||
$this->logger->info("store.enable_store not found in settings, migration skipped");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,37 +4,35 @@ namespace App\DTO\Settings;
|
||||
|
||||
final class StoreDTO
|
||||
{
|
||||
private bool $enableStore;
|
||||
private bool $featureCoupons;
|
||||
private bool $featureVouchers;
|
||||
private bool $showCategoryProductsButton;
|
||||
private string $productInteractionMode;
|
||||
private ?string $managerUsername;
|
||||
private string $ocDefaultCurrency;
|
||||
private bool $ocConfigTax;
|
||||
private int $ocStoreId;
|
||||
|
||||
public function __construct(
|
||||
bool $enableStore,
|
||||
bool $featureCoupons,
|
||||
bool $featureVouchers,
|
||||
bool $showCategoryProductsButton,
|
||||
string $productInteractionMode,
|
||||
?string $managerUsername,
|
||||
string $ocDefaultCurrency,
|
||||
bool $ocConfigTax,
|
||||
int $ocStoreId
|
||||
) {
|
||||
$this->enableStore = $enableStore;
|
||||
$this->featureCoupons = $featureCoupons;
|
||||
$this->featureVouchers = $featureVouchers;
|
||||
$this->showCategoryProductsButton = $showCategoryProductsButton;
|
||||
$this->productInteractionMode = $productInteractionMode;
|
||||
$this->managerUsername = $managerUsername;
|
||||
$this->ocDefaultCurrency = $ocDefaultCurrency;
|
||||
$this->ocConfigTax = $ocConfigTax;
|
||||
$this->ocStoreId = $ocStoreId;
|
||||
}
|
||||
|
||||
public function isEnableStore(): bool
|
||||
{
|
||||
return $this->enableStore;
|
||||
}
|
||||
|
||||
public function isFeatureCoupons(): bool
|
||||
{
|
||||
return $this->featureCoupons;
|
||||
@@ -50,6 +48,16 @@ final class StoreDTO
|
||||
return $this->showCategoryProductsButton;
|
||||
}
|
||||
|
||||
public function getProductInteractionMode(): string
|
||||
{
|
||||
return $this->productInteractionMode;
|
||||
}
|
||||
|
||||
public function getManagerUsername(): ?string
|
||||
{
|
||||
return $this->managerUsername;
|
||||
}
|
||||
|
||||
public function getOcDefaultCurrency(): string
|
||||
{
|
||||
return $this->ocDefaultCurrency;
|
||||
@@ -68,10 +76,12 @@ final class StoreDTO
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'enable_store' => $this->enableStore,
|
||||
// enable_store больше не сериализуется, так как заменен на product_interaction_mode
|
||||
'feature_coupons' => $this->featureCoupons,
|
||||
'feature_vouchers' => $this->featureVouchers,
|
||||
'show_category_products_button' => $this->showCategoryProductsButton,
|
||||
'product_interaction_mode' => $this->productInteractionMode,
|
||||
'manager_username' => $this->managerUsername,
|
||||
'oc_default_currency' => $this->ocDefaultCurrency,
|
||||
'oc_config_tax' => $this->ocConfigTax,
|
||||
'oc_store_id' => $this->ocStoreId,
|
||||
|
||||
@@ -45,7 +45,8 @@ class SettingsHandler
|
||||
'theme_dark' => $appConfig->getThemeDark(),
|
||||
'ya_metrika_enabled' => $this->settings->config()->getMetrics()->isYandexMetrikaEnabled(),
|
||||
'app_enabled' => $appConfig->isAppEnabled(),
|
||||
'store_enabled' => $this->settings->config()->getStore()->isEnableStore(),
|
||||
'product_interaction_mode' => $this->settings->config()->getStore()->getProductInteractionMode(),
|
||||
'manager_username' => $this->settings->config()->getStore()->getManagerUsername(),
|
||||
'feature_coupons' => $this->settings->config()->getStore()->isFeatureCoupons(),
|
||||
'feature_vouchers' => $this->settings->config()->getStore()->isFeatureVouchers(),
|
||||
'show_category_products_button' => $this->settings->config()->getStore()->isShowCategoryProductsButton(),
|
||||
|
||||
@@ -138,10 +138,11 @@ class SettingsSerializerService
|
||||
}
|
||||
|
||||
return new StoreDTO(
|
||||
$data['enable_store'] ?? true,
|
||||
$data['feature_coupons'] ?? true,
|
||||
$data['feature_vouchers'] ?? true,
|
||||
$data['show_category_products_button'] ?? true,
|
||||
$data['product_interaction_mode'] ?? 'browser',
|
||||
$data['manager_username'] ?? null,
|
||||
$data['oc_default_currency'],
|
||||
$data['oc_config_tax'],
|
||||
$data['oc_store_id']
|
||||
@@ -270,9 +271,7 @@ class SettingsSerializerService
|
||||
|
||||
private function validateStore(array $data): void
|
||||
{
|
||||
if (isset($data['enable_store']) && ! is_bool($data['enable_store'])) {
|
||||
throw new InvalidArgumentException('store.enable_store must be a boolean');
|
||||
}
|
||||
// enable_store больше не валидируется, так как заменен на product_interaction_mode
|
||||
|
||||
if (isset($data['feature_coupons']) && ! is_bool($data['feature_coupons'])) {
|
||||
throw new InvalidArgumentException('store.feature_coupons must be a boolean');
|
||||
@@ -286,6 +285,25 @@ class SettingsSerializerService
|
||||
throw new InvalidArgumentException('store.show_category_products_button must be a boolean');
|
||||
}
|
||||
|
||||
if (isset($data['product_interaction_mode']) && ! is_string($data['product_interaction_mode'])) {
|
||||
throw new InvalidArgumentException('store.product_interaction_mode must be a string');
|
||||
}
|
||||
|
||||
if (isset($data['product_interaction_mode']) && ! in_array($data['product_interaction_mode'], ['order', 'manager', 'browser'], true)) {
|
||||
throw new InvalidArgumentException('store.product_interaction_mode must be one of: order, manager, browser');
|
||||
}
|
||||
|
||||
if (isset($data['manager_username']) && $data['manager_username'] !== null) {
|
||||
if (! is_string($data['manager_username'])) {
|
||||
throw new InvalidArgumentException('store.manager_username must be a string or null');
|
||||
}
|
||||
// Проверяем, что это username (не числовой ID)
|
||||
$managerUsername = trim($data['manager_username']);
|
||||
if ($managerUsername !== '' && preg_match('/^-?\d+$/', $managerUsername)) {
|
||||
throw new InvalidArgumentException('store.manager_username must be a username (e.g., @username), not a numeric ID');
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($data['oc_default_currency'])) {
|
||||
throw new InvalidArgumentException('store.oc_default_currency is required');
|
||||
}
|
||||
|
||||
0
module/oc_telegram_shop/upload/oc_telegram_shop/stubs/phpstan-bootstrap.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/stubs/phpstan-bootstrap.php
Normal file → Executable file
Reference in New Issue
Block a user