{"version":3,"file":"client.mjs","sources":["../app/constants.ts","../app/utils.ts","../app/teaser_internet.client.ts"],"sourcesContent":["export const PSS_URL_PROD = 'https://internet.check24.de/';\n\nexport const JERSEY_STOCK_API_URL = `${PSS_URL_PROD}api/jersey-stock`;\n\nexport const TID3_VALUE = 'jerseypromo_webteaser';\n\nexport type Labels = {\n    readonly linkLabel: string;\n    readonly title: string;\n    readonly headline2: string;\n    readonly legend: string;\n    readonly new: string;\n    readonly change: string;\n    readonly move: string;\n    readonly submit: string;\n    readonly badge: string;\n    readonly badgeFew: string;\n    readonly cashbackLabelTop: string;\n    readonly cashbackLabelBottom: string;\n};\n\nexport const labels = {\n    linkLabel: 'Zum Internet-Vergleich',\n    title: 'Internet-Tarife vergleichen',\n    headline2: 'und gratis Trikot sichern',\n    legend: 'Art des Vertrags:',\n    new: 'neuer Vertrag',\n    change: 'Vertrag wechseln',\n    move: 'Umzug',\n    submit: 'Tarife finden',\n    badge: 'Nur noch {count} Trikots',\n    badgeFew: 'Nur noch wenige Trikots',\n    cashbackLabelTop: 'Bis zu',\n    cashbackLabelBottom: 'Cashback',\n} satisfies Labels;\n","import type { Labels } from './constants';\nimport { JERSEY_STOCK_API_URL } from './constants';\n\ntype JerseyStockResponse = {\n    readonly totalAmountLeft: number;\n};\n\nexport type JerseyBadgeState = { status: 'loading' } | { status: 'few' } | { status: 'count'; value: number };\n\n/** Fail fast when the API is unreachable (e.g. Factory layout check on localhost). */\nconst JERSEY_STOCK_FETCH_TIMEOUT_MS = 3_000;\n\nexport async function fetchJerseyStock(): Promise<JerseyBadgeState> {\n    try {\n        const response = await fetch(JERSEY_STOCK_API_URL, {\n            signal: AbortSignal.timeout(JERSEY_STOCK_FETCH_TIMEOUT_MS),\n        });\n        if (!response.ok) return { status: 'few' };\n\n        const data = (await response.json()) as JerseyStockResponse;\n        if (typeof data.totalAmountLeft !== 'number' || data.totalAmountLeft <= 0) {\n            return { status: 'few' };\n        }\n\n        return { status: 'count', value: data.totalAmountLeft };\n    } catch {\n        return { status: 'few' };\n    }\n}\n\nexport function formatBadgeHtml(\n    labels: Pick<Labels, 'badge' | 'badgeFew'>,\n    state: Exclude<JerseyBadgeState, { status: 'loading' }>,\n): string {\n    if (state.status === 'few') {\n        return labels.badgeFew;\n    }\n\n    const [prefix, suffix] = labels.badge.split('{count}');\n    const formattedCount = new Intl.NumberFormat('de-DE').format(state.value);\n    return `${prefix}${formattedCount}${suffix}`;\n}\n","/// <reference types=\"../.check24-home/types/client\" />\n\nimport type { Language } from '../.check24-home/types/check24';\nimport type { HomeClientComponentElement, HomeClientComponentProps } from '../.check24-home/types/client';\nimport { labels, PSS_URL_PROD, TID3_VALUE } from './constants';\nimport { fetchJerseyStock, formatBadgeHtml, type JerseyBadgeState } from './utils';\nimport sharedStyles from './assets/teaser_internet_shared.css?raw';\nimport clientStyles from './assets/teaser_internet_client.css?raw';\nimport jerseyCutout from './assets/jersey-cutout.webp';\n\nconst styles = `${sharedStyles}\\n${clientStyles}`;\n\ntype CustomerType = 'new' | 'change' | 'move';\n\nexport default class InternetTeaserClient extends HTMLElement implements HomeClientComponentElement {\n    deviceOutput: 'desktop' | 'mobile' | 'tabletapp' | 'app' = 'desktop';\n    language: Language = 'de-DE';\n    platform: 'app' | 'web' = 'web';\n    systemTime = 0;\n\n    private shadow: ShadowRoot | null = null;\n    private badgeState: JerseyBadgeState = { status: 'loading' };\n\n    static get observedAttributes(): string[] {\n        return ['device-output', 'platform', 'system-time'];\n    }\n\n    attributeChangedCallback(_name: string, oldValue: string, newValue: string): void {\n        if (oldValue === newValue) return;\n        this.applyAttributes();\n\n        if (this.shadow) {\n            this.render();\n        }\n    }\n\n    private handleSubmit(event: Event): void {\n        event.preventDefault();\n        const form = event.target as HTMLFormElement & {\n            customerType?: { value: CustomerType };\n        };\n\n        const customerType = form.customerType?.value;\n        const url = this.buildUrlWithCustomerType(customerType);\n        window.location.assign(url);\n    }\n\n    private boundHandleSubmit = this.handleSubmit.bind(this);\n\n    connectedCallback(): void {\n        if (!this.shadow) {\n            this.shadow = this.attachShadow({ mode: 'closed' });\n        }\n\n        this.badgeState = { status: 'loading' };\n        this.applyAttributes();\n        this.render();\n        void this.loadJerseyStock();\n\n        this.shadow?.addEventListener('submit', this.boundHandleSubmit);\n    }\n\n    disconnectedCallback(): void {\n        this.shadow?.removeEventListener('submit', this.boundHandleSubmit);\n    }\n\n    private applyAttributes(): void {\n        const validator = (globalThis as Record<string, unknown>).C24Home as typeof C24Home | undefined;\n\n        if (typeof validator?.validateAttributes === 'function') {\n            const validated = validator.validateAttributes<HomeClientComponentProps>(this.attributes);\n            this.deviceOutput = validated.deviceOutput ?? this.deviceOutput;\n            this.platform = validated.platform ?? this.platform;\n        } else {\n            this.deviceOutput =\n                (this.getAttribute('device-output') as InternetTeaserClient['deviceOutput']) || 'desktop';\n            this.platform = (this.getAttribute('platform') as InternetTeaserClient['platform']) || 'web';\n        }\n\n        this.language = 'de-DE';\n        this.systemTime = Number(this.getAttribute('system-time') || 0);\n    }\n\n    private buildUrlWithCustomerType(customerType: CustomerType | undefined): string {\n        const url = new URL(PSS_URL_PROD);\n        const currentParams = new URLSearchParams(window.location.search);\n\n        // Copy existing params except customerType\n        currentParams.forEach((value, key) => {\n            if (key !== 'customerType') {\n                url.searchParams.set(key, value);\n            }\n        });\n\n        // Add new customerType if provided\n        if (customerType) {\n            url.searchParams.set('customerType', customerType);\n        }\n\n        url.searchParams.set('tid3', TID3_VALUE);\n\n        return url.toString();\n    }\n\n    private render(): void {\n        if (!this.shadow) return;\n\n        const cssStyles = this.getStyles();\n        const content = this.getContent();\n\n        this.shadow.innerHTML = `\n            <style>${cssStyles}</style>\n            ${content}\n        `;\n    }\n\n    private async loadJerseyStock(): Promise<void> {\n        const nextState = await fetchJerseyStock();\n        if (!this.isConnected) return;\n\n        this.badgeState = nextState;\n        this.updateBadge();\n    }\n\n    private updateBadge(): void {\n        const badge = this.shadow?.querySelector('.badge');\n        if (!badge) return;\n\n        if (this.badgeState.status === 'loading') {\n            badge.className = 'badge badge--loading c24-dsl-teaser-skeleton-pulse';\n            badge.textContent = '';\n            badge.setAttribute('aria-hidden', 'true');\n            return;\n        }\n\n        badge.className = 'badge';\n        badge.removeAttribute('aria-hidden');\n        badge.textContent = formatBadgeHtml(labels, this.badgeState);\n    }\n\n    private getContent(): string {\n        const href = this.buildUrlWithCustomerType(undefined);\n        const badgeMarkup =\n            this.badgeState.status === 'loading'\n                ? '<span class=\"badge badge--loading c24-dsl-teaser-skeleton-pulse\" aria-hidden=\"true\"></span>'\n                : `<span class=\"badge\">${formatBadgeHtml(labels, this.badgeState)}</span>`;\n\n        // Single HTML structure with CSS-based responsive behavior\n        // Link wraps only non-interactive content (for small screens)\n        // Form is separate to avoid HTML content model violation\n        return `\n            <div class=\"dsl-teaser-content\">\n                <a class=\"teaser-link\" href=\"${href}\" aria-label=\"${labels.linkLabel}\">\n                    <div class=\"container\">\n                        <div class=\"headlines\">\n                            <h2 class=\"title\">${labels.title}</h2>\n                            ${labels.headline2 ? `<p class=\"headline-2\">${labels.headline2}</p>` : ''}\n                            ${badgeMarkup}\n                        </div>\n                        <div class=\"cutout-image\">\n                            <img src=\"${jerseyCutout}\" alt=\"\" width=\"239\" height=\"266\" loading=\"lazy\" decoding=\"async\" />\n                        </div>\n                    </div>\n                </a>\n                <form>\n                    <fieldset>\n                        <legend class=\"sr-only\">${labels.legend}</legend>\n                        <div>\n                            <input type=\"radio\" name=\"customerType\" value=\"new\" id=\"new\" />\n                            <label for=\"new\">${labels.new}</label>\n                        </div>\n                        <div>\n                            <input type=\"radio\" name=\"customerType\" value=\"change\" id=\"change\" />\n                            <label for=\"change\">${labels.change}</label>\n                        </div>\n                        <div>\n                            <input type=\"radio\" name=\"customerType\" value=\"move\" id=\"move\" />\n                            <label for=\"move\">${labels.move}</label>\n                        </div>\n                    </fieldset>\n                    <button type=\"submit\">${labels.submit}</button>\n                </form>\n            </div>\n        `;\n    }\n\n    private getStyles(): string {\n        return styles;\n    }\n}\n\n// SlugTagMatchStep: tag must match manifest slug (dsl-internetminibox-logout)\ncustomElements.define('dsl-internetminibox-logout', InternetTeaserClient);\n"],"names":["PSS_URL_PROD","JERSEY_STOCK_API_URL","labels","linkLabel","title","headline2","legend","new","change","move","submit","badge","badgeFew","formatBadgeHtml","state","status","prefix","suffix","split","Intl","NumberFormat","format","value","InternetTeaserClient","HTMLElement","constructor","super","arguments","__publicField","this","handleSubmit","bind","observedAttributes","attributeChangedCallback","_name","oldValue","newValue","applyAttributes","shadow","render","event","_a","preventDefault","customerType","target","url","buildUrlWithCustomerType","window","location","assign","connectedCallback","attachShadow","mode","badgeState","loadJerseyStock","addEventListener","boundHandleSubmit","disconnectedCallback","removeEventListener","_b","validator","globalThis","C24Home","validateAttributes","validated","attributes","deviceOutput","platform","getAttribute","language","systemTime","Number","URL","URLSearchParams","search","forEach","key","searchParams","set","toString","cssStyles","getStyles","content","getContent","innerHTML","__async","nextState","response","fetch","signal","AbortSignal","timeout","ok","data","json","totalAmountLeft","e","fetchJerseyStock","isConnected","updateBadge","querySelector","className","textContent","setAttribute","removeAttribute","href","badgeMarkup","customElements","define"],"mappings":"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"}