feat(helpdesk): add Domains list page with BC OData contract type enrichment

Add a new "Domains" page to the helpdesk module that fetches domain data
from the FS_Contract_Domains OData endpoint and enriches each domain with
its contract type (PI_Header_Type) by joining with FS_Contract_Lines_Test
where Type = 'Domain'.

New files:
- domains/index().php, index(default).phtml, filter-schema.php — list page
- domains-data().php — data endpoint with PHP-side filtering/sorting
- helpdesk-domains-index.js — Grid.js via initStandardListPage()

Gateway additions:
- listDomains() — fetch all contract domains
- listDomainContractLines() — fetch domain-type contract lines for type lookup

Sidebar restructured into three collapsible groups (Lookup, Monitoring,
Administration) with per-group icons and color coding, matching the core
admin panel pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 21:34:32 +02:00
parent 3f0db68b27
commit d472026df4
11 changed files with 586 additions and 38 deletions

View File

@@ -345,5 +345,16 @@
"Upcoming meetings": "Kommende Termine",
"Past meetings": "Vergangene Termine",
"No meetings found for this customer.": "Keine Termine für diesen Kunden gefunden.",
"Could not load meetings.": "Termine konnten nicht geladen werden."
"Could not load meetings.": "Termine konnten nicht geladen werden.",
"Domains": "Domains",
"Search domains...": "Domains suchen...",
"Customer No.": "Kunden-Nr.",
"Customer Name": "Kundenname",
"Administration": "Verwaltung",
"No domains found": "Keine Domains gefunden",
"Could not load domains.": "Domains konnten nicht geladen werden.",
"Lookup": "Nachschlagen",
"Monitoring": "Monitoring",
"Contract type": "Vertragsart",
"Customer": "Kunde"
}

View File

@@ -345,5 +345,16 @@
"Upcoming meetings": "Upcoming meetings",
"Past meetings": "Past meetings",
"No meetings found for this customer.": "No meetings found for this customer.",
"Could not load meetings.": "Could not load meetings."
"Could not load meetings.": "Could not load meetings.",
"Domains": "Domains",
"Search domains...": "Search domains...",
"Customer No.": "Customer No.",
"Customer Name": "Customer Name",
"Administration": "Administration",
"No domains found": "No domains found",
"Could not load domains.": "Could not load domains.",
"Lookup": "Lookup",
"Monitoring": "Monitoring",
"Contract type": "Contract type",
"Customer": "Customer"
}

View File

