diff --git a/modules/helpdesk/i18n/default_de.json b/modules/helpdesk/i18n/default_de.json index 93d2542..5400c1e 100644 --- a/modules/helpdesk/i18n/default_de.json +++ b/modules/helpdesk/i18n/default_de.json @@ -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" } diff --git a/modules/helpdesk/i18n/default_en.json b/modules/helpdesk/i18n/default_en.json index e03a598..c32a78b 100644 --- a/modules/helpdesk/i18n/default_en.json +++ b/modules/helpdesk/i18n/default_en.json @@ -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" } diff --git a/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php b/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php index 450f911..d54e89b 100644 --- a/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php +++ b/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php @@ -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> + */ + 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> + */ + 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. * diff --git a/modules/helpdesk/module.php b/modules/helpdesk/module.php index 6c20413..31c07da 100644 --- a/modules/helpdesk/module.php +++ b/modules/helpdesk/module.php @@ -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'], diff --git a/modules/helpdesk/pages/helpdesk/domains-data().php b/modules/helpdesk/pages/helpdesk/domains-data().php new file mode 100644 index 0000000..41ebce2 --- /dev/null +++ b/modules/helpdesk/pages/helpdesk/domains-data().php @@ -0,0 +1,157 @@ +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); diff --git a/modules/helpdesk/pages/helpdesk/domains/filter-schema.php b/modules/helpdesk/pages/helpdesk/domains/filter-schema.php new file mode 100644 index 0000000..e9061ac --- /dev/null +++ b/modules/helpdesk/pages/helpdesk/domains/filter-schema.php @@ -0,0 +1,76 @@ + [ + '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], + ], + ], +]); diff --git a/modules/helpdesk/pages/helpdesk/domains/index().php b/modules/helpdesk/pages/helpdesk/domains/index().php new file mode 100644 index 0000000..a72d86c --- /dev/null +++ b/modules/helpdesk/pages/helpdesk/domains/index().php @@ -0,0 +1,62 @@ +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')], +]; diff --git a/modules/helpdesk/pages/helpdesk/domains/index(default).phtml b/modules/helpdesk/pages/helpdesk/domains/index(default).phtml new file mode 100644 index 0000000..a4e5c16 --- /dev/null +++ b/modules/helpdesk/pages/helpdesk/domains/index(default).phtml @@ -0,0 +1,51 @@ + + + + + + + + + +
+
+
+ + + + diff --git a/modules/helpdesk/templates/aside-helpdesk-panel.phtml b/modules/helpdesk/templates/aside-helpdesk-panel.phtml index c8a3733..056405e 100644 --- a/modules/helpdesk/templates/aside-helpdesk-panel.phtml +++ b/modules/helpdesk/templates/aside-helpdesk-panel.phtml @@ -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; - } -} ?> -