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:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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'],
|
||||
|
||||
157
modules/helpdesk/pages/helpdesk/domains-data().php
Normal file
157
modules/helpdesk/pages/helpdesk/domains-data().php
Normal 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);
|
||||
76
modules/helpdesk/pages/helpdesk/domains/filter-schema.php
Normal file
76
modules/helpdesk/pages/helpdesk/domains/filter-schema.php
Normal 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],
|
||||
],
|
||||
],
|
||||
]);
|
||||
62
modules/helpdesk/pages/helpdesk/domains/index().php
Normal file
62
modules/helpdesk/pages/helpdesk/domains/index().php
Normal 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')],
|
||||
];
|
||||
51
modules/helpdesk/pages/helpdesk/domains/index(default).phtml
Normal file
51
modules/helpdesk/pages/helpdesk/domains/index(default).phtml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
85
modules/helpdesk/web/js/pages/helpdesk-domains-index.js
Normal file
85
modules/helpdesk/web/js/pages/helpdesk-domains-index.js
Normal 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' });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user