Files
breakpilot-compliance/breakpilot-compliance-sdk/packages/vanilla/src/web-components/dsr-portal.ts
Sharang Parnerkar 9ecd3b2d84 refactor(sdk): split hooks, dsr-portal, provider, sync approaching 500 LOC
All four files split into focused sibling modules so every file lands
comfortably under the 300-LOC soft target (hard cap 500):

  hooks.ts (474→43)  → hooks-core / hooks-dsgvo / hooks-compliance
                        hooks-rag-security / hooks-ui
  dsr-portal.ts (464→129) → dsr-portal-translations / dsr-portal-render
  provider.tsx (462→247)  → provider-effects / provider-callbacks
  sync.ts (435→299)       → sync-storage / sync-conflict

Zero behaviour changes. All public APIs remain importable from the
original paths (hooks.ts re-exports every hook, provider.tsx keeps all
named exports, sync.ts preserves StateSyncManager + factory).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 08:40:20 +02:00

130 lines
3.6 KiB
TypeScript

/**
* <breakpilot-dsr-portal> Web Component
*
* Data Subject Request Portal for GDPR rights
*
* Usage:
* <breakpilot-dsr-portal
* api-key="pk_live_xxx"
* language="de">
* </breakpilot-dsr-portal>
*
* Split: translations → dsr-portal-translations.ts
* styles + HTML builders → dsr-portal-render.ts
*/
import type { DSRRequestType } from '@breakpilot/compliance-sdk-types'
import { BreakPilotElement } from './base'
import { DSR_TRANSLATIONS, type DSRLanguage } from './dsr-portal-translations'
import { DSR_PORTAL_STYLES, buildFormHtml, buildSuccessHtml } from './dsr-portal-render'
export class DSRPortalElement extends BreakPilotElement {
static get observedAttributes(): string[] {
return ['api-key', 'api-endpoint', 'tenant-id', 'language']
}
private selectedType: DSRRequestType | null = null
private name = ''
private email = ''
private additionalInfo = ''
private isSubmitting = false
private isSubmitted = false
private error: string | null = null
private get language(): DSRLanguage {
return (this.getAttribute('language') as DSRLanguage) || 'de'
}
private get t() {
return DSR_TRANSLATIONS[this.language]
}
private handleTypeSelect = (type: DSRRequestType): void => {
this.selectedType = type
this.render()
}
private handleSubmit = async (e: Event): Promise<void> => {
e.preventDefault()
if (!this.selectedType || !this.email || !this.name) {
return
}
this.isSubmitting = true
this.error = null
this.render()
try {
// In a real implementation, this would use the client
// For now, we simulate a successful submission
await new Promise(resolve => setTimeout(resolve, 1000))
this.emit('dsr-submitted', {
type: this.selectedType,
email: this.email,
name: this.name,
additionalInfo: this.additionalInfo,
})
this.isSubmitted = true
} catch (err) {
this.error = err instanceof Error ? err.message : 'Ein Fehler ist aufgetreten'
} finally {
this.isSubmitting = false
this.render()
}
}
protected render(): void {
if (this.isSubmitted) {
this.shadow.innerHTML = buildSuccessHtml(DSR_PORTAL_STYLES, this.t, this.email)
} else {
this.renderForm()
}
}
private renderForm(): void {
this.shadow.innerHTML = buildFormHtml(DSR_PORTAL_STYLES, {
t: this.t,
selectedType: this.selectedType,
name: this.name,
email: this.email,
additionalInfo: this.additionalInfo,
isSubmitting: this.isSubmitting,
error: this.error,
})
// Bind events
const form = this.shadow.getElementById('dsr-form') as HTMLFormElement
form.onsubmit = this.handleSubmit
const nameInput = this.shadow.getElementById('name-input') as HTMLInputElement
nameInput.oninput = e => {
this.name = (e.target as HTMLInputElement).value
}
const emailInput = this.shadow.getElementById('email-input') as HTMLInputElement
emailInput.oninput = e => {
this.email = (e.target as HTMLInputElement).value
}
const infoInput = this.shadow.getElementById('info-input') as HTMLTextAreaElement
infoInput.oninput = e => {
this.additionalInfo = (e.target as HTMLTextAreaElement).value
}
// Bind radio buttons
this.shadow.querySelectorAll<HTMLInputElement>('input[name="dsrType"]').forEach(radio => {
radio.onchange = () => {
this.handleTypeSelect(radio.value as DSRRequestType)
}
})
}
}
// Register the custom element
if (typeof customElements !== 'undefined') {
customElements.define('breakpilot-dsr-portal', DSRPortalElement)
}