import type { PropertyValueMap } from 'lit' import { LitElement, css, html } from 'lit' import SimpleBar from 'simplebar' import type { Options } from 'simplebar' import simpleBar_css from 'simplebar/dist/simplebar.css' import { property, query, state } from 'lit/decorators.js' import { repeat } from 'lit/directives/repeat.js' import { CORE_BANKING_SERVICE_IDS, ILicensingObjects, statusCode } from '@upswot/core' import { customElementIfNotExist } from '../../utils/customElementIfNotExist' import { dispatchCustomEvent } from '../../utils/dispatchCustomEvent' import { UsBaseElement } from '../base/UsBaseElement' import { getLocale, localizeManager, localized, setBrowserLocale } from '../../dependencies' import type { IService } from './models/IService' import style_css from './styles.pcss' import './service-card/ServiceCard' declare global { interface HTMLElementTagNameMap { 'us-service-grid': ServiceGrid } } @customElementIfNotExist('us-service-grid') @localized() export class ServiceGrid extends LitElement { static styles = [ UsBaseElement.styles, css([simpleBar_css] as TemplateStringsArray | any), css([style_css] as TemplateStringsArray | any), ] @property({ type: String }) variant: 'default' | 'bureau' = 'default' @property({ type: Array }) services: IService[] = [] @property({ type: Number }) selectionLimit = 1 @property({ type: Boolean }) isSelectable = false @property({ type: Array }) preselected: IService[] = [] @property({ type: String }) widgetView = 'vertical' @property({ type: Array }) newLicenseConditions = [ { access: true, licensingObject: ILicensingObjects.BanksConnecting, requested: false, }, { access: true, licensingObject: ILicensingObjects.BusinessAppsConnecting, requested: false, }, { access: true, licensingObject: ILicensingObjects.Marketing, requested: false, }, ] @property({ type: String }) containerHeight = '' @property({ type: String }) locale: string = getLocale() @property({ type: Boolean }) disableScroll = false @property({ type: Boolean }) showInviteIcon = true @property({ type: Boolean }) isDarkTheme = false @property({ type: Boolean }) isSuspendFeature = false @property({ type: Boolean }) isDisconnectFeature = false @property({ type: Boolean }) isLoadingKebabMenuItem = false @property({ type: Boolean }) customTooltipPlacement = false @state() selectedServices: IService[] = [] @state() numOfLoadedServices = 0 @query('[data-simplebar]') simpleBarEl: HTMLDivElement | undefined simpleBar!: SimpleBar willUpdate(changedProperties: PropertyValueMap | Map): void { if (changedProperties.has('preselected')) this.selectedServices = this.preselected if (changedProperties.has('locale')) setBrowserLocale(this.locale) } async connectedCallback() { super.connectedCallback() await setBrowserLocale(this.locale) } disconnectedCallback() { this.disposeSimpleBar() super.disconnectedCallback() } firstUpdated() { if (!this.disableScroll) this.initSimpleBar() } render() { return html`
${repeat(this.services, service => service.name, service => html` this.handleClickKebabListItem(e, service)} > `)}
` } handleServiceOnLoad() { this.numOfLoadedServices += 1 const total = this.services.length const percent = Math.round(100 / total * this.numOfLoadedServices) if (percent > 70) this.dispatchLoadEvent() } dispatchLoadEvent() { const event = new CustomEvent('load') this.dispatchEvent(event) } shouldShowInvite(service: IService) { if (Object.values(CORE_BANKING_SERVICE_IDS).includes(service.parentServiceId)) return false return this.showInviteIcon } shouldHideRetry(service: IService) { if (service.parentServiceId === CORE_BANKING_SERVICE_IDS.alkami) return true return false } handleServiceSelect(service: IService) { if (this.isDisabledAppCard(service.relatedLicensingObject)) return if (this.isSelectable) { this.manageSelectedServices(service) this.dispatchSelectEvent(this.selectedServices) } else { this.dispatchSelectEvent(service) } } getActionsByStatus(service: IService) { if (Object.values(CORE_BANKING_SERVICE_IDS).includes(service.parentServiceId)) return [] const resume = { label: localizeManager.resume_data_sync(), action: 'resumeDataSync', popup: false, } const disconnect = { label: localizeManager.disconnect_data(), popup: true, iconPopup: 'error', titlePopup: localizeManager.disconnect_data_title_popup(), descriptionPopup: localizeManager.disconnect_data_description_popup(service.title || service.name), confirmPopup: localizeManager.disconnect_data(), cancelPopup: localizeManager.cancel(), actionMethod: 'handleDisconnect', } const suspend = { label: localizeManager.suspend_data_sync(), popup: true, iconPopup: 'warning', titlePopup: localizeManager.suspend_data_sync(), descriptionPopup: localizeManager.suspend_description_popup(service.title || service.name), confirmPopup: localizeManager.suspend(), cancelPopup: localizeManager.cancel(), actionMethod: 'suspendDataSync', } const result: any[] = [] if (this.isSuspendFeature) { if (service.serviceConnectionStatus === 'Suspended') result.push(resume) else if (service.statusCode !== 4) result.push(suspend) } if (this.isDisconnectFeature) result.push(disconnect) return result } handleClickOnServiceCard(service: IService) { const disabledStatusCodes = [1, 2, 3, 4] if ( this.isDisabledAppCard(service.relatedLicensingObject) || service.serviceConnectionStatus === 'Suspended' || service.serviceConnectionStatus === 'Disconnecting' || (disabledStatusCodes.includes(service.statusCode!)) ) return this.dispatchSelectEvent(service) } handleClickOnServiceShare(service: IService) { this.dispatchShareEvent(service) } handleClickKebabListItem(e: CustomEvent, service: IService) { dispatchCustomEvent('clickKebabListItem', { ...e.detail.data, ...service }, this) } dispatchShareEvent(data: IService) { const detail = data const event = new CustomEvent('share', { detail, composed: true, bubbles: true, }) this.dispatchEvent(event) } initSimpleBar() { if (this.simpleBarEl) this.simpleBar = new SimpleBar(this.simpleBarEl, { ariaLabel: this.ariaLabel || 'simplebar-el' } as Options) } disposeSimpleBar() { this.simpleBar && this.simpleBar.unMount() } async reInitSimpleBar() { await this.updateComplete this.disposeSimpleBar() this.initSimpleBar() } manageSelectedServices(service: IService) { const foundIndex = this.selectedServices.findIndex((selected: IService) => selected.instanceId === service.instanceId) const isSelected = foundIndex >= 0 if (isSelected) { this.selectedServices = this.selectedServices.filter((_, i) => i !== foundIndex) return } if (this.selectionLimit <= this.selectedServices.length) this.selectedServices = this.selectedServices.slice(1) this.selectedServices = [...this.selectedServices, service] } dispatchSelectEvent(data: IService | IService[]) { const detail = { [Array.isArray(data) ? 'services' : 'service']: data } const event = new CustomEvent('select', { detail, composed: true, bubbles: true, }) this.dispatchEvent(event) } getIsSelected(id: string) { const index = this.selectedServices.findIndex(s => s.instanceId === id) return index >= 0 } handleServiceCardEnter(e: KeyboardEvent, service: IService) { if (e.key === 'Enter') this.handleClickOnServiceCard(service) } isDisabledAppCard(relatedLicensingObject: string) { if (this.variant === 'bureau') { return false } else { const license = this.newLicenseConditions.find(license => license.licensingObject === relatedLicensingObject) return !license?.access } } }