diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5869cffd..74b9019c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,6 +78,7 @@
- **Некорректные URL в ЛК заказов клиента (#141):** кнопка «Сбросить» фильтра и ссылки пагинации вели на корень сайта (`/?`) из-за `` в шаблоне MODX — URL формируются server-side через `makeUrl()` + `http_build_query()`, пагинация сохраняет фильтр по статусу. Добавлены недостающие SVG-иконки в спрайт (`icon-arrow-left`, `icon-truck`, `icon-credit-card`, `icon-message`), исправлен `fill` → `stroke` для Feather-иконок в деталях заказа
- **Некорректные URL и хардкод английских сообщений в ЛК адресов (#142):** кнопки «Добавить адрес», «Редактировать», «Отмена» вели на корень сайта — URL формируются server-side через `makeUrl()` + `http_build_query()`. API-контроллер `CustomerAddressController` содержал хардкод английских строк вместо лексиконов — все сообщения заменены на `$this->modx->lexicon()`, добавлено 7 ключей в ru/en лексиконы
- **Отсутствие ключа `desc` в свойствах сниппетов и источников при сборке пакета (#127):** добавлена инициализация `desc` пустой строкой если ключ отсутствует в определении свойства — устраняет warning/ошибку в `resolver_04_sources` и `resolver_08_snippet_properties`
+- **Ссылка на товар в таблице категории открывалась в новой вкладке:** у шаблона колонки `pagetitle` убран `target="_blank"` в дефолтном конфиге Vue, в сидере грида и миграцией для уже сохранённых записей `ms3_grid_fields`
#### ⚠️ Breaking changes
diff --git a/core/components/minishop3/migrations/20251231140000_seed_category_products_grid_config.php b/core/components/minishop3/migrations/20251231140000_seed_category_products_grid_config.php
index 6cce055d..3ae3c2f8 100644
--- a/core/components/minishop3/migrations/20251231140000_seed_category_products_grid_config.php
+++ b/core/components/minishop3/migrations/20251231140000_seed_category_products_grid_config.php
@@ -60,7 +60,7 @@ public function up()
'min_width' => '200px',
'config' => json_encode([
'type' => 'template',
- 'template' => '({id}) {pagetitle}'
+ 'template' => '({id}) {pagetitle}'
], JSON_UNESCAPED_UNICODE),
'is_system' => 0,
'is_default' => 1,
diff --git a/core/components/minishop3/migrations/20260328120000_category_products_pagetitle_link_same_tab.php b/core/components/minishop3/migrations/20260328120000_category_products_pagetitle_link_same_tab.php
new file mode 100644
index 00000000..ac1b8c05
--- /dev/null
+++ b/core/components/minishop3/migrations/20260328120000_category_products_pagetitle_link_same_tab.php
@@ -0,0 +1,102 @@
+gridFieldsTable();
+ $stmt = $this->getAdapter()->getConnection()->prepare(
+ "UPDATE `{$table}` SET config = ? WHERE id = ?"
+ );
+
+ foreach ($this->fetchPagetitleFieldRows() as $row) {
+ $config = json_decode($row['config'], true);
+ if (!is_array($config) || empty($config['template']) || !is_string($config['template'])) {
+ continue;
+ }
+ $template = $config['template'];
+ if (!$this->templateHasBlankTarget($template)) {
+ continue;
+ }
+ $config['template'] = $this->stripBlankTargetFromTemplate($template);
+ $stmt->execute([json_encode($config, JSON_UNESCAPED_UNICODE), (int) $row['id']]);
+ }
+ }
+
+ public function down(): void
+ {
+ $table = $this->gridFieldsTable();
+ $stmt = $this->getAdapter()->getConnection()->prepare(
+ "UPDATE `{$table}` SET config = ? WHERE id = ?"
+ );
+
+ foreach ($this->fetchPagetitleFieldRows() as $row) {
+ $config = json_decode($row['config'], true);
+ if (!is_array($config) || empty($config['template']) || !is_string($config['template'])) {
+ continue;
+ }
+ $template = $config['template'];
+ if ($this->templateHasBlankTarget($template)) {
+ continue;
+ }
+ $restored = $this->restoreBlankTargetBeforeProductLinkClass($template);
+ if ($restored === null) {
+ continue;
+ }
+ $config['template'] = $restored;
+ $stmt->execute([json_encode($config, JSON_UNESCAPED_UNICODE), (int) $row['id']]);
+ }
+ }
+
+ private function gridFieldsTable(): string
+ {
+ $prefix = $this->getAdapter()->getOption('table_prefix');
+
+ return $prefix . 'ms3_grid_fields';
+ }
+
+ /**
+ * @return list
+ */
+ private function fetchPagetitleFieldRows(): array
+ {
+ $table = $this->gridFieldsTable();
+
+ return $this->fetchAll(
+ "SELECT id, config FROM `{$table}` WHERE grid_key = 'category-products' AND field_name = 'pagetitle'"
+ );
+ }
+
+ private function templateHasBlankTarget(string $template): bool
+ {
+ return str_contains($template, 'target="_blank"')
+ || str_contains($template, "target='_blank'");
+ }
+
+ private function stripBlankTargetFromTemplate(string $template): string
+ {
+ return str_replace([' target="_blank"', " target='_blank'"], '', $template);
+ }
+
+ /**
+ * Re-inserts target="_blank" only for templates that use class product-link (stock layout).
+ */
+ private function restoreBlankTargetBeforeProductLinkClass(string $template): ?string
+ {
+ if (str_contains($template, 'class="product-link"')) {
+ return str_replace(' class="product-link"', ' target="_blank" class="product-link"', $template);
+ }
+ if (str_contains($template, "class='product-link'")) {
+ return str_replace(" class='product-link'", " target='_blank' class='product-link'", $template);
+ }
+
+ return null;
+ }
+}
diff --git a/vueManager/src/components/CategoryProductsGrid.vue b/vueManager/src/components/CategoryProductsGrid.vue
index 2e2b213c..e765408c 100644
--- a/vueManager/src/components/CategoryProductsGrid.vue
+++ b/vueManager/src/components/CategoryProductsGrid.vue
@@ -540,7 +540,7 @@ function getDefaultColumns() {
minWidth: '12.5rem',
type: 'template',
template:
- '({id}) {pagetitle}',
+ '({id}) {pagetitle}',
},
{
name: 'article',