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
- Back office: a **Customer Specialties** admin screen (under Customers) lets you maintain the
list of selectable specialty values (title per language, position, active/inactive). Values
are never hard-deleted from the UI, only deactivated, since they may already be linked to
customers.
- Back office: clicking **Configure** on the module (Modules list) opens a "Settings" panel with
the front-office display toggle, and a small tab-style nav that also links to a **Specialties**
grid for maintaining the list of selectable specialty values (title per language,
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
native Customers grid gets a sortable/filterable "Specialty" column and a read-only specialty
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
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,
whose exact API wasn't verified live against a running 1.7 and 9.0 instance.
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.
- Grid `getFilters()`/`getColumns()` option keys (`ToggleColumn`, `SearchAndResetType`) follow the
standard, long-stable PrestaShop Grid API from memory; they were not verified against a live
PrestaShop 1.7 or 9.0 install (per request, devdocs research was skipped for this build). Smoke
test the "Customer Specialties" admin grid after install before relying on it in production.
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.
## Biggest assumption to verify
+1 -1
View File
@@ -20,7 +20,7 @@ class Config
const TABS = [
[
'class_name' => 'AdminCustomerSpecialtyOverview',
'parent_class_name' => 'AdminParentCustomer',
'id_parent' => -1,
'name' => [
'en' => 'Customer Specialties',
'nl' => 'Klantspecialisaties',
@@ -24,9 +24,17 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
return $this->render('@Modules/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig', [
'customerSpecialtyGrid' => $this->presentGrid($grid),
'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
{
return $this->get('prestashop.bundle.grid.response_builder')->buildSearchResponse(
@@ -49,7 +57,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
$handlerResult = $formHandler->handle($form);
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');
}
@@ -74,7 +82,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
$handlerResult = $formHandler->handleFor($idCustomerSpecialty, $form);
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');
}
@@ -91,7 +99,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
{
try {
$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) {
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
}
@@ -130,7 +138,7 @@ class CustomerSpecialtyController extends FrameworkBundleAdminController
$toolbarButtons['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',
'class' => 'btn-primary',
'floating_class' => '',
@@ -32,6 +32,7 @@ trait AdminHooks
'front_enabled' => (bool) Configuration::get(Config::FRONT_ENABLED_CONFIG_KEY),
'form_action' => $this->context->link->getAdminLink('AdminModules') . '&configure=' . $this->name,
'confirmation' => $confirmation,
'specialties_url' => $this->get('router')->generate('customerspecialty_index'),
]);
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);
foreach ($moduleTabs as $moduleTab) {
if (Tab::getIdFromClassName($moduleTab['class_name'])) {
continue;
}
$idTab = (int) Tab::getIdFromClassName($moduleTab['class_name']);
$tab = $idTab ? new Tab($idTab) : new Tab();
$tab = new Tab();
$tab->class_name = $moduleTab['class_name'];
$tab->module = $moduleTab['module'];
$tab->active = (int) $moduleTab['active'];
$tab->route_name = $moduleTab['route_name'];
$tab->icon = $moduleTab['icon'];
$tab->id_parent = (int) Tab::getIdFromClassName($moduleTab['parent_class_name']);
$tab->id_parent = $this->resolveTabParentId($moduleTab);
foreach ($languages as $language) {
$tab->name[(int) $language['id_lang']] = $moduleTab['name'][$language['iso_code']]
?? $moduleTab['name']['en'];
}
$tab->add();
if (!$tab->id) {
if (!$tab->save()) {
return false;
}
}
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}
{/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-heading">
<i class="icon-user"></i>
@@ -1,6 +1,11 @@
{% 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>
</ul>
{% include '@PrestaShop/Admin/Common/Grid/grid_panel.html.twig' with {'grid': customerSpecialtyGrid} %}
{% endblock %}