From 1c7f2f263c186116254ed76fe6694acf7c5a25b0 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Fri, 10 Jul 2026 10:46:45 +0200 Subject: [PATCH] Improved back office config with tabs --- ws_customerspecialty.zip | Bin 54341 -> 55021 bytes ws_customerspecialty/README.md | 25 +++++++++++------- ws_customerspecialty/src/Config/Config.php | 6 ++--- .../CustomerSpecialtyController.php | 16 ++++++++--- .../src/Module/AdminHooks.php | 7 ++--- ws_customerspecialty/src/Module/Install.php | 21 +++++++++------ .../views/templates/admin/configuration.tpl | 5 ++++ .../admin/grid/customerspecialty.html.twig | 5 ++++ 8 files changed, 57 insertions(+), 28 deletions(-) diff --git a/ws_customerspecialty.zip b/ws_customerspecialty.zip index 00a15ee6d90d24313dfed2114af595c2195ecf18..cd2c5aaa9639227fa380c55ad28e5dae8baaac2c 100644 GIT binary patch delta 10937 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 diff --git a/ws_customerspecialty/README.md b/ws_customerspecialty/README.md index 61c9f49..143aa31 100644 --- a/ws_customerspecialty/README.md +++ b/ws_customerspecialty/README.md @@ -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 diff --git a/ws_customerspecialty/src/Config/Config.php b/ws_customerspecialty/src/Config/Config.php index 8dc5dc1..d980eaf 100644 --- a/ws_customerspecialty/src/Config/Config.php +++ b/ws_customerspecialty/src/Config/Config.php @@ -19,9 +19,9 @@ class Config const TABS = [ [ - 'class_name' => 'AdminCustomerSpecialtyOverview', - 'parent_class_name' => 'AdminParentCustomer', - 'name' => [ + 'class_name' => 'AdminCustomerSpecialtyOverview', + 'id_parent' => -1, + 'name' => [ 'en' => 'Customer Specialties', 'nl' => 'Klantspecialisaties', ], diff --git a/ws_customerspecialty/src/Controller/CustomerSpecialtyController.php b/ws_customerspecialty/src/Controller/CustomerSpecialtyController.php index e85a17d..ed78d80 100644 --- a/ws_customerspecialty/src/Controller/CustomerSpecialtyController.php +++ b/ws_customerspecialty/src/Controller/CustomerSpecialtyController.php @@ -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' => '', diff --git a/ws_customerspecialty/src/Module/AdminHooks.php b/ws_customerspecialty/src/Module/AdminHooks.php index a260795..4b2b34c 100644 --- a/ws_customerspecialty/src/Module/AdminHooks.php +++ b/ws_customerspecialty/src/Module/AdminHooks.php @@ -29,9 +29,10 @@ trait AdminHooks } $this->context->smarty->assign([ - 'front_enabled' => (bool) Configuration::get(Config::FRONT_ENABLED_CONFIG_KEY), - 'form_action' => $this->context->link->getAdminLink('AdminModules') . '&configure=' . $this->name, - 'confirmation' => $confirmation, + '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'); diff --git a/ws_customerspecialty/src/Module/Install.php b/ws_customerspecialty/src/Module/Install.php index 3880b0e..080efc0 100644 --- a/ws_customerspecialty/src/Module/Install.php +++ b/ws_customerspecialty/src/Module/Install.php @@ -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']); + } } diff --git a/ws_customerspecialty/views/templates/admin/configuration.tpl b/ws_customerspecialty/views/templates/admin/configuration.tpl index 9a86d44..8cae1db 100644 --- a/ws_customerspecialty/views/templates/admin/configuration.tpl +++ b/ws_customerspecialty/views/templates/admin/configuration.tpl @@ -2,6 +2,11 @@ {$confirmation nofilter} {/if} + +
diff --git a/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig b/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig index 2f4d3cc..2e66da1 100644 --- a/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig +++ b/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig @@ -1,6 +1,11 @@ {% extends '@PrestaShop/Admin/layout.html.twig' %} {% block content %} + + {% include '@PrestaShop/Admin/Common/Grid/grid_panel.html.twig' with {'grid': customerSpecialtyGrid} %} {% endblock %}