Improved back office config with tabs

This commit is contained in:
2026-07-10 10:46:45 +02:00
parent 71a0efe445
commit 1c7f2f263c
8 changed files with 57 additions and 28 deletions
Binary file not shown.
+15 -10
View File
@@ -5,10 +5,13 @@ accounts.
## What it does ## What it does
- Back office: a **Customer Specialties** admin screen (under Customers) lets you maintain the - Back office: clicking **Configure** on the module (Modules list) opens a "Settings" panel with
list of selectable specialty values (title per language, position, active/inactive). Values the front-office display toggle, and a small tab-style nav that also links to a **Specialties**
are never hard-deleted from the UI, only deactivated, since they may already be linked to grid for maintaining the list of selectable specialty values (title per language,
customers. active/inactive, move up/down). Values are never hard-deleted from the UI, only deactivated,
since they may already be linked to customers. The Specialties admin page is intentionally
**not** registered in the back-office sidebar menu (`id_parent => -1` on its tab, per this
module's own conventions doc, section 18) — Configure is the only entry point, by design.
- Back office: the native "Add/Edit customer" page gets a required "Specialty" dropdown, and the - Back office: the native "Add/Edit customer" page gets a required "Specialty" dropdown, and the
native Customers grid gets a sortable/filterable "Specialty" column and a read-only specialty native Customers grid gets a sortable/filterable "Specialty" column and a read-only specialty
block on the customer view page. block on the customer view page.
@@ -51,15 +54,17 @@ 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 - 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 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, adjacent row). This avoids depending on PrestaShop's core Grid position/drag-reorder plumbing.
whose exact API wasn't verified live against a running 1.7 and 9.0 instance.
- The active/inactive toggle uses a `ToggleColumn` + `customerspecialty_toggle_active` route. If - 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 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. via the Edit form as a fallback — it isn't the only way to flip the flag.
- Grid `getFilters()`/`getColumns()` option keys (`ToggleColumn`, `SearchAndResetType`) follow the
standard, long-stable PrestaShop Grid API from memory; they were not verified against a live Confirmed working live on PrestaShop 9: create, edit, and active/inactive toggle on the
PrestaShop 1.7 or 9.0 install (per request, devdocs research was skipped for this build). Smoke Specialties grid. Three real bugs were found and fixed this way (not by static guessing) during
test the "Customer Specialties" admin grid after install before relying on it in production. 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.
## Biggest assumption to verify ## Biggest assumption to verify
+1 -1
View File
@@ -20,7 +20,7 @@ class Config
const TABS = [ const TABS = [
[ [
'class_name' => 'AdminCustomerSpecialtyOverview', 'class_name' => 'AdminCustomerSpecialtyOverview',
'parent_class_name' => 'AdminParentCustomer', 'id_parent' => -1,
'name' => [ 'name' => [
'en' => 'Customer Specialties', 'en' => 'Customer Specialties',
'nl' => 'Klantspecialisaties', 'nl' => 'Klantspecialisaties',
@@ -24,9 +24,17 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
return $this->render('@Modules/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig', [ return $this->render('@Modules/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig', [
'customerSpecialtyGrid' => $this->presentGrid($grid), 'customerSpecialtyGrid' => $this->presentGrid($grid),
'layoutHeaderToolbarBtn' => $this->getToolbarButtons($request->get('_legacy_controller')), 'layoutHeaderToolbarBtn' => $this->getToolbarButtons($request->get('_legacy_controller')),
'settingsUrl' => $this->getSettingsUrl(),
]); ]);
} }
private function getSettingsUrl(): string
{
$legacyContext = $this->get('prestashop.adapter.legacy.context')->getContext();
return $legacyContext->link->getAdminLink('AdminModules') . '&configure=ws_customerspecialty';
}
public function searchAction(Request $request): RedirectResponse public function searchAction(Request $request): RedirectResponse
{ {
return $this->get('prestashop.bundle.grid.response_builder')->buildSearchResponse( return $this->get('prestashop.bundle.grid.response_builder')->buildSearchResponse(
@@ -49,7 +57,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
$handlerResult = $formHandler->handle($form); $handlerResult = $formHandler->handle($form);
if (null !== $handlerResult->getIdentifiableObjectId()) { if (null !== $handlerResult->getIdentifiableObjectId()) {
$this->addFlash('success', $this->trans('Successful creation', [], 'Admin.Notifications.Success')); $this->addFlash('success', $this->trans('Successful creation', 'Admin.Notifications.Success'));
return $this->redirectToRoute('customerspecialty_index'); return $this->redirectToRoute('customerspecialty_index');
} }
@@ -74,7 +82,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
$handlerResult = $formHandler->handleFor($idCustomerSpecialty, $form); $handlerResult = $formHandler->handleFor($idCustomerSpecialty, $form);
if ($handlerResult->isSubmitted() && $handlerResult->isValid()) { if ($handlerResult->isSubmitted() && $handlerResult->isValid()) {
$this->addFlash('success', $this->trans('Successful update', [], 'Admin.Notifications.Success')); $this->addFlash('success', $this->trans('Successful update', 'Admin.Notifications.Success'));
return $this->redirectToRoute('customerspecialty_index'); return $this->redirectToRoute('customerspecialty_index');
} }
@@ -91,7 +99,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
{ {
try { try {
$this->getCommandBus()->handle(new ToggleCustomerSpecialtyActiveCommand($idCustomerSpecialty)); $this->getCommandBus()->handle(new ToggleCustomerSpecialtyActiveCommand($idCustomerSpecialty));
$this->addFlash('success', $this->trans('Successful update', [], 'Admin.Notifications.Success')); $this->addFlash('success', $this->trans('Successful update', 'Admin.Notifications.Success'));
} catch (Exception $e) { } catch (Exception $e) {
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages())); $this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
} }
@@ -130,7 +138,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
$toolbarButtons['customerspecialty_create'] = [ $toolbarButtons['customerspecialty_create'] = [
'href' => $this->generateUrl('customerspecialty_create'), 'href' => $this->generateUrl('customerspecialty_create'),
'desc' => $this->trans('New specialty', [], 'Admin.Actions'), 'desc' => $this->trans('New specialty', 'Admin.Actions'),
'icon' => 'add_circle_outline', 'icon' => 'add_circle_outline',
'class' => 'btn-primary', 'class' => 'btn-primary',
'floating_class' => '', 'floating_class' => '',
@@ -32,6 +32,7 @@ trait AdminHooks
'front_enabled' => (bool) Configuration::get(Config::FRONT_ENABLED_CONFIG_KEY), 'front_enabled' => (bool) Configuration::get(Config::FRONT_ENABLED_CONFIG_KEY),
'form_action' => $this->context->link->getAdminLink('AdminModules') . '&configure=' . $this->name, 'form_action' => $this->context->link->getAdminLink('AdminModules') . '&configure=' . $this->name,
'confirmation' => $confirmation, 'confirmation' => $confirmation,
'specialties_url' => $this->get('router')->generate('customerspecialty_index'),
]); ]);
return $this->fetch('module:ws_customerspecialty/views/templates/admin/configuration.tpl'); return $this->fetch('module:ws_customerspecialty/views/templates/admin/configuration.tpl');
+13 -8
View File
@@ -95,30 +95,35 @@ trait Install
$languages = Language::getLanguages(false); $languages = Language::getLanguages(false);
foreach ($moduleTabs as $moduleTab) { foreach ($moduleTabs as $moduleTab) {
if (Tab::getIdFromClassName($moduleTab['class_name'])) { $idTab = (int) Tab::getIdFromClassName($moduleTab['class_name']);
continue; $tab = $idTab ? new Tab($idTab) : new Tab();
}
$tab = new Tab();
$tab->class_name = $moduleTab['class_name']; $tab->class_name = $moduleTab['class_name'];
$tab->module = $moduleTab['module']; $tab->module = $moduleTab['module'];
$tab->active = (int) $moduleTab['active']; $tab->active = (int) $moduleTab['active'];
$tab->route_name = $moduleTab['route_name']; $tab->route_name = $moduleTab['route_name'];
$tab->icon = $moduleTab['icon']; $tab->icon = $moduleTab['icon'];
$tab->id_parent = (int) Tab::getIdFromClassName($moduleTab['parent_class_name']); $tab->id_parent = $this->resolveTabParentId($moduleTab);
foreach ($languages as $language) { foreach ($languages as $language) {
$tab->name[(int) $language['id_lang']] = $moduleTab['name'][$language['iso_code']] $tab->name[(int) $language['id_lang']] = $moduleTab['name'][$language['iso_code']]
?? $moduleTab['name']['en']; ?? $moduleTab['name']['en'];
} }
$tab->add(); if (!$tab->save()) {
if (!$tab->id) {
return false; return false;
} }
} }
return true; return true;
} }
private function resolveTabParentId(array $moduleTab): int
{
if (array_key_exists('id_parent', $moduleTab)) {
return (int) $moduleTab['id_parent'];
}
return (int) Tab::getIdFromClassName($moduleTab['parent_class_name']);
}
} }
@@ -2,6 +2,11 @@
{$confirmation nofilter} {$confirmation nofilter}
{/if} {/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>
</ul>
<div class="panel"> <div class="panel">
<div class="panel-heading"> <div class="panel-heading">
<i class="icon-user"></i> <i class="icon-user"></i>
@@ -1,6 +1,11 @@
{% extends '@PrestaShop/Admin/layout.html.twig' %} {% extends '@PrestaShop/Admin/layout.html.twig' %}
{% block content %} {% 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>
</ul>
{% include '@PrestaShop/Admin/Common/Grid/grid_panel.html.twig' with {'grid': customerSpecialtyGrid} %} {% include '@PrestaShop/Admin/Common/Grid/grid_panel.html.twig' with {'grid': customerSpecialtyGrid} %}
{% endblock %} {% endblock %}