From 55eef6a285addb8d34d90daef6208d153792469d Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Tue, 31 Mar 2026 00:26:01 +0600 Subject: [PATCH] =?UTF-8?q?fix(vue):=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D1=8C=20DELETE=20=D0=BA=D0=BB=D0=B8=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D1=82=D0=B2=D0=B5=D1=80=D0=B6=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20=D0=B3=D1=80=D0=B8=D0=B4=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Убрано второе confirm в deleteCustomer: диалог useActions уже подтверждает действие; вложенный ConfirmDialog блокировал запрос. Дополнено: лексикон {id}, seed конфиг колонки actions, whitelist handlers в GridConfigService (addresses, publish, duplicate). Fixes #178 --- .../minishop3/lexicon/en/vue.inc.php | 2 +- .../minishop3/lexicon/ru/vue.inc.php | 2 +- ...51127000002_seed_customers_grid_config.php | 33 ++++- .../src/Services/GridConfigService.php | 4 +- vueManager/src/components/ActionsColumn.vue | 3 + vueManager/src/components/CustomersGrid.vue | 116 +++++++++--------- 6 files changed, 93 insertions(+), 67 deletions(-) diff --git a/core/components/minishop3/lexicon/en/vue.inc.php b/core/components/minishop3/lexicon/en/vue.inc.php index 80468f0e..59131aa2 100644 --- a/core/components/minishop3/lexicon/en/vue.inc.php +++ b/core/components/minishop3/lexicon/en/vue.inc.php @@ -294,7 +294,7 @@ $_lang['customer_updated'] = 'Customer updated successfully'; $_lang['customer_deleted'] = 'Customer deleted successfully'; $_lang['customer_delete_confirm_title'] = 'Confirm Deletion'; -$_lang['customer_delete_confirm_message'] = 'Are you sure you want to delete customer #{id}?'; +$_lang['customer_delete_confirm_message'] = 'Are you sure you want to delete the customer with ID {id}?'; $_lang['customer_new_password'] = 'New Password'; $_lang['customer_password_placeholder'] = 'Leave empty to keep current password'; $_lang['customer_password_hint'] = 'Enter a new password or generate one automatically. Leave empty to keep the current password.'; diff --git a/core/components/minishop3/lexicon/ru/vue.inc.php b/core/components/minishop3/lexicon/ru/vue.inc.php index 11e83803..a9df0b06 100644 --- a/core/components/minishop3/lexicon/ru/vue.inc.php +++ b/core/components/minishop3/lexicon/ru/vue.inc.php @@ -328,7 +328,7 @@ $_lang['customer_updated'] = 'Клиент успешно обновлен'; $_lang['customer_deleted'] = 'Клиент успешно удален'; $_lang['customer_delete_confirm_title'] = 'Подтверждение удаления'; -$_lang['customer_delete_confirm_message'] = 'Вы уверены, что хотите удалить клиента #{id}?'; +$_lang['customer_delete_confirm_message'] = 'Вы уверены, что хотите удалить клиента с идентификатором {id}?'; $_lang['customer_new_password'] = 'Новый пароль'; $_lang['customer_password_placeholder'] = 'Оставьте пустым, чтобы не менять'; $_lang['customer_password_hint'] = 'Введите новый пароль или сгенерируйте автоматически. Оставьте пустым, если не хотите менять пароль.'; diff --git a/core/components/minishop3/migrations/20251127000002_seed_customers_grid_config.php b/core/components/minishop3/migrations/20251127000002_seed_customers_grid_config.php index 022f6c39..d24bd729 100644 --- a/core/components/minishop3/migrations/20251127000002_seed_customers_grid_config.php +++ b/core/components/minishop3/migrations/20251127000002_seed_customers_grid_config.php @@ -41,7 +41,10 @@ public function up() 'frozen' => 0, 'width' => null, 'min_width' => '200px', - 'config' => json_encode(['type' => 'template', 'template' => '{first_name} {last_name}'], JSON_UNESCAPED_UNICODE), + 'config' => json_encode( + ['type' => 'template', 'template' => '{first_name} {last_name}'], + JSON_UNESCAPED_UNICODE + ), 'is_system' => 0, 'is_default' => 1, ], @@ -129,10 +132,30 @@ public function up() 'config' => json_encode([ 'type' => 'actions', 'actions' => [ - ['name' => 'addresses', 'handler' => 'addresses', 'icon' => 'pi-map-marker', 'label' => 'addresses'], - ['name' => 'edit', 'handler' => 'edit', 'icon' => 'pi-pencil', 'label' => 'edit'], - ['name' => 'delete', 'handler' => 'delete', 'icon' => 'pi-trash', 'label' => 'delete', 'severity' => 'danger', 'confirm' => true, 'confirmMessage' => 'customer_delete_confirm_message'] - ] + [ + 'name' => 'addresses', + 'handler' => 'addresses', + 'icon' => 'pi-map-marker', + 'label' => 'addresses', + ], + [ + 'name' => 'edit', + 'handler' => 'edit', + 'icon' => 'pi-pencil', + 'label' => 'edit', + ], + [ + 'name' => 'delete', + 'handler' => 'delete', + 'icon' => 'pi-trash', + 'label' => 'delete', + 'severity' => 'danger', + 'confirm' => true, + 'confirmTitle' => 'customer_delete_confirm_title', + 'confirmMessage' => 'customer_delete_confirm_message', + 'confirmAccept' => 'delete', + ], + ], ], JSON_UNESCAPED_UNICODE), 'is_system' => 1, 'is_default' => 1, diff --git a/core/components/minishop3/src/Services/GridConfigService.php b/core/components/minishop3/src/Services/GridConfigService.php index 8fa1413a..052a635c 100644 --- a/core/components/minishop3/src/Services/GridConfigService.php +++ b/core/components/minishop3/src/Services/GridConfigService.php @@ -680,8 +680,8 @@ protected function validateActionsConfig(array $config): array return ['success' => false, 'message' => 'actions must be an array']; } - // Validate each action - $allowedHandlers = ['edit', 'delete', 'view', 'refresh']; + // Built-in Vue manager handlers (see vueManager/src/actionRegistry.js) + $allowedHandlers = ['edit', 'delete', 'view', 'refresh', 'addresses', 'publish', 'duplicate']; $actionNames = []; foreach ($actions as $index => $action) { diff --git a/vueManager/src/components/ActionsColumn.vue b/vueManager/src/components/ActionsColumn.vue index 4235fde8..ba925860 100644 --- a/vueManager/src/components/ActionsColumn.vue +++ b/vueManager/src/components/ActionsColumn.vue @@ -23,7 +23,10 @@ * label: 'edit', // Lexicon key or text * severity: null, // PrimeVue severity: danger, secondary, success, etc. * confirm: false, // Confirmation required + * confirmTitle: '...', // Confirm dialog title (lexicon key, optional) * confirmMessage: '...', // Confirmation message (lexicon key) + * confirmAccept: '...', // Accept button label (lexicon key, optional) + * confirmReject: '...', // Reject button label (lexicon key, optional) * permission: 'ms3_save', // Access permission (optional) * visible: true, // Button visibility * disabled: false, // Disabled state diff --git a/vueManager/src/components/CustomersGrid.vue b/vueManager/src/components/CustomersGrid.vue index 4f133d58..db55a5a7 100644 --- a/vueManager/src/components/CustomersGrid.vue +++ b/vueManager/src/components/CustomersGrid.vue @@ -65,6 +65,19 @@ const editingAddress = ref(null) const addressFormVisible = ref(false) const savingAddress = ref(false) +/** Delete row action: aligned with `20251127000002_seed_customers_grid_config` (lexicon keys). */ +const CUSTOMER_GRID_DELETE_ACTION = { + name: 'delete', + handler: 'delete', + icon: 'pi-trash', + label: 'delete', + severity: 'danger', + confirm: true, + confirmTitle: 'customer_delete_confirm_title', + confirmMessage: 'customer_delete_confirm_message', + confirmAccept: 'delete', +} + /** * Load customers list */ @@ -203,42 +216,29 @@ async function saveCustomer() { } /** - * Delete customer + * Delete customer (called after confirmation in ActionsColumn / useActions) */ -function deleteCustomer(customer) { - confirm.require({ - message: _('customer_delete_confirm_message').replace( - '{name}', - getCustomerDisplayName(customer) - ), - header: _('customer_delete_confirm_title'), - icon: 'pi pi-exclamation-triangle', - acceptLabel: _('delete'), - rejectLabel: _('cancel'), - acceptClass: 'p-button-danger', - accept: async () => { - try { - await request.delete(`/api/mgr/customers/${customer.id}`) +async function deleteCustomer(customer) { + try { + await request.delete(`/api/mgr/customers/${customer.id}`) - toast.add({ - severity: 'success', - summary: _('success'), - detail: _('customer_deleted'), - life: 3000, - }) + toast.add({ + severity: 'success', + summary: _('success'), + detail: _('customer_deleted'), + life: 3000, + }) - await loadCustomers() - } catch (error) { - console.error('[CustomersGrid] Error deleting customer:', error) - toast.add({ - severity: 'error', - summary: _('error'), - detail: error.message || _('error_deleting_data'), - life: 5000, - }) - } - }, - }) + await loadCustomers() + } catch (error) { + console.error('[CustomersGrid] Error deleting customer:', error) + toast.add({ + severity: 'error', + summary: _('error'), + detail: error.message || _('error_deleting_data'), + life: 5000, + }) + } } /** @@ -583,40 +583,40 @@ function getDefaultColumns() { actions: [ { name: 'addresses', handler: 'addresses', icon: 'pi-map-marker', label: 'addresses' }, { name: 'edit', handler: 'edit', icon: 'pi-pencil', label: 'edit' }, - { - name: 'delete', - handler: 'delete', - icon: 'pi-trash', - label: 'delete', - severity: 'danger', - confirm: true, - confirmMessage: 'customer_delete_confirm_message', - }, + { ...CUSTOMER_GRID_DELETE_ACTION }, ], }, ] } +/** + * Ensure delete action uses customer-specific confirm copy (covers API-loaded grid config). + */ +function applyCustomerDeleteConfirmDefaults(actions) { + return actions.map(action => { + const handler = action.handler || action.name + if (handler !== 'delete') return action + return { + ...action, + confirmTitle: action.confirmTitle || 'customer_delete_confirm_title', + confirmMessage: action.confirmMessage || 'customer_delete_confirm_message', + confirmAccept: action.confirmAccept || 'delete', + } + }) +} + /** * Get action configuration for column */ function getActionsConfig(column) { - if (!column.actions || column.actions.length === 0) { - return [ - { name: 'addresses', handler: 'addresses', icon: 'pi-map-marker', label: 'addresses' }, - { name: 'edit', handler: 'edit', icon: 'pi-pencil', label: 'edit' }, - { - name: 'delete', - handler: 'delete', - icon: 'pi-trash', - label: 'delete', - severity: 'danger', - confirm: true, - confirmMessage: 'customer_delete_confirm_message', - }, - ] - } - return column.actions + const fallback = [ + { name: 'addresses', handler: 'addresses', icon: 'pi-map-marker', label: 'addresses' }, + { name: 'edit', handler: 'edit', icon: 'pi-pencil', label: 'edit' }, + { ...CUSTOMER_GRID_DELETE_ACTION }, + ] + const raw = + !column.actions || column.actions.length === 0 ? fallback : column.actions + return applyCustomerDeleteConfirmDefaults(raw) } /**