// eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair /* eslint-disable max-lines */ import type { BulmaJS } from '@cityssm/bulma-js/types.js' import type { cityssmGlobal } from '@cityssm/bulma-webapp-js/src/types.js' import type { BurialSiteType, ContractType, Fee, FeeCategory } from '../../types/recordTypes.js' import type { Sunrise } from './types.js' declare const cityssm: cityssmGlobal declare const bulmaJS: BulmaJS declare const exports: Record ;(() => { const sunrise = exports.sunrise as Sunrise const feeCategoriesContainerElement = document.querySelector( '#container--feeCategories' ) as HTMLElement let feeCategories = exports.feeCategories as FeeCategory[] delete exports.feeCategories type ResponseJSON = | { success: true feeCategories: FeeCategory[] } | { success: false errorMessage?: string } function getFeeCategory(feeCategoryId: number): FeeCategory { return feeCategories.find( (currentFeeCategory) => currentFeeCategory.feeCategoryId === feeCategoryId ) as FeeCategory } function getFee(feeCategory: FeeCategory, feeId: number): Fee { return feeCategory.fees.find( (currentFee) => currentFee.feeId === feeId ) as Fee } // eslint-disable-next-line complexity function renderFeeCategories(): void { if (feeCategories.length === 0) { feeCategoriesContainerElement.innerHTML = `

There are no available fees.

` return } feeCategoriesContainerElement.innerHTML = '' for (const feeCategory of feeCategories) { const feeCategoryContainerElement = document.createElement('section') feeCategoryContainerElement.className = 'panel container--feeCategory' feeCategoryContainerElement.dataset.feeCategoryId = feeCategory.feeCategoryId.toString() // eslint-disable-next-line no-unsanitized/property feeCategoryContainerElement.innerHTML = `

${cityssm.escapeHTML(feeCategory.feeCategory ?? '')}

${ feeCategory.isGroupedFee ? 'Grouped Fee' : '' }
${ feeCategory.fees.length === 0 ? `
` : '' }
${sunrise.getMoveUpDownButtonFieldHTML( 'button--moveFeeCategoryUp', 'button--moveFeeCategoryDown' )}
` if (feeCategory.fees.length === 0) { feeCategoryContainerElement.insertAdjacentHTML( 'beforeend', `

There are no fees in the "${cityssm.escapeHTML(feeCategory.feeCategory ?? '')}" category.

` ) feeCategoryContainerElement .querySelector('.button--deleteFeeCategory') ?.addEventListener('click', confirmDeleteFeeCategory) } for (const fee of feeCategory.fees) { const panelBlockElement = document.createElement('div') panelBlockElement.className = 'panel-block is-block container--fee' panelBlockElement.dataset.feeId = fee.feeId.toString() const hasTagsBlock = (fee.isRequired ?? false) || fee.contractTypeId !== undefined || fee.burialSiteTypeId !== undefined // eslint-disable-next-line no-unsanitized/property panelBlockElement.innerHTML = `

