import thisCss from "./privacy-policy.css"; import thisHtml from "./privacy-policy.html"; import commonComponentStylesCss from "../common-component-styles.css"; import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs"; import { convertMarkdownToHtml } from "@ibgib/helper-gib/dist/helpers/markdown-helper.mjs"; import { ROOT_ADDR } from "@ibgib/ts-gib/dist/V1/constants.mjs"; import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs"; import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs"; import { IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase } from "../ibgib-dynamic-component-bases.mjs"; import { ElementsBase, IbGibDynamicComponentInstance } from "../component-types.mjs"; import { getComponentCtorArg } from "../../../app-bootstrap/init-orchestration.mjs"; export const PRIVACY_POLICY_COMPONENT_NAME = 'ibgib-privacy-policy'; /** * Metadata for the Privacy Policy component. */ export class PrivacyPolicyComponentMeta extends IbGibDynamicComponentMetaBase { protected lc: string = `[PrivacyPolicyComponentMeta]`; routeRegExp?: RegExp = new RegExp(`^${PRIVACY_POLICY_COMPONENT_NAME}$|^privacy(-policy)?$`, 'i'); componentName = PRIVACY_POLICY_COMPONENT_NAME; constructor() { super(getComponentCtorArg()); if (typeof customElements !== 'undefined' && !customElements.get(this.componentName)) { customElements.define(this.componentName, PrivacyPolicyComponentInstance); } } async createInstance({ path, ibGibAddr }: { path: string; ibGibAddr: IbGibAddr; }): Promise { const lc = `${this.lc}[${this.createInstance.name}]`; const component = document.createElement(this.componentName) as PrivacyPolicyComponentInstance; await component.initialize({ ibGibAddr: ibGibAddr || ROOT_ADDR, meta: this, html: thisHtml, css: [commonComponentStylesCss, thisCss], }); return component; } } /** * References to DOM elements within the Privacy Policy shadow root. */ interface PrivacyPolicyElements extends ElementsBase { btnBack?: HTMLButtonElement; loadingSpinner?: HTMLDivElement; errorContainer?: HTMLDivElement; errorMessage?: HTMLParagraphElement; btnRetry?: HTMLButtonElement; privacyContent?: HTMLElement; } /** * Concrete Web Component implementation for rendering Privacy Policy markdown. */ export class PrivacyPolicyComponentInstance extends IbGibDynamicComponentInstanceBase { protected lc: string = `[PrivacyPolicyComponentInstance]`; /** Path or URL to the markdown privacy policy file */ public markdownUrl: string = '/PRIVACY_POLICY.md'; constructor() { super(); } public override async created(): Promise { const lc = `${this.lc}[${this.created.name}]`; try { const shadow = this.shadowRoot!; const contentEl = (shadow.querySelector('.privacy-container') || shadow.querySelector('#privacy-content') || this) as HTMLElement; this.elements = { contentEl, btnBack: shadow.querySelector('#btn-back') as HTMLButtonElement, loadingSpinner: shadow.querySelector('#loading-spinner') as HTMLDivElement, errorContainer: shadow.querySelector('#error-container') as HTMLDivElement, errorMessage: shadow.querySelector('#error-message') as HTMLParagraphElement, btnRetry: shadow.querySelector('#btn-retry') as HTMLButtonElement, privacyContent: shadow.querySelector('#privacy-content') as HTMLElement, }; this.elements.btnBack?.addEventListener('click', () => { if (window.history.length > 1) { window.history.back(); } else { window.location.href = '/'; } }); this.elements.btnRetry?.addEventListener('click', () => { this.loadPrivacyPolicy(); }); await this.loadPrivacyPolicy(); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); } } public override async disconnected(): Promise { const lc = `${this.lc}[${this.disconnected.name}]`; // Cleanup if needed } /** * Fetches the markdown policy and converts it to formatted HTML in the DOM. */ public async loadPrivacyPolicy(): Promise { const lc = `${this.lc}[${this.loadPrivacyPolicy.name}]`; try { const el = this.elements; if (el?.loadingSpinner) { el.loadingSpinner.classList.remove('hidden'); } if (el?.errorContainer) { el.errorContainer.classList.add('hidden'); } if (el?.privacyContent) { el.privacyContent.classList.add('hidden'); } const res = await fetch(this.markdownUrl); if (!res.ok) { throw new Error(`Failed to load ${this.markdownUrl} (status ${res.status}: ${res.statusText})`); } const mdText = await res.text(); const renderedHtml = convertMarkdownToHtml(mdText, { linkTarget: '_blank', linkRel: 'noopener', enableAlerts: true }); if (this.elements?.privacyContent) { this.elements.privacyContent.innerHTML = renderedHtml; this.elements.privacyContent.classList.remove('hidden'); } } catch (error) { const msg = extractErrorMsg(error); console.error(`${lc} ${msg}`); if (this.elements?.errorMessage) { this.elements.errorMessage.textContent = `Unable to load Privacy Policy: ${msg}`; } if (this.elements?.errorContainer) { this.elements.errorContainer.classList.remove('hidden'); } } finally { if (this.elements?.loadingSpinner) { this.elements.loadingSpinner.classList.add('hidden'); } } } }