fix(main): Fixing bugs
This commit is contained in:
Binary file not shown.
@@ -55,26 +55,76 @@ not something the module can safely do on your behalf without overriding core ma
|
||||
- No drag-and-drop reordering of specialty values. The grid has "Move up" / "Move down" row
|
||||
actions instead (`customerspecialty_move_up` / `_move_down` routes swapping `position` with the
|
||||
adjacent row). This avoids depending on PrestaShop's core Grid position/drag-reorder plumbing.
|
||||
- The active/inactive toggle uses a `ToggleColumn` + `customerspecialty_toggle_active` route. If
|
||||
that route ever misbehaves on a given PrestaShop version, active/inactive can always be changed
|
||||
via the Edit form as a fallback — it isn't the only way to flip the flag.
|
||||
- The active/inactive column is display-only (`Active`/`Inactive` text via a
|
||||
`GridDataModifier` hook — `actionCustomerspecialtyGridDataModifier`). An earlier version used a
|
||||
clickable `ToggleColumn` for instant AJAX toggling. Checked against PrestaShop's real core
|
||||
source (`src/Core/Grid/Column/Type/Common/ToggleColumn.php`): the column's required options
|
||||
(`field`, `primary_field`, `route`) were all correctly set, so that wasn't the bug. The actual
|
||||
cause was on the controller side — `toggleActiveAction()` returned a full-page
|
||||
`RedirectResponse`, but the toggle's AJAX click handler expects a JSON response to update its
|
||||
own UI state, so the click likely persisted server-side with no visible feedback. Removed rather
|
||||
than fixed, since the display-only column + existing Edit form covers the same functionality
|
||||
with less surface area. Change active/inactive via the Edit form (pencil icon) instead.
|
||||
|
||||
Confirmed working live on PrestaShop 9: create, edit, and active/inactive toggle on the
|
||||
Specialties grid. Three real bugs were found and fixed this way (not by static guessing) during
|
||||
initial rollout: a wrong `FormDataProviderInterface` namespace, a missing `LinkRowAction`
|
||||
`route_param_field` option, and `FrameworkBundleAdminController::trans()` taking `(key, domain,
|
||||
params)` instead of `Module::trans()`'s `(key, params, domain)`. "Move up"/"Move down" reordering
|
||||
has not been explicitly click-tested yet — verify before relying on it.
|
||||
Confirmed working live on PrestaShop 9: create, edit, and (formerly) active/inactive toggle on the
|
||||
Specialties grid. Several real bugs were found via live errors during rollout rather than caught
|
||||
ahead of time: a wrong `FormDataProviderInterface` namespace, a missing `LinkRowAction`
|
||||
`route_param_field` option, a wrong `FormField` namespace, and `FrameworkBundleAdminController::trans()`
|
||||
taking `(key, domain, params)` instead of `Module::trans()`'s `(key, params, domain)`. "Move
|
||||
up"/"Move down" reordering hasn't been explicitly click-tested, but checked against PrestaShop's
|
||||
real `LinkRowAction` source (`src/Core/Grid/Action/Row/Type/LinkRowAction.php`): its only required
|
||||
options are `route`, `route_param_name`, `route_param_field` — the exact same three already
|
||||
confirmed working on the "Edit" row action, and "Move up"/"Move down" use the identical shape. Low
|
||||
residual risk, but still worth a click to confirm.
|
||||
|
||||
## Biggest assumption to verify
|
||||
## Bug caught by research before it hit production
|
||||
|
||||
The storefront "Specialty" field on registration/account-edit relies on `additionalCustomerFormFields`
|
||||
already being called by core's own front-office `CustomerFormatter` (passing `fields` by reference).
|
||||
The legacy module achieved this only by overriding `CustomerFormatter` to add that hook call itself —
|
||||
an override intentionally not reproduced here (see the architecture note above). If the installed
|
||||
PrestaShop version's `CustomerFormatter` does not already call this hook natively, the storefront
|
||||
field will not appear (back office, grid, and mail-variable behavior are unaffected either way,
|
||||
since none of those depend on this hook). Verify on a real 1.7 and 9.0 install; if the hook isn't
|
||||
native, the fallback is to make the field mandatory server-side via a small first-party
|
||||
`CustomerFormatter` override scoped to only adding that one `Hook::exec()` call — much smaller and
|
||||
safer than the legacy module's full-method copy.
|
||||
The `active`/`inactive` label transform used the hook name `actionCustomerspecialtyGridDataModifier`
|
||||
with `$params['presented_grid']['data']['records']` — copied directly from this module's own
|
||||
written conventions doc. Checked against real PrestaShop core source (confirmed identically on
|
||||
1.7.8.11, 8.1.6, and 9.0.0): `GridFactory::getGrid()` dispatches `action{Id}GridDataModifier` with
|
||||
`['data' => &$data]` (a raw `GridData` object, no `presented_grid` key at all) — the
|
||||
`presented_grid` array shape actually belongs to a *different* hook, `action{Id}GridPresenterModifier`,
|
||||
dispatched separately from `GridPresenter::present()`. The conventions doc has the hook name and
|
||||
parameter shape from two different hooks crossed. Our hook name has been corrected to
|
||||
`actionCustomerspecialtyGridPresenterModifier`; the body logic was already right for that shape.
|
||||
This was caught via source research before ever being deployed, unlike most other bugs in this
|
||||
module which surfaced as live fatals — worth flagging back if this doc gets used for future modules.
|
||||
|
||||
## Confirmed live on PrestaShop 9
|
||||
|
||||
`additionalCustomerFormFields` **is** called natively by core's front-office `CustomerFormatter::getFormat()`
|
||||
(confirmed via a live stack trace from the registration page) — no `CustomerFormatter` override is
|
||||
needed, as originally hoped. This was the biggest unverified assumption in the initial build; it's
|
||||
now settled.
|
||||
|
||||
## Front-office vs back-office service container gotcha (found live, now fixed)
|
||||
|
||||
The front office runs on a different, smaller Symfony container than the back office — it does
|
||||
**not** have this module's own `config/*.yml`-registered services compiled into it, only core
|
||||
services. A hook that may fire in front-office context (like `additionalCustomerFormFields`, fired
|
||||
from the registration page) must not fetch custom module services via `$this->get('websmid.*')` —
|
||||
it throws `ServiceNotFoundException` there even though the identical call works fine from
|
||||
admin-only hooks. Fixed by instantiating `CustomerSpecialtyChoiceProvider` directly (`new
|
||||
CustomerSpecialtyChoiceProvider()`) instead of routing it through the container — it has no real
|
||||
dependencies, so this works identically in both contexts. `config/provider.yml` was removed since
|
||||
the class is no longer registered as a DI service.
|
||||
|
||||
**Update, confirmed live**: even `prestashop.core.command_bus` — a *core* service, not a
|
||||
module-registered one — throws the same `ServiceNotFoundException` when called from front-office
|
||||
hooks (`actionObjectCustomerAddAfter`/`UpdateAfter`, fired from registration/account-edit). The
|
||||
front-office container is more limited than assumed; it's not just custom module services that are
|
||||
missing. Fixed by having `persistFrontOfficeCustomerSpecialty()` write directly via
|
||||
`Db::getInstance()` (an `INSERT ... ON DUPLICATE KEY UPDATE` against
|
||||
`ws_customerspecialty_customer`) instead of dispatching `LinkCustomerSpecialtyCommand` through the
|
||||
command bus. This also retroactively explains why `ws_giftdropdown`'s own front controller bypasses
|
||||
its fully-wired Command/CommandHandler/Doctrine plumbing in favor of raw SQL — same limitation,
|
||||
same workaround, already an established pattern in this codebase.
|
||||
|
||||
**General rule going forward**: any hook that can fire in front-office context
|
||||
(`additionalCustomerFormFields`, `actionObjectCustomerAddAfter`/`UpdateAfter`, and similar) must
|
||||
stick to `Db::getInstance()`/legacy globals only — no `$this->get(...)` container service, custom
|
||||
or core, and no Doctrine entity manager. Only hooks that are provably admin-only
|
||||
(`actionCustomerFormBuilderModifier`, `actionAfterCreate/UpdateCustomerFormHandler`,
|
||||
`actionCustomerGridDefinitionModifier`/`QueryBuilderModifier`) can safely use the command bus,
|
||||
Doctrine, or custom DI services.
|
||||
|
||||
@@ -11,12 +11,6 @@ services:
|
||||
- { name: 'tactician.handler', command: 'Websmid\CustomerSpecialty\Command\MoveCustomerSpecialtyCommand' }
|
||||
- { name: 'messenger.message_handler' }
|
||||
|
||||
websmid.customerspecialty.command_handler.toggle_customer_specialty_active_handler:
|
||||
class: 'Websmid\CustomerSpecialty\Command\CommandHandler\ToggleCustomerSpecialtyActiveCommandHandler'
|
||||
tags:
|
||||
- { name: 'tactician.handler', command: 'Websmid\CustomerSpecialty\Command\ToggleCustomerSpecialtyActiveCommand' }
|
||||
- { name: 'messenger.message_handler' }
|
||||
|
||||
websmid.customerspecialty.command_handler.link_customer_specialty_handler:
|
||||
class: 'Websmid\CustomerSpecialty\Command\CommandHandler\LinkCustomerSpecialtyCommandHandler'
|
||||
tags:
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
services:
|
||||
websmid.customerspecialty.form.choice_provider.customer_specialty_choice_provider:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Form\ChoiceProvider\CustomerSpecialtyChoiceProvider'
|
||||
@@ -27,13 +27,6 @@ customerspecialty_edit:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::editAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_toggle_active:
|
||||
path: customerspecialty/toggle-active/{idCustomerSpecialty}
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::toggleActiveAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_move_up:
|
||||
path: customerspecialty/move-up/{idCustomerSpecialty}
|
||||
methods: [GET]
|
||||
|
||||
@@ -2,7 +2,6 @@ imports:
|
||||
- { resource: form.yml }
|
||||
- { resource: grid.yml }
|
||||
- { resource: command.yml }
|
||||
- { resource: provider.yml }
|
||||
|
||||
services:
|
||||
websmid.customerspecialty.entity.table_prefix_subscriber:
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command\CommandHandler;
|
||||
|
||||
use Websmid\CustomerSpecialty\Command\ToggleCustomerSpecialtyActiveCommand;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialty;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShopException;
|
||||
|
||||
final class ToggleCustomerSpecialtyActiveCommandHandler
|
||||
{
|
||||
public function __invoke(ToggleCustomerSpecialtyActiveCommand $command)
|
||||
{
|
||||
return $this->handle($command);
|
||||
}
|
||||
|
||||
public function handle(ToggleCustomerSpecialtyActiveCommand $command)
|
||||
{
|
||||
$entityManager = SymfonyContainer::getInstance()->get('doctrine.orm.entity_manager');
|
||||
$repository = $entityManager->getRepository(CustomerSpecialty::class);
|
||||
|
||||
$customerSpecialty = $repository->find($command->getIdCustomerSpecialty());
|
||||
|
||||
if (!$customerSpecialty) {
|
||||
throw new PrestaShopException('Customer specialty not found.');
|
||||
}
|
||||
|
||||
$customerSpecialty->setActive(!$customerSpecialty->isActive());
|
||||
|
||||
$entityManager->persist($customerSpecialty);
|
||||
$entityManager->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command;
|
||||
|
||||
class ToggleCustomerSpecialtyActiveCommand
|
||||
{
|
||||
private $idCustomerSpecialty;
|
||||
|
||||
public function __construct(int $idCustomerSpecialty)
|
||||
{
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ class Config
|
||||
'actionCustomerGridQueryBuilderModifier',
|
||||
'displayAdminCustomers',
|
||||
'actionGetExtraMailTemplateVars',
|
||||
'actionCustomerspecialtyGridPresenterModifier',
|
||||
];
|
||||
|
||||
const TABS = [
|
||||
|
||||
@@ -7,7 +7,6 @@ use PrestaShopBundle\Security\Voter\PageVoter;
|
||||
use Websmid\CustomerSpecialty\Grid\Definition\Factory\CustomerSpecialtyGridDefinitionFactory;
|
||||
use Websmid\CustomerSpecialty\Grid\Filters\CustomerSpecialtyFilters;
|
||||
use Websmid\CustomerSpecialty\Command\MoveCustomerSpecialtyCommand;
|
||||
use Websmid\CustomerSpecialty\Command\ToggleCustomerSpecialtyActiveCommand;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
@@ -32,7 +31,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
|
||||
{
|
||||
$legacyContext = $this->get('prestashop.adapter.legacy.context')->getContext();
|
||||
|
||||
return $legacyContext->link->getAdminLink('AdminModules') . '&configure=ws_customerspecialty';
|
||||
return $legacyContext->link->getAdminLink('AdminModules') . '&configure=ws_customerspecialty&viewsettings=1';
|
||||
}
|
||||
|
||||
public function searchAction(Request $request): RedirectResponse
|
||||
@@ -95,18 +94,6 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
|
||||
]);
|
||||
}
|
||||
|
||||
public function toggleActiveAction(int $idCustomerSpecialty): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$this->getCommandBus()->handle(new ToggleCustomerSpecialtyActiveCommand($idCustomerSpecialty));
|
||||
$this->addFlash('success', $this->trans('Successful update', 'Admin.Notifications.Success'));
|
||||
} catch (Exception $e) {
|
||||
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('customerspecialty_index');
|
||||
}
|
||||
|
||||
public function moveUpAction(int $idCustomerSpecialty): RedirectResponse
|
||||
{
|
||||
return $this->move($idCustomerSpecialty, MoveCustomerSpecialtyCommand::DIRECTION_UP);
|
||||
|
||||
+1
-5
@@ -5,7 +5,6 @@ namespace Websmid\CustomerSpecialty\Grid\Definition\Factory;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\AbstractGridDefinitionFactory;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\ColumnCollection;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\DataColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ToggleColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ActionColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\Row\RowActionCollection;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\LinkRowAction;
|
||||
@@ -49,13 +48,10 @@ class CustomerSpecialtyGridDefinitionFactory extends AbstractGridDefinitionFacto
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new ToggleColumn('active'))
|
||||
(new DataColumn('active'))
|
||||
->setName($this->trans('Active', [], 'Admin.Global'))
|
||||
->setOptions([
|
||||
'field' => 'active',
|
||||
'primary_field' => 'id_ws_customerspecialty',
|
||||
'route' => 'customerspecialty_toggle_active',
|
||||
'route_param_name' => 'idCustomerSpecialty',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Websmid\CustomerSpecialty\Module;
|
||||
|
||||
use Websmid\CustomerSpecialty\Config\Config;
|
||||
use Websmid\CustomerSpecialty\Command\LinkCustomerSpecialtyCommand;
|
||||
use Websmid\CustomerSpecialty\Form\ChoiceProvider\CustomerSpecialtyChoiceProvider;
|
||||
use Db;
|
||||
use Configuration;
|
||||
use Tools;
|
||||
@@ -11,6 +12,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\DataColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Record\RecordCollection;
|
||||
|
||||
trait AdminHooks
|
||||
{
|
||||
@@ -26,6 +28,9 @@ trait AdminHooks
|
||||
if (Tools::isSubmit('submitWsCustomerSpecialty')) {
|
||||
Configuration::updateValue(Config::FRONT_ENABLED_CONFIG_KEY, (bool) Tools::getValue('front_enabled'));
|
||||
$confirmation = $this->displayConfirmation($this->trans('Settings saved.', [], 'Admin.Notifications.Success'));
|
||||
} elseif (!Tools::getValue('viewsettings')) {
|
||||
header('Location: ' . $this->get('router')->generate('customerspecialty_index'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->context->smarty->assign([
|
||||
@@ -113,6 +118,20 @@ trait AdminHooks
|
||||
$this->applyCustomerSpecialtyFilter($searchCriteria, $searchQueryBuilder);
|
||||
}
|
||||
|
||||
public function hookActionCustomerspecialtyGridPresenterModifier(array $params): void
|
||||
{
|
||||
$activeLabel = $this->trans('Active', [], 'Admin.Global');
|
||||
$inactiveLabel = $this->trans('Inactive', [], 'Admin.Global');
|
||||
|
||||
$params['presented_grid']['data']['records'] = new RecordCollection(
|
||||
array_map(static function (array $row) use ($activeLabel, $inactiveLabel) {
|
||||
$row['active'] = $row['active'] ? $activeLabel : $inactiveLabel;
|
||||
|
||||
return $row;
|
||||
}, $params['presented_grid']['data']['records']->all())
|
||||
);
|
||||
}
|
||||
|
||||
public function hookDisplayAdminCustomers(array $params)
|
||||
{
|
||||
$title = $this->loadCustomerSpecialtyTitle((int) $params['id_customer']);
|
||||
@@ -200,8 +219,8 @@ trait AdminHooks
|
||||
return false !== $title && null !== $title ? (string) $title : null;
|
||||
}
|
||||
|
||||
private function getCustomerSpecialtyChoiceProvider()
|
||||
private function getCustomerSpecialtyChoiceProvider(): CustomerSpecialtyChoiceProvider
|
||||
{
|
||||
return $this->get('websmid.customerspecialty.form.choice_provider.customer_specialty_choice_provider');
|
||||
return new CustomerSpecialtyChoiceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
namespace Websmid\CustomerSpecialty\Module;
|
||||
|
||||
use Websmid\CustomerSpecialty\Config\Config;
|
||||
use Websmid\CustomerSpecialty\Command\LinkCustomerSpecialtyCommand;
|
||||
use Configuration;
|
||||
use Tools;
|
||||
use Db;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormField;
|
||||
use FormField;
|
||||
|
||||
trait DisplayHooks
|
||||
{
|
||||
@@ -17,7 +16,7 @@ trait DisplayHooks
|
||||
return;
|
||||
}
|
||||
|
||||
$choices = $this->get('websmid.customerspecialty.form.choice_provider.customer_specialty_choice_provider')->getChoices();
|
||||
$choices = $this->getCustomerSpecialtyChoiceProvider()->getChoices();
|
||||
|
||||
if (empty($choices)) {
|
||||
return;
|
||||
@@ -28,7 +27,17 @@ trait DisplayHooks
|
||||
->setType('select')
|
||||
->setLabel($this->trans('Specialty', [], 'Modules.Wscustomerspecialty.Shop'))
|
||||
->setRequired(true)
|
||||
->setAvailableValues($choices);
|
||||
->setAvailableValues(array_flip($choices));
|
||||
|
||||
$idCustomer = (int) $this->context->customer->id;
|
||||
|
||||
if ($idCustomer > 0) {
|
||||
$currentSpecialization = $this->loadCustomerSpecialtyId($idCustomer);
|
||||
|
||||
if ($currentSpecialization > 0) {
|
||||
$specializationField->setValue($currentSpecialization);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['fields'])) {
|
||||
$this->insertFieldAfterEmail($params['fields'], $specializationField);
|
||||
@@ -86,8 +95,16 @@ trait DisplayHooks
|
||||
return;
|
||||
}
|
||||
|
||||
$this->get('prestashop.core.command_bus')->handle(
|
||||
new LinkCustomerSpecialtyCommand($idCustomer, $specialization)
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
Db::getInstance()->execute(
|
||||
'INSERT INTO `' . _DB_PREFIX_ . 'ws_customerspecialty_customer`
|
||||
(`id_customer`, `id_ws_customerspecialty`, `date_add`, `date_upd`)
|
||||
VALUES
|
||||
(' . $idCustomer . ', ' . $specialization . ', \'' . pSQL($now) . '\', \'' . pSQL($now) . '\')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`id_ws_customerspecialty` = ' . $specialization . ',
|
||||
`date_upd` = \'' . pSQL($now) . '\''
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,26 @@
|
||||
{$confirmation nofilter}
|
||||
{/if}
|
||||
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 20px;">
|
||||
<li class="active"><a href="#"><i class="icon-cogs"></i> {l s='Settings' d='Modules.Wscustomerspecialty.Admin'}</a></li>
|
||||
<li><a href="{$specialties_url|escape:'htmlall':'UTF-8'}"><i class="icon-list"></i> {l s='Specialties' d='Modules.Wscustomerspecialty.Admin'}</a></li>
|
||||
<div class="page-head-tabs" id="head_tabs">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="nav-item">
|
||||
<a href="{$specialties_url|escape:'htmlall':'UTF-8'}" class="nav-link tab">
|
||||
{l s='Specialties' d='Modules.Wscustomerspecialty.Admin'}
|
||||
<span class="notification-container">
|
||||
<span class="notification-counter"></span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link tab active current">
|
||||
{l s='Settings' d='Modules.Wscustomerspecialty.Admin'}
|
||||
<span class="notification-container">
|
||||
<span class="notification-counter"></span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
{% extends '@PrestaShop/Admin/layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 20px;">
|
||||
<li><a href="{{ settingsUrl }}"><i class="material-icons">settings</i> {{ 'Settings'|trans({}, 'Modules.Wscustomerspecialty.Admin') }}</a></li>
|
||||
<li class="active"><a href="#"><i class="material-icons">list</i> {{ 'Specialties'|trans({}, 'Modules.Wscustomerspecialty.Admin') }}</a></li>
|
||||
<div class="page-head-tabs" id="head_tabs">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link tab active current">
|
||||
{{ 'Specialties'|trans({}, 'Modules.Wscustomerspecialty.Admin') }}
|
||||
<span class="notification-container">
|
||||
<span class="notification-counter"></span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ settingsUrl }}" class="nav-link tab">
|
||||
{{ 'Settings'|trans({}, 'Modules.Wscustomerspecialty.Admin') }}
|
||||
<span class="notification-container">
|
||||
<span class="notification-counter"></span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% include '@PrestaShop/Admin/Common/Grid/grid_panel.html.twig' with {'grid': customerSpecialtyGrid} %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user