${cityssm.escapeHTML(fee.feeName ?? '')}
${ // eslint-disable-next-line @typescript-eslint/no-unsafe-call cityssm .escapeHTML(fee.feeDescription ?? '') .replaceAll('\n', '
') }

${ hasTagsBlock ? `

${ // eslint-disable-next-line sonarjs/no-nested-conditional fee.isRequired ?? false ? 'Required' : '' } ${ // eslint-disable-next-line sonarjs/no-nested-conditional (fee.contractTypeId ?? -1) === -1 ? '' : ` ${cityssm.escapeHTML(fee.contractType ?? '')} ` } ${ // eslint-disable-next-line sonarjs/no-nested-conditional (fee.burialSiteTypeId ?? -1) === -1 ? '' : ` ${cityssm.escapeHTML(fee.burialSiteType ?? '')} ` }

` : '' }
${ fee.feeFunction ? `${cityssm.escapeHTML(fee.feeFunction)}
Fee Function` : ` $${(fee.feeAmount ?? 0).toFixed(2)}
Fee
` }
${ fee.taxPercentage ? `${fee.taxPercentage.toString()}%` : `$${(fee.taxAmount ?? 0).toFixed(2)}` }
Tax
${ fee.includeQuantity ? `${cityssm.escapeHTML(fee.quantityUnit ?? '')}
Quantity` : '' }
${sunrise.getMoveUpDownButtonFieldHTML( 'button--moveFeeUp', 'button--moveFeeDown' )}
` panelBlockElement .querySelector('.a--editFee') ?.addEventListener('click', openEditFee) panelBlockElement .querySelector('.a--editFeeAmount') ?.addEventListener('click', openEditFeeAmount) ;( panelBlockElement.querySelector( '.button--moveFeeUp' ) as HTMLButtonElement ).addEventListener('click', moveFee) ;( panelBlockElement.querySelector( '.button--moveFeeDown' ) as HTMLButtonElement ).addEventListener('click', moveFee) feeCategoryContainerElement.append(panelBlockElement) } feeCategoryContainerElement .querySelector('.button--editFeeCategory') ?.addEventListener('click', openEditFeeCategory) feeCategoryContainerElement .querySelector('.button--addFee') ?.addEventListener('click', openAddFee) ;( feeCategoryContainerElement.querySelector( '.button--moveFeeCategoryUp' ) as HTMLButtonElement ).addEventListener('click', moveFeeCategory) ;( feeCategoryContainerElement.querySelector( '.button--moveFeeCategoryDown' ) as HTMLButtonElement ).addEventListener('click', moveFeeCategory) feeCategoriesContainerElement.append(feeCategoryContainerElement) } } /* * Fee Categories */ document .querySelector('#button--addFeeCategory') ?.addEventListener('click', () => { let addCloseModalFunction: () => void function doAddFeeCategory(submitEvent: SubmitEvent): void { submitEvent.preventDefault() cityssm.postJSON( `${sunrise.urlPrefix}/admin/doAddFeeCategory`, submitEvent.currentTarget, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories addCloseModalFunction() renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Creating Fee Category', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } cityssm.openHtmlModal('adminFees-addFeeCategory', { onshown(modalElement, closeModalFunction) { bulmaJS.toggleHtmlClipped() ;( modalElement.querySelector( '#feeCategoryAdd--feeCategory' ) as HTMLInputElement ).focus() addCloseModalFunction = closeModalFunction modalElement .querySelector('form') ?.addEventListener('submit', doAddFeeCategory) }, onremoved() { bulmaJS.toggleHtmlClipped() ;( document.querySelector( '#button--addFeeCategory' ) as HTMLButtonElement ).focus() } }) }) function openEditFeeCategory(clickEvent: Event): void { const feeCategoryId = Number.parseInt( ( (clickEvent.currentTarget as HTMLElement).closest( '.container--feeCategory' ) as HTMLElement ).dataset.feeCategoryId ?? '', 10 ) const feeCategory = getFeeCategory(feeCategoryId) let editCloseModalFunction: () => void function doUpdateFeeCategory(submitEvent: SubmitEvent): void { submitEvent.preventDefault() cityssm.postJSON( `${sunrise.urlPrefix}/admin/doUpdateFeeCategory`, submitEvent.currentTarget, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories editCloseModalFunction() renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Updating Fee Category', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } cityssm.openHtmlModal('adminFees-editFeeCategory', { onshow(modalElement) { ;( modalElement.querySelector( '#feeCategoryEdit--feeCategoryId' ) as HTMLInputElement ).value = feeCategory.feeCategoryId.toString() ;( modalElement.querySelector( '#feeCategoryEdit--feeCategory' ) as HTMLInputElement ).value = feeCategory.feeCategory if (feeCategory.isGroupedFee) { ;( modalElement.querySelector( '#feeCategoryEdit--isGroupedFee' ) as HTMLInputElement ).checked = true } }, onshown(modalElement, closeModalFunction) { bulmaJS.toggleHtmlClipped() editCloseModalFunction = closeModalFunction modalElement .querySelector('form') ?.addEventListener('submit', doUpdateFeeCategory) ;( modalElement.querySelector( '#feeCategoryEdit--feeCategory' ) as HTMLInputElement ).focus() }, onremoved() { bulmaJS.toggleHtmlClipped() } }) } function confirmDeleteFeeCategory(clickEvent: Event): void { const feeCategoryId = Number.parseInt( ( (clickEvent.currentTarget as HTMLElement).closest( '.container--feeCategory' ) as HTMLElement ).dataset.feeCategoryId ?? '', 10 ) function doDelete(): void { cityssm.postJSON( `${sunrise.urlPrefix}/admin/doDeleteFeeCategory`, { feeCategoryId }, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Updating Fee Category', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } bulmaJS.confirm({ title: 'Delete Fee Category?', message: 'Are you sure you want to delete this fee category?', contextualColorName: 'warning', okButton: { text: 'Yes, Delete the Fee Category', callbackFunction: doDelete } }) } function moveFeeCategory(clickEvent: MouseEvent): void { const buttonElement = clickEvent.currentTarget as HTMLButtonElement const feeCategoryId = (buttonElement.closest('.container--feeCategory') as HTMLElement).dataset .feeCategoryId ?? '' cityssm.postJSON( `${sunrise.urlPrefix}/admin/${ buttonElement.dataset.direction === 'up' ? 'doMoveFeeCategoryUp' : 'doMoveFeeCategoryDown' }`, { feeCategoryId, moveToEnd: clickEvent.shiftKey ? '1' : '0' }, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Moving Fee Category', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } /* * Fees */ function openAddFee(clickEvent: Event): void { const feeCategoryId = Number.parseInt( ( (clickEvent.currentTarget as HTMLElement).closest( '.container--feeCategory' ) as HTMLElement ).dataset.feeCategoryId ?? '', 10 ) let addCloseModalFunction: () => void function doAddFee(submitEvent: SubmitEvent): void { submitEvent.preventDefault() cityssm.postJSON( `${sunrise.urlPrefix}/admin/doAddFee`, submitEvent.currentTarget, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories addCloseModalFunction() renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Adding Fee', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } cityssm.openHtmlModal('adminFees-addFee', { onshow(modalElement) { const feeCategoryElement = modalElement.querySelector( '#feeAdd--feeCategoryId' ) as HTMLSelectElement for (const feeCategory of feeCategories) { const optionElement = document.createElement('option') optionElement.value = feeCategory.feeCategoryId.toString() optionElement.textContent = feeCategory.feeCategory if (feeCategory.feeCategoryId === feeCategoryId) { optionElement.selected = true } feeCategoryElement.append(optionElement) } const contractTypeElement = modalElement.querySelector( '#feeAdd--contractTypeId' ) as HTMLSelectElement for (const contractType of exports.contractTypes as ContractType[]) { const optionElement = document.createElement('option') optionElement.value = contractType.contractTypeId.toString() optionElement.textContent = contractType.contractType contractTypeElement.append(optionElement) } const burialSiteTypeElement = modalElement.querySelector( '#feeAdd--burialSiteTypeId' ) as HTMLSelectElement for (const burialSiteType of exports.burialSiteTypes as BurialSiteType[]) { const optionElement = document.createElement('option') optionElement.value = burialSiteType.burialSiteTypeId.toString() optionElement.textContent = burialSiteType.burialSiteType burialSiteTypeElement.append(optionElement) } ;( modalElement.querySelector( '#feeAdd--taxPercentage' ) as HTMLInputElement ).value = (exports.taxPercentageDefault as number).toString() sunrise.populateAliases(modalElement) }, onshown(modalElement, closeModalFunction) { bulmaJS.toggleHtmlClipped() addCloseModalFunction = closeModalFunction modalElement.querySelector('form')?.addEventListener('submit', doAddFee) ;( modalElement.querySelector('#feeAdd--feeName') as HTMLInputElement ).focus() ;( modalElement.querySelector('#feeAdd--feeFunction') as HTMLInputElement ).addEventListener('change', () => { const feeAmountElement = modalElement.querySelector( '#feeAdd--feeAmount' ) as HTMLInputElement const feeFunctionElement = modalElement.querySelector( '#feeAdd--feeFunction' ) as HTMLSelectElement if (feeFunctionElement.value === '') { feeFunctionElement .closest('.select') ?.classList.remove('is-success') feeAmountElement.classList.add('is-success') feeAmountElement.disabled = false } else { feeFunctionElement.closest('.select')?.classList.add('is-success') feeAmountElement.classList.remove('is-success') feeAmountElement.disabled = true } }) modalElement .querySelector('#feeAdd--taxPercentage') ?.addEventListener('keyup', () => { const taxAmountElement = modalElement.querySelector( '#feeAdd--taxAmount' ) as HTMLInputElement const taxPercentageElement = modalElement.querySelector( '#feeAdd--taxPercentage' ) as HTMLInputElement if (taxPercentageElement.value === '') { taxPercentageElement.classList.remove('is-success') taxAmountElement.classList.add('is-success') taxAmountElement.disabled = false } else { taxPercentageElement.classList.add('is-success') taxAmountElement.classList.remove('is-success') taxAmountElement.disabled = true } }) modalElement .querySelector('#feeAdd--includeQuantity') ?.addEventListener('change', () => { ;( modalElement.querySelector( '#feeAdd--quantityUnit' ) as HTMLInputElement ).disabled = ( modalElement.querySelector( '#feeAdd--includeQuantity' ) as HTMLSelectElement ).value === '' }) }, onremoved() { bulmaJS.toggleHtmlClipped() } }) } function openEditFeeAmount(clickEvent: Event): void { clickEvent.preventDefault() const feeContainerElement = ( clickEvent.currentTarget as HTMLElement ).closest('.container--fee') as HTMLElement const feeId = Number.parseInt(feeContainerElement.dataset.feeId ?? '', 10) const feeCategoryId = Number.parseInt( (feeContainerElement.closest('.container--feeCategory') as HTMLElement) .dataset.feeCategoryId ?? '' ) const feeCategory = getFeeCategory(feeCategoryId) const fee = getFee(feeCategory, feeId) let editCloseModalFunction: () => void function doUpdateFeeAmount(submitEvent: SubmitEvent): void { submitEvent.preventDefault() cityssm.postJSON( `${sunrise.urlPrefix}/admin/doUpdateFeeAmount`, submitEvent.currentTarget, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories editCloseModalFunction() renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Updating Fee Amount', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } cityssm.openHtmlModal('adminFees-editFeeAmount', { onshow(modalElement) { ;( modalElement.querySelector( '#feeAmountEdit--feeId' ) as HTMLInputElement ).value = fee.feeId.toString() ;( modalElement.querySelector( '#feeAmountEdit--feeCategory' ) as HTMLElement ).textContent = feeCategory.feeCategory ;( modalElement.querySelector('#feeAmountEdit--feeName') as HTMLElement ).textContent = fee.feeName ?? '' ;( modalElement.querySelector( '#feeAmountEdit--feeAmount' ) as HTMLInputElement ).value = fee.feeAmount?.toFixed(2) ?? '0' }, onshown(modalElement, closeModalFunction) { ;( modalElement.querySelector( '#feeAmountEdit--feeAmount' ) as HTMLInputElement ).select() editCloseModalFunction = closeModalFunction modalElement .querySelector('form') ?.addEventListener('submit', doUpdateFeeAmount) } }) } function openEditFee(clickEvent: Event): void { clickEvent.preventDefault() const feeContainerElement = ( clickEvent.currentTarget as HTMLElement ).closest('.container--fee') as HTMLElement const feeId = Number.parseInt(feeContainerElement.dataset.feeId ?? '', 10) const feeCategoryId = Number.parseInt( (feeContainerElement.closest('.container--feeCategory') as HTMLElement) .dataset.feeCategoryId ?? '' ) const feeCategory = getFeeCategory(feeCategoryId) const fee = getFee(feeCategory, feeId) let editCloseModalFunction: () => void let editModalElement: HTMLElement function doUpdateFee(submitEvent: SubmitEvent): void { submitEvent.preventDefault() cityssm.postJSON( `${sunrise.urlPrefix}/admin/doUpdateFee`, submitEvent.currentTarget, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories editCloseModalFunction() renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Updating Fee', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } function confirmDeleteFee(clickEvent: Event): void { clickEvent.preventDefault() function doDelete(): void { cityssm.postJSON( `${sunrise.urlPrefix}/admin/doDeleteFee`, { feeId }, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories editCloseModalFunction() renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Deleting Fee', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } bulmaJS.confirm({ title: 'Delete Fee?', message: 'Are you sure you want to delete this fee?', contextualColorName: 'warning', okButton: { text: 'Yes, Delete the Fee', callbackFunction: doDelete } }) } function toggleFeeFields(): void { const feeAmountElement = editModalElement.querySelector( '#feeEdit--feeAmount' ) as HTMLInputElement const feeFunctionElement = editModalElement.querySelector( '#feeEdit--feeFunction' ) as HTMLSelectElement if (feeFunctionElement.value === '') { feeFunctionElement.closest('.select')?.classList.remove('is-success') feeAmountElement.classList.add('is-success') feeAmountElement.disabled = false } else { feeFunctionElement.closest('.select')?.classList.add('is-success') feeAmountElement.classList.remove('is-success') feeAmountElement.disabled = true } } function toggleTaxFields(): void { const taxAmountElement = editModalElement.querySelector( '#feeEdit--taxAmount' ) as HTMLInputElement const taxPercentageElement = editModalElement.querySelector( '#feeEdit--taxPercentage' ) as HTMLInputElement if (taxPercentageElement.value === '') { taxPercentageElement.classList.remove('is-success') taxAmountElement.classList.add('is-success') taxAmountElement.disabled = false } else { taxPercentageElement.classList.add('is-success') taxAmountElement.classList.remove('is-success') taxAmountElement.disabled = true } } function toggleQuantityFields(): void { const includeQuantityValue = ( editModalElement.querySelector( '#feeEdit--includeQuantity' ) as HTMLSelectElement ).value ;( editModalElement.querySelector( '#feeEdit--quantityUnit' ) as HTMLInputElement ).disabled = includeQuantityValue === '' } cityssm.openHtmlModal('adminFees-editFee', { onshow(modalElement) { editModalElement = modalElement ;( modalElement.querySelector('#feeEdit--feeId') as HTMLInputElement ).value = fee.feeId.toString() const feeCategoryElement = modalElement.querySelector( '#feeEdit--feeCategoryId' ) as HTMLSelectElement for (const feeCategory of feeCategories) { const optionElement = document.createElement('option') optionElement.value = feeCategory.feeCategoryId.toString() optionElement.textContent = feeCategory.feeCategory if (feeCategory.feeCategoryId === feeCategoryId) { optionElement.selected = true } feeCategoryElement.append(optionElement) } ;( modalElement.querySelector('#feeEdit--feeName') as HTMLInputElement ).value = fee.feeName ?? '' ;( modalElement.querySelector('#feeEdit--feeAccount') as HTMLInputElement ).value = fee.feeAccount ?? '' ;( modalElement.querySelector( '#feeEdit--feeDescription' ) as HTMLTextAreaElement ).value = fee.feeDescription ?? '' const contractTypeElement = modalElement.querySelector( '#feeEdit--contractTypeId' ) as HTMLSelectElement for (const contractType of exports.contractTypes as ContractType[]) { const optionElement = document.createElement('option') optionElement.value = contractType.contractTypeId.toString() optionElement.textContent = contractType.contractType if (contractType.contractTypeId === fee.contractTypeId) { optionElement.selected = true } contractTypeElement.append(optionElement) } const lotTypeElement = modalElement.querySelector( '#feeEdit--burialSiteTypeId' ) as HTMLSelectElement for (const burialSiteType of exports.burialSiteTypes as BurialSiteType[]) { const optionElement = document.createElement('option') optionElement.value = burialSiteType.burialSiteTypeId.toString() optionElement.textContent = burialSiteType.burialSiteType if (burialSiteType.burialSiteTypeId === fee.burialSiteTypeId) { optionElement.selected = true } lotTypeElement.append(optionElement) } ;( modalElement.querySelector('#feeEdit--feeAmount') as HTMLInputElement ).value = fee.feeAmount ? fee.feeAmount.toFixed(2) : '' modalElement .querySelector('#feeEdit--feeFunction') ?.addEventListener('change', toggleFeeFields) toggleFeeFields() ;( modalElement.querySelector('#feeEdit--taxAmount') as HTMLInputElement ).value = fee.taxAmount ? fee.taxAmount.toFixed(2) : '' const taxPercentageElement = modalElement.querySelector( '#feeEdit--taxPercentage' ) as HTMLInputElement taxPercentageElement.value = fee.taxPercentage ? fee.taxPercentage.toString() : '' taxPercentageElement.addEventListener('keyup', toggleTaxFields) toggleTaxFields() const includeQuantityElement = modalElement.querySelector( '#feeEdit--includeQuantity' ) as HTMLSelectElement if (fee.includeQuantity ?? false) { includeQuantityElement.value = '1' } includeQuantityElement.addEventListener('change', toggleQuantityFields) ;( modalElement.querySelector( '#feeEdit--quantityUnit' ) as HTMLInputElement ).value = fee.quantityUnit ?? '' toggleQuantityFields() if (fee.isRequired ?? false) { ;( modalElement.querySelector( '#feeEdit--isRequired' ) as HTMLSelectElement ).value = '1' } sunrise.populateAliases(modalElement) }, onshown(modalElement, closeModalFunction) { bulmaJS.toggleHtmlClipped() editCloseModalFunction = closeModalFunction modalElement .querySelector('form') ?.addEventListener('submit', doUpdateFee) bulmaJS.init(modalElement) modalElement .querySelector('.button--deleteFee') ?.addEventListener('click', confirmDeleteFee) }, onremoved() { bulmaJS.toggleHtmlClipped() } }) } function moveFee(clickEvent: MouseEvent): void { const buttonElement = clickEvent.currentTarget as HTMLButtonElement const feeContainerElement = buttonElement.closest( '.container--fee' ) as HTMLElement const feeId = feeContainerElement.dataset.feeId ?? '' cityssm.postJSON( `${sunrise.urlPrefix}/admin/${ buttonElement.dataset.direction === 'up' ? 'doMoveFeeUp' : 'doMoveFeeDown' }`, { feeId, moveToEnd: clickEvent.shiftKey ? '1' : '0' }, (rawResponseJSON) => { const responseJSON = rawResponseJSON as ResponseJSON if (responseJSON.success) { feeCategories = responseJSON.feeCategories renderFeeCategories() } else { bulmaJS.alert({ title: 'Error Moving Fee', message: responseJSON.errorMessage ?? '', contextualColorName: 'danger' }) } } ) } /* * Initialize */ renderFeeCategories() })()