@@ -22,6 +22,7 @@ class BcODataGateway
public const ENTITY_TICKET_LOG_LV = 'PBI_LV_SupportTicketLog';
public const ENTITY_CONTRACT_LINES = 'FS_Contract_Lines_Test';
public const ENTITY_MEETINGS = 'FS_Debitor_Meetings';
public const ENTITY_CONTRACT_DOMAINS = 'FS_Contract_Domains';
private const CONNECT_TIMEOUT = 10;
private const REQUEST_TIMEOUT = 30;
@@ -825,6 +826,57 @@ class BcODataGateway
return $this->extractODataValues($response);
}
/**
* Get all contract domains.
*
* Fetches all domains from the FS_Contract_Domains entity.
* Filtering, sorting and pagination are handled PHP-side by the caller.
*
* @return array<int, array<string, mixed>>
*/
public function listDomains(): array
{
$select = 'No,Customer_No,Customer_Name,URL,State,Administration';
$url = $this->settingsGateway->buildEntityUrl(self::ENTITY_CONTRACT_DOMAINS)
. '?$top=5000'
. '&$select=' . rawurlencode($select)
. '&$orderby=' . rawurlencode('Customer_Name asc');
$response = $this->request('GET', $url);
if ($response === null) {
return [];
}
return $this->extractODataValues($response);
}
/**
* Get all contract lines of type "Domain".
*
* Returns contract line metadata for domain-type lines,
* keyed by DNS number (No field). Used to enrich the domain
* list with contract type information (PI_Header_Type).
*
* @return array<int, array<string, mixed>>
*/
public function listDomainContractLines(): array
{
$filter = "Type eq 'Domain'";
$select = 'No,Header_No,PI_Header_Type,PI_Header_Description,PI_Header_State,Line_State';
$url = $this->settingsGateway->buildEntityUrl(self::ENTITY_CONTRACT_LINES)
. '?$filter=' . rawurlencode($filter)
. '&$top=5000'
. '&$select=' . rawurlencode($select)
. '&$orderby=' . rawurlencode('No asc');
$response = $this->request('GET', $url);
if ($response === null) {
return [];
}
return $this->extractODataValues($response);
}
/**
* Get a single ticket by ticket number.
*

View File

@@ -15,6 +15,8 @@ return [
'routes' => [
['path' => 'helpdesk/search-data', 'target' => 'helpdesk/search-data'],
['path' => 'helpdesk/domains', 'target' => 'helpdesk/domains'],
['path' => 'helpdesk/domains-data', 'target' => 'helpdesk/domains-data'],
['path' => 'helpdesk/debitor/{id}', 'target' => 'helpdesk/debitor'],
['path' => 'helpdesk/ticket/{id}', 'target' => 'helpdesk/ticket'],
['path' => 'helpdesk/settings', 'target' => 'helpdesk/settings'],

View File

@@ -0,0 +1,157 @@
<?php
use MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy;
use MintyPHP\Module\Helpdesk\Service\BcODataGateway;
use MintyPHP\Support\Guard;
Guard::requireLogin();
Guard::requireAbilityOrForbidden(HelpdeskAuthorizationPolicy::ABILITY_ACCESS);
gridRequireGetRequest();
$filters = gridParseFiltersFromSchemaFile(__DIR__ . '/domains/filter-schema.php');
$search = trim((string) ($filters['search'] ?? ''));
$customer = trim((string) ($filters['customer'] ?? ''));
$contractType = trim((string) ($filters['contract_type'] ?? ''));
$state = trim((string) ($filters['state'] ?? ''));
$state = $state === 'all' ? '' : $state;
$administration = trim((string) ($filters['administration'] ?? ''));
$administration = $administration === 'all' ? '' : $administration;
$order = (string) ($filters['order'] ?? 'Customer_Name');
$dir = (string) ($filters['dir'] ?? 'asc');
$limit = (int) ($filters['limit'] ?? 10);
$offset = (int) ($filters['offset'] ?? 0);
$gateway = app(BcODataGateway::class);
try {
$allDomains = $gateway->listDomains();
} catch (\Throwable) {
gridJsonDataResult([], 0);
return;
}
// Build contract type lookup: DNS number → {contract_type, contract_no, contract_description}
$contractLookup = [];
try {
$contractLines = $gateway->listDomainContractLines();
foreach ($contractLines as $line) {
$dnsNo = trim((string) ($line['No'] ?? ''));
if ($dnsNo !== '' && !isset($contractLookup[$dnsNo])) {
$contractLookup[$dnsNo] = [
'contract_type' => trim((string) ($line['PI_Header_Type'] ?? '')),
'contract_no' => trim((string) ($line['Header_No'] ?? '')),
'contract_description' => trim((string) ($line['PI_Header_Description'] ?? '')),
];
}
}
} catch (\Throwable) {
// Contract enrichment is best-effort — continue without it
}
// Enrich domains with contract type before filtering (so search/sort can use it)
foreach ($allDomains as &$domain) {
$domainNo = trim((string) ($domain['No'] ?? ''));
$contract = $contractLookup[$domainNo] ?? null;
$domain['contract_type'] = $contract['contract_type'] ?? '';
$domain['contract_no'] = $contract['contract_no'] ?? '';
$domain['contract_description'] = $contract['contract_description'] ?? '';
}
unset($domain);
$filtered = $allDomains;
// Text search across Customer_No, Customer_Name, URL, No, contract_type
if ($search !== '') {
$searchLower = mb_strtolower($search);
$filtered = array_values(array_filter($filtered, static function (array $domain) use ($searchLower): bool {
$haystack = mb_strtolower(
($domain['Customer_No'] ?? '') . ' '
. ($domain['Customer_Name'] ?? '') . ' '
. ($domain['URL'] ?? '') . ' '
. ($domain['No'] ?? '') . ' '
. ($domain['contract_type'] ?? '')
);
return str_contains($haystack, $searchLower);
}));
}
// Customer filter (partial match on Customer_No + Customer_Name)
if ($customer !== '') {
$customerLower = mb_strtolower($customer);
$filtered = array_values(array_filter($filtered, static function (array $domain) use ($customerLower): bool {
$haystack = mb_strtolower(
($domain['Customer_No'] ?? '') . ' '
. ($domain['Customer_Name'] ?? '')
);
return str_contains($haystack, $customerLower);
}));
}
// Contract type filter (partial match)
if ($contractType !== '') {
$contractTypeLower = mb_strtolower($contractType);
$filtered = array_values(array_filter($filtered, static function (array $domain) use ($contractTypeLower): bool {
return str_contains(mb_strtolower((string) ($domain['contract_type'] ?? '')), $contractTypeLower);
}));
}
// State filter (exact match)
if ($state !== '') {
$filtered = array_values(array_filter($filtered, static function (array $domain) use ($state): bool {
return trim((string) ($domain['State'] ?? '')) === $state;
}));
}
// Administration filter (exact match)
if ($administration !== '') {
$filtered = array_values(array_filter($filtered, static function (array $domain) use ($administration): bool {
return trim((string) ($domain['Administration'] ?? '')) === $administration;
}));
}
// Sorting
$total = count($filtered);
usort($filtered, static function (array $a, array $b) use ($order, $dir): int {
$va = (string) ($a[$order] ?? '');
$vb = (string) ($b[$order] ?? '');
$cmp = strnatcasecmp($va, $vb);
return $dir === 'desc' ? -$cmp : $cmp;
});
// Pagination
$rows = array_slice($filtered, $offset, $limit);
// Row preparation
$stateVariantMap = [
'Aktiv' => 'success',
'In Vorbereitung' => 'warning',
];
$debitorBaseUrl = lurl('helpdesk/debitor/');
$preparedRows = [];
foreach ($rows as $domain) {
$customerNo = trim((string) ($domain['Customer_No'] ?? ''));
$domainState = (string) ($domain['State'] ?? '');
$preparedRows[] = [
'No' => (string) ($domain['No'] ?? ''),
'Customer_No' => $customerNo,
'Customer_Name' => (string) ($domain['Customer_Name'] ?? ''),
'URL' => (string) ($domain['URL'] ?? ''),
'State' => $domainState,
'state_variant' => $stateVariantMap[$domainState] ?? 'neutral',
'Administration' => (string) ($domain['Administration'] ?? ''),
'contract_type' => (string) ($domain['contract_type'] ?? ''),
'contract_no' => (string) ($domain['contract_no'] ?? ''),
'debitor_url' => $customerNo !== '' ? $debitorBaseUrl . rawurlencode($customerNo) : '',
];
}
gridJsonDataResult($preparedRows, $total);

View File

@@ -0,0 +1,76 @@
<?php
return gridFilterSchema([
'query' => [
'search' => ['type' => 'string'],
'customer' => ['type' => 'string'],
'contract_type' => ['type' => 'string'],
'state' => ['type' => 'string', 'default' => 'all'],
'administration' => ['type' => 'string', 'default' => 'all'],
'limit' => ['type' => 'int', 'default' => 10, 'min' => 1, 'max' => 100],
'offset' => ['type' => 'int', 'default' => 0, 'min' => 0],
'order' => [
'type' => 'order',
'allowed' => ['Customer_No', 'Customer_Name', 'URL', 'State', 'Administration', 'No', 'contract_type'],
'default' => 'Customer_Name',
],
'dir' => ['type' => 'dir', 'default' => 'asc'],
],
'toolbar' => [
[
'key' => 'search',
'type' => 'text',
'label' => 'Search',
'placeholder' => 'Search domains...',
'input_id' => 'helpdesk-domains-search-input',
'search' => true,
'query' => ['type' => 'string'],
],
[
'key' => 'customer',
'type' => 'text',
'label' => 'Customer',
'placeholder' => 'Customer name or no...',
'input_id' => 'helpdesk-domains-customer-filter',
'query' => ['type' => 'string'],
'label_attributes' => ['data-filter-optional' => true],
],
[
'key' => 'contract_type',
'type' => 'text',
'label' => 'Contract type',
'placeholder' => 'e.g. WEBSITE, SW&H...',
'input_id' => 'helpdesk-domains-contract-type-filter',
'query' => ['type' => 'string'],
'label_attributes' => ['data-filter-optional' => true],
],
[
'key' => 'state',
'type' => 'select',
'label' => 'State',
'input_id' => 'helpdesk-domains-state-filter',
'default' => 'all',
'normalize' => 'all_to_empty',
'allowed' => [
['id' => 'all', 'description' => 'All'],
['id' => 'Aktiv', 'description' => 'Aktiv'],
['id' => 'In Vorbereitung', 'description' => 'In Vorbereitung'],
],
'label_attributes' => ['data-filter-optional' => true],
],
[
'key' => 'administration',
'type' => 'select',
'label' => 'Administration',
'input_id' => 'helpdesk-domains-admin-filter',
'default' => 'all',
'normalize' => 'all_to_empty',
'allowed' => [
['id' => 'all', 'description' => 'All'],
['id' => 'Intern', 'description' => 'Intern'],
['id' => 'Extern', 'description' => 'Extern'],
],
'label_attributes' => ['data-filter-optional' => true],
],
],
]);

View File

@@ -0,0 +1,62 @@
<?php
use MintyPHP\Buffer;
use MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy;
use MintyPHP\Module\Helpdesk\Service\HelpdeskSettingsGateway;
use MintyPHP\Support\Guard;
Guard::requireLogin();
Guard::requireAbilityOrForbidden(HelpdeskAuthorizationPolicy::ABILITY_ACCESS);
$request = requestInput();
$query = $request->queryAll();
$filterSchema = require __DIR__ . '/filter-schema.php';
$listFilterContext = gridBuildListFilterContext($filterSchema, [
'query' => $query,
'search_keys' => ['search'],
]);
$toolbarFilterSchema = $listFilterContext['toolbarFilterSchema'];
$searchToolbarFilterSchema = $listFilterContext['searchToolbarFilterSchema'];
$drawerToolbarFilterSchema = $listFilterContext['drawerToolbarFilterSchema'];
$toolbarFilterState = $listFilterContext['toolbarFilterState'];
$clientFilterSchema = $listFilterContext['clientFilterSchema'];
$searchConfig = $listFilterContext['searchConfig'];
$schemaByKey = $listFilterContext['schemaByKey'] ?? [];
$filterChipMeta = [
'search' => [
'label' => t('Search'),
'type' => 'text',
],
'customer' => [
'label' => t('Customer'),
'type' => 'text',
],
'contract_type' => [
'label' => t('Contract type'),
'type' => 'text',
],
'state' => [
'label' => t('State'),
'type' => 'select',
'default' => (string) ($schemaByKey['state']['default'] ?? 'all'),
'options' => gridOptionMapFromAllowed((array) ($schemaByKey['state'] ?? [])),
],
'administration' => [
'label' => t('Administration'),
'type' => 'select',
'default' => (string) ($schemaByKey['administration']['default'] ?? 'all'),
'options' => gridOptionMapFromAllowed((array) ($schemaByKey['administration'] ?? [])),
],
];
$settingsGateway = app(HelpdeskSettingsGateway::class);
$isConfigured = $settingsGateway->isConfigured();
Buffer::set('title', t('Domains'));
Buffer::set('style_groups', json_encode(['helpdesk']));
$breadcrumbs = [
['label' => t('Home'), 'path' => 'admin'],
['label' => t('Helpdesk'), 'path' => 'helpdesk'],
['label' => t('Domains')],
];

View File

@@ -0,0 +1,51 @@
<?php
$toolbarFilterSchema = is_array($toolbarFilterSchema ?? null) ? $toolbarFilterSchema : [];
$searchToolbarFilterSchema = is_array($searchToolbarFilterSchema ?? null) ? $searchToolbarFilterSchema : [];
$drawerToolbarFilterSchema = is_array($drawerToolbarFilterSchema ?? null) ? $drawerToolbarFilterSchema : [];
$toolbarFilterState = is_array($toolbarFilterState ?? null) ? $toolbarFilterState : [];
$toolbarOptionSets = is_array($toolbarOptionSets ?? null) ? $toolbarOptionSets : [];
$clientFilterSchema = is_array($clientFilterSchema ?? null) ? $clientFilterSchema : [];
$searchConfig = is_array($searchConfig ?? null) ? $searchConfig : null;
$filterChipMeta = is_array($filterChipMeta ?? null) ? $filterChipMeta : [];
$isConfigured = $isConfigured ?? false;
?>
<?php require templatePath('partials/app-flash.phtml'); ?>
<?php if (!$isConfigured): ?>
<div class="notice" data-variant="warning" role="alert">
<p><?php e(t('BC connection is not configured. Please configure the connection in the settings.')); ?></p>
<a href="<?php e(lurl('helpdesk/settings')); ?>" role="button" class="secondary outline small">
<?php e(t('Open settings')); ?>
</a>
</div>
<?php endif; ?>
<?php
$listTitle = t('Domains');
require templatePath('partials/app-list-titlebar.phtml');
?>
<?php
$filterUiNamespace = 'helpdesk-domains';
require templatePath('partials/app-list-filters.phtml');
?>
<div class="app-list-table">
<div id="helpdesk-domains-grid"></div>
</div>
<script src="<?php e(assetVersion('vendor/gridjs/gridjs.umd.js')); ?>"></script>
<script type="application/json" id="page-config-helpdesk-domains"><?php gridJsonForJs([
'dataUrl' => lurl('helpdesk/domains-data'),
'gridLang' => gridLang(),
'gridSearch' => $searchConfig,
'filterSchema' => $clientFilterSchema,
'filterChipMeta' => $filterChipMeta,
'labels' => [
'no' => t('No.'),
'customerName' => t('Customer Name'),
'url' => t('URL'),
'state' => t('State'),
'administration' => t('Administration'),
'contractType' => t('Contract type'),
],
]); ?></script>
<script type="module" src="<?php e(assetVersion('modules/helpdesk/js/pages/helpdesk-domains-index.js')); ?>"></script>

View File

@@ -9,55 +9,85 @@ $canViewRiskRadar = !empty($helpdeskNav['can_view_risk_radar']);
$settingsActive = navActive('helpdesk/settings', true);
$teamActive = navActive('helpdesk/team', true);
$riskRadarActive = navActive('helpdesk/risk-radar', true);
$domainsActive = navActive('helpdesk/domains', true);
$customersActive = navActive('helpdesk', true);
if ($settingsActive['isActive'] || $teamActive['isActive'] || $riskRadarActive['isActive']) {
if ($settingsActive['isActive'] || $teamActive['isActive'] || $riskRadarActive['isActive'] || $domainsActive['isActive']) {
$customersActive = ['class' => '', 'aria' => '', 'isActive' => false];
}
$helpdeskNavItems = [
$helpdeskNavGroups = [
[
'label' => t('Customers'),
'path' => 'helpdesk',
'active' => $customersActive,
'visible' => true,
'key' => 'helpdesk-lookup',
'label' => t('Lookup'),
'icon' => 'bi-search',
'items' => [
[
'label' => t('Customers'),
'path' => 'helpdesk',
'active' => $customersActive,
'visible' => true,
],
[
'label' => t('Domains'),
'path' => 'helpdesk/domains',
'active' => $domainsActive,
'visible' => true,
],
],
],
[
'label' => t('Team workload'),
'path' => 'helpdesk/team',
'active' => $teamActive,
'visible' => $canViewTeam,
'key' => 'helpdesk-monitoring',
'label' => t('Monitoring'),
'icon' => 'bi-bar-chart-line',
'items' => [
[
'label' => t('Team workload'),
'path' => 'helpdesk/team',
'active' => $teamActive,
'visible' => $canViewTeam,
],
[
'label' => t('Risk radar'),
'path' => 'helpdesk/risk-radar',
'active' => $riskRadarActive,
'visible' => $canViewRiskRadar,
],
],
],
[
'label' => t('Risk radar'),
'path' => 'helpdesk/risk-radar',
'active' => $riskRadarActive,
'visible' => $canViewRiskRadar,
],
[
'label' => t('Settings'),
'path' => 'helpdesk/settings',
'active' => $settingsActive,
'visible' => $canManageSettings,
'key' => 'helpdesk-administration',
'label' => t('Administration'),
'icon' => 'bi-sliders',
'items' => [
[
'label' => t('Settings'),
'path' => 'helpdesk/settings',
'active' => $settingsActive,
'visible' => $canManageSettings,
],
],
],
];
$visibleItems = array_values(array_filter($helpdeskNavItems, static fn (array $item): bool => !empty($item['visible'])));
$groupIsActive = false;
foreach ($visibleItems as $item) {
$activeState = $item['active'] ?? [];
if (!empty($activeState['isActive'])) {
$groupIsActive = true;
break;
}
}
?>
<ul class="app-sidebar-admin-nav" aria-label="<?php e(t('Helpdesk')); ?>">
<?php if ($visibleItems): ?>
<ul>
<?php foreach ($helpdeskNavGroups as $group):
$visibleItems = array_values(array_filter($group['items'], static fn (array $item): bool => !empty($item['visible'])));
if (!$visibleItems) {
continue;
}
$groupIsActive = false;
foreach ($visibleItems as $item) {
if (!empty(($item['active'] ?? [])['isActive'])) {
$groupIsActive = true;
break;
}
}
?>
<li class="app-sidebar-group app-sidebar-admin-group">
<details data-details-key="helpdesk-navigation"<?php if ($groupIsActive): ?> open<?php endif; ?>>
<details data-details-key="<?php e($group['key']); ?>"<?php if ($groupIsActive): ?> open<?php endif; ?>>
<summary>
<i class="bi bi-headset"></i>
<span><?php e(t('Helpdesk')); ?></span>
<i class="bi <?php e($group['icon']); ?>"></i>
<span><?php e($group['label']); ?></span>
</summary>
<ul>
<?php foreach ($visibleItems as $item):
@@ -72,5 +102,5 @@ foreach ($visibleItems as $item) {
</ul>
</details>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>

View File

@@ -0,0 +1,85 @@
import { initStandardListPage } from '/js/core/app-grid-factory.js';
import { readPageConfig } from '/js/core/app-page-config.js';
import { warnOnce } from '/js/core/app-dom.js';
import { escapeHtml, getAppBase, withCurrentListReturn } from '/js/pages/app-list-utils.js';
const config = readPageConfig('helpdesk-domains');
if (config) {
const gridjs = window.gridjs;
if (!gridjs) {
warnOnce('UI_INIT_FAIL', 'Helpdesk domains grid init failed: Grid.js missing', { module: 'helpdesk-domains', component: 'grid' });
} else {
const appBase = getAppBase();
const labels = config.labels || {};
const debitorUrlIndex = 6;
const gridOptions = {
gridjs,
container: '#helpdesk-domains-grid',
dataUrl: config.dataUrl || 'helpdesk/domains-data',
appBase,
columns: [
{ name: labels.no || 'No.', sort: true },
{
name: labels.customerName || 'Customer Name',
sort: true,
formatter: (cell) => {
const name = escapeHtml(cell?.name || '');
const detailUrl = String(cell?.url || '').trim();
if (detailUrl === '') {
return gridjs.html(name);
}
const href = escapeHtml(withCurrentListReturn(detailUrl));
return gridjs.html(`<a href="${href}">${name}</a>`);
},
},
{ name: labels.url || 'URL', sort: true },
{ name: labels.contractType || 'Contract Type', sort: true },
{
name: labels.state || 'State',
sort: true,
formatter: (cell) => {
const state = escapeHtml(cell?.state || '');
const variant = cell?.variant || 'neutral';
return gridjs.html(`<span class="badge" data-variant="${escapeHtml(variant)}">${state}</span>`);
},
},
{ name: labels.administration || 'Administration', sort: true },
{ name: 'debitor_url', hidden: true },
],
sortColumns: ['No', 'Customer_Name', 'URL', 'contract_type', 'State', 'Administration', null],
paginationLimit: 10,
language: config.gridLang || {},
mapData: (data) => (data.data || []).map((row) => [
row.No || '',
{ name: row.Customer_Name || '', url: row.debitor_url || '' },
row.URL || '',
row.contract_type || '',
{ state: row.State || '', variant: row.state_variant || 'neutral' },
row.Administration || '',
row.debitor_url || '',
]),
search: config.gridSearch || null,
filterSchema: config.filterSchema || [],
urlSync: true,
rowInteraction: { linkColumn: 1 },
rowDblClick: {
getUrl: (rowData) => rowData?.cells?.[debitorUrlIndex]?.data || '',
},
};
const { gridConfig } = initStandardListPage({
grid: gridOptions,
filters: {
mode: 'drawer',
chipMeta: config.filterChipMeta || {},
watchInputs: ['#helpdesk-domains-search-input'],
},
});
if (!gridConfig || !gridConfig.grid) {
warnOnce('UI_INIT_FAIL', 'Helpdesk domains grid init failed', { module: 'helpdesk-domains', component: 'grid' });
}
}
}

View File

@@ -75,6 +75,17 @@
--app-nav-icon-color: #6b7280;
}
/* Helpdesk module groups */
.app-sidebar-admin-group [data-details-key="helpdesk-lookup"] {
--app-nav-icon-color: #2563eb;
}
.app-sidebar-admin-group [data-details-key="helpdesk-monitoring"] {
--app-nav-icon-color: #059669;
}
.app-sidebar-admin-group [data-details-key="helpdesk-administration"] {
--app-nav-icon-color: #6b7280;
}
aside.app-sidebar li:has(> .app-empty-state) {
padding-inline: 19px;
}