Privacy Policy Generator
blocklegalCustomizable website privacy policy builder with third-party tracking disclosures, CCPA, and GDPR compliance clauses.
Installation
$pnpm dlx uipkge-ng@latest add privacy-policy-generator$npx uipkge-ng@latest add privacy-policy-generator$yarn dlx uipkge-ng@latest add privacy-policy-generator$bunx uipkge-ng@latest add privacy-policy-generatorManifest:
https://uipkge.dev/r/angular/privacy-policy-generator.jsonInstalls to:src/app/components/blocks/privacy-policy-generator/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
ProcessorMetainterface ProcessorMeta {
id: ProcessorKey
name: string
category: 'Analytics' | 'Payment' | 'Telemetry' | 'Infrastructure'
description: string
purpose: string
dataCollected: string
location: string
}ProcessorGroupinterface ProcessorGroup {
label: string
icon: unknown
items: ProcessorMeta[]
}Files installed (2)
src/app/components/blocks/privacy-policy-generator/privacy-policy-generator.component.ts63 kB
// Website privacy policy & compliance generator: company identity inputs, jurisdiction // checkboxes (GDPR / CCPA / PIPEDA / LGPD), third-party sub-processor switches, cookie // disclosure toggle, live document preview, and Markdown / HTML export. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core' import { BarChart3, Bug, Building2, Check, CheckCircle2, Code2, Cookie, Copy, CreditCard, Download, FileCode, LucideAngularModule, Mail, Scale, Server, ShieldCheck, Sliders, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component' import { UiInputComponent } from '@/ui/input/input.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiSwitchComponent } from '@/ui/switch/switch.component' export type JurisdictionKey = 'gdpr' | 'ccpa' | 'pipeda' | 'lgpd' export type ProcessorKey = 'ga4' | 'posthog' | 'plausible' | 'stripe' | 'paddle' | 'sentry' | 'datadog' | 'cloudflare' | 'aws' export interface ProcessorMeta { id: ProcessorKey name: string category: 'Analytics' | 'Payment' | 'Telemetry' | 'Infrastructure' description: string purpose: string dataCollected: string location: string } export interface ProcessorGroup { label: string icon: unknown items: ProcessorMeta[] } const ALL_PROCESSORS: ProcessorMeta[] = [ { id: 'ga4', name: 'Google Analytics 4', category: 'Analytics', description: 'Traffic telemetry, session counts, anonymized IP', purpose: 'Audience engagement & web traffic analytics', dataCollected: 'Anonymized IP, browser type, referral URLs, screen resolution', location: 'United States (Google LLC)', }, { id: 'posthog', name: 'PostHog Cloud', category: 'Analytics', description: 'Product telemetry, session recording, feature flags', purpose: 'User journey tracking and product telemetry', dataCollected: 'Interaction events, UI clickstreams, feature flag evaluation data', location: 'United States / EU (PostHog, Inc.)', }, { id: 'plausible', name: 'Plausible Analytics', category: 'Analytics', description: 'Privacy-first cookieless analytics', purpose: 'Aggregated website traffic metrics without personal identification', dataCollected: 'Page views, referrers, operating system name (no persistent cookies)', location: 'European Union (Estonia)', }, { id: 'stripe', name: 'Stripe, Inc.', category: 'Payment', description: 'PCI-DSS Level 1 tokenized billing and transactions', purpose: 'Payment gateway execution, fraud screening, and subscription billing', dataCollected: 'Card token, last 4 digits, billing address, customer ID', location: 'United States / Global (Stripe, Inc.)', }, { id: 'paddle', name: 'Paddle', category: 'Payment', description: 'Merchant of record, checkout & tax compliance', purpose: 'International checkout, VAT/sales tax management, and receipt delivery', dataCollected: 'Customer billing details, invoice records, transaction totals', location: 'United Kingdom / Global (Paddle.com Market Ltd.)', }, { id: 'sentry', name: 'Sentry', category: 'Telemetry', description: 'Application exceptions, crash stack traces, release health', purpose: 'Crash diagnosis, real-time error telemetry, and performance tracking', dataCollected: 'Error stack traces, browser environment, client runtime exceptions', location: 'United States (Functional Software, Inc.)', }, { id: 'datadog', name: 'Datadog', category: 'Telemetry', description: 'APM performance telemetry, log indexing', purpose: 'Edge latency monitoring, API endpoint tracing, system uptime auditing', dataCollected: 'Server request metrics, response latencies, infrastructure logs', location: 'United States (Datadog, Inc.)', }, { id: 'cloudflare', name: 'Cloudflare Pages', category: 'Infrastructure', description: 'Global CDN routing, edge caching, and DDoS mitigation', purpose: 'Edge website delivery, SSL/TLS encryption, and automated bot mitigation', dataCollected: 'Connecting IP address (for DDoS protection), HTTP request headers', location: 'Global Anycast Edge Network (Cloudflare, Inc.)', }, { id: 'aws', name: 'AWS S3', category: 'Infrastructure', description: 'Encrypted cloud object storage for assets and files', purpose: 'Scalable cloud object storage for media assets and encrypted backups', dataCollected: 'Uploaded user media assets, encrypted data archives', location: 'United States & EU Regions (Amazon Web Services, Inc.)', }, ] const PROCESSOR_GROUPS: ProcessorGroup[] = [ { label: 'Analytics & Metrics', icon: BarChart3, items: ALL_PROCESSORS.filter((p) => p.category === 'Analytics'), }, { label: 'Payment Processing', icon: CreditCard, items: ALL_PROCESSORS.filter((p) => p.category === 'Payment'), }, { label: 'Error Logging & Telemetry', icon: Bug, items: ALL_PROCESSORS.filter((p) => p.category === 'Telemetry'), }, { label: 'Infrastructure & Hosting', icon: Server, items: ALL_PROCESSORS.filter((p) => p.category === 'Infrastructure'), }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-privacy-policy-generator, [ui-privacy-policy-generator]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, UiCheckboxComponent, UiInputComponent, UiSeparatorComponent, UiSwitchComponent, ], template: ` <div data-slot="privacy-policy-generator" [class]="rootClass"> <!-- Header Section --> <div class="border-border flex flex-col gap-4 border-b pb-5 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1"> <div class="flex flex-wrap items-center gap-2.5"> <div class="bg-primary/10 text-primary border-primary/20 flex size-8 items-center justify-center rounded-md border" > <lucide-icon [img]="ShieldCheck" class="size-4" aria-hidden="true" /> </div> <h1 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl"> Website Privacy Policy & Compliance Generator </h1> <span ui-badge variant="outline" class="gap-1 px-2 py-0.5 font-mono text-xs"> <lucide-icon [img]="Scale" class="size-3" aria-hidden="true" /> {{ activeJurisdictionsCount() }} Jurisdictions </span> </div> <p class="text-muted-foreground text-xs sm:text-sm"> Generate compliant privacy disclosures tailored to your tech stack and analytics tools. </p> </div> <div class="flex flex-wrap items-center gap-2"> <button ui-button aria-label="Download attachment" variant="outline" size="sm" class="gap-1.5 text-xs" (click)="downloadPolicy('md')" > <lucide-icon [img]="Download" class="size-3.5" aria-hidden="true" /> <span>Export (.md)</span> </button> <button ui-button size="sm" class="gap-1.5 text-xs font-semibold" (click)="downloadPolicy('html')"> <lucide-icon [img]="FileCode" class="size-3.5" aria-hidden="true" /> <span>Export (.html)</span> </button> </div> </div> <!-- 2-Column Builder Workspace --> <div class="grid grid-cols-1 items-start gap-8 lg:grid-cols-12"> <!-- Left Column: Policy Configuration Sidebar --> <div class="space-y-6 lg:col-span-5"> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="pb-4"> <div class="flex items-center justify-between"> <div class="flex items-center gap-2"> <lucide-icon [img]="Sliders" class="text-primary size-4" aria-hidden="true" /> <h2 ui-card-title class="text-base font-semibold">Policy Configuration</h2> </div> <span ui-badge variant="secondary" class="text-xs" >{{ activeProcessorsList().length }} Sub-Processors</span > </div> <p ui-card-description class="text-xs"> Configure company identity, applicable legal regulations, and active data processors. </p> </div> <div ui-card-content class="space-y-6"> <!-- 1. Organization & Contact Details --> <div class="space-y-4"> <div class="flex items-center gap-2"> <lucide-icon [img]="Building2" class="text-muted-foreground size-4" aria-hidden="true" /> <span class="text-foreground text-xs font-semibold tracking-wider uppercase"> Entity & Contact Information </span> </div> <div class="space-y-3"> <div class="space-y-1.5"> <label for="company-name" class="text-foreground text-xs font-medium"> Company / App Name <span class="text-destructive">*</span> </label> <ui-input id="company-name" [value]="companyName()" (valueChange)="companyName.set($event)" placeholder="e.g. UIPKGE Technologies Inc." size="middle" /> </div> <div class="space-y-1.5"> <label for="contact-email" class="text-foreground text-xs font-medium"> Contact & DPO Email <span class="text-destructive">*</span> </label> <ui-input id="contact-email" type="email" [value]="contactEmail()" (valueChange)="contactEmail.set($event)" placeholder="e.g. [email protected]" size="middle" /> </div> <div class="grid grid-cols-1 gap-3 sm:grid-cols-2"> <div class="space-y-1.5"> <label for="website-url" class="text-foreground text-xs font-medium">Website URL</label> <ui-input id="website-url" [value]="websiteUrl()" (valueChange)="websiteUrl.set($event)" placeholder="https://uipkge.dev" size="middle" /> </div> <div class="space-y-1.5"> <label for="effective-date" class="text-foreground text-xs font-medium">Effective Date</label> <ui-input id="effective-date" [value]="effectiveDate()" (valueChange)="effectiveDate.set($event)" placeholder="August 21, 2026" size="middle" /> </div> </div> </div> </div> <ui-separator /> <!-- 2. Jurisdiction & Legal Regulations --> <div class="space-y-3"> <div class="flex items-center justify-between"> <div class="flex items-center gap-2"> <lucide-icon [img]="Scale" class="text-muted-foreground size-4" aria-hidden="true" /> <span class="text-foreground text-xs font-semibold tracking-wider uppercase"> Jurisdiction & Regulations </span> </div> <span class="text-muted-foreground text-xs">{{ activeJurisdictionsCount() }} selected</span> </div> <p class="text-muted-foreground text-xs"> Select applicable privacy frameworks to automatically insert required consumer rights clauses. </p> <div class="space-y-2.5 pt-1"> <div class="border-border bg-muted/20 hover:bg-muted/40 flex items-start gap-3 rounded-lg border p-3 transition-colors" > <ui-checkbox id="jur-gdpr" [checked]="jurisdictions().gdpr" (checkedChange)="setJurisdiction('gdpr', !!$event)" class="mt-0.5" /> <div class="space-y-0.5"> <label for="jur-gdpr" class="text-foreground cursor-pointer text-xs font-semibold select-none"> GDPR (EU) </label> <p class="text-muted-foreground text-xs leading-normal"> General Data Protection Regulation (Articles 13, 14, 15–22, DPO and supervisory authority clauses). </p> </div> </div> <div class="border-border bg-muted/20 hover:bg-muted/40 flex items-start gap-3 rounded-lg border p-3 transition-colors" > <ui-checkbox id="jur-ccpa" [checked]="jurisdictions().ccpa" (checkedChange)="setJurisdiction('ccpa', !!$event)" class="mt-0.5" /> <div class="space-y-0.5"> <label for="jur-ccpa" class="text-foreground cursor-pointer text-xs font-semibold select-none"> CCPA / CPRA (California) </label> <p class="text-muted-foreground text-xs leading-normal"> California Consumer Privacy Act (Right to Know, Delete, Correct, and “Do Not Sell My Info”). </p> </div> </div> <div class="border-border bg-muted/20 hover:bg-muted/40 flex items-start gap-3 rounded-lg border p-3 transition-colors" > <ui-checkbox id="jur-pipeda" [checked]="jurisdictions().pipeda" (checkedChange)="setJurisdiction('pipeda', !!$event)" class="mt-0.5" /> <div class="space-y-0.5"> <label for="jur-pipeda" class="text-foreground cursor-pointer text-xs font-semibold select-none"> PIPEDA (Canada) </label> <p class="text-muted-foreground text-xs leading-normal"> Personal Information Protection and Electronic Documents Act (10 Fair Information Principles). </p> </div> </div> <div class="border-border bg-muted/20 hover:bg-muted/40 flex items-start gap-3 rounded-lg border p-3 transition-colors" > <ui-checkbox id="jur-lgpd" [checked]="jurisdictions().lgpd" (checkedChange)="setJurisdiction('lgpd', !!$event)" class="mt-0.5" /> <div class="space-y-0.5"> <label for="jur-lgpd" class="text-foreground cursor-pointer text-xs font-semibold select-none"> LGPD (Brazil) </label> <p class="text-muted-foreground text-xs leading-normal"> Lei Geral de Proteção de Dados (Law No. 13.709/2018, ANPD compliance rights). </p> </div> </div> </div> </div> <ui-separator /> <!-- 3. Third-Party Data Processors --> <div class="space-y-4"> <div class="flex items-center justify-between"> <div class="flex items-center gap-2"> <lucide-icon [img]="Server" class="text-muted-foreground size-4" aria-hidden="true" /> <span class="text-foreground text-xs font-semibold tracking-wider uppercase"> Third-Party Processors </span> </div> <span ui-badge variant="outline" class="font-mono text-xs"> {{ activeProcessorsList().length }}/{{ allProcessors.length }} Active </span> </div> @for (group of processorGroups; track group.label) { <div class="space-y-2"> <div class="text-foreground flex items-center gap-1.5 text-xs font-medium"> <lucide-icon [img]="$any(group.icon)" class="text-primary size-3.5" aria-hidden="true" /> <span>{{ group.label }}</span> </div> <div class="divide-border/60 border-border bg-card divide-y rounded-lg border"> @for (proc of group.items; track proc.id) { <div class="flex items-center justify-between gap-3 p-2.5"> <div class="space-y-0.5"> <p class="text-foreground text-xs font-medium">{{ proc.name }}</p> <p class="text-muted-foreground text-xs">{{ proc.description }}</p> </div> <ui-switch [checked]="processors()[proc.id] ?? false" (checkedChange)="setProcessor(proc.id, !!$event)" [attr.aria-label]="proc.name" /> </div> } </div> </div> } </div> <ui-separator /> <!-- 4. Cookie Tracking Disclosure Switch --> <div class="border-border bg-muted/20 flex items-center justify-between gap-4 rounded-lg border p-3.5"> <div class="space-y-0.5"> <div class="flex items-center gap-2"> <lucide-icon [img]="Cookie" class="text-primary size-4" aria-hidden="true" /> <p class="text-foreground text-xs font-semibold">Cookie Tracking Disclosure</p> </div> <p class="text-muted-foreground text-xs leading-normal"> Enable if using session cookies, analytics tags, or local storage. When disabled, generates a strict cookieless guarantee. </p> </div> <ui-switch [checked]="cookieTracking()" (checkedChange)="cookieTracking.set(!!$event)" aria-label="Cookie Tracking Disclosure" /> </div> </div> </div> </div> <!-- Right Column: Live Policy Document Preview --> <div class="space-y-5 lg:sticky lg:top-6 lg:col-span-7"> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="pb-3"> <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1"> <div class="flex items-center gap-2"> <span class="relative flex size-2"> <span class="bg-success absolute inline-flex h-full w-full rounded-full opacity-75"></span> <span class="bg-success relative inline-flex size-2 rounded-full"></span> </span> <h2 ui-card-title class="text-base font-semibold">Live Policy Document Preview</h2> </div> <p ui-card-description class="text-xs"> Real-time generated legal disclosures tailored to your chosen configuration. </p> </div> <div class="flex items-center gap-2"> <button ui-button variant="outline" size="sm" class="gap-1.5 text-xs" (click)="copyPolicy('md')"> @if (copiedMd()) { <lucide-icon [img]="Check" class="text-success size-3.5" aria-hidden="true" /> } @else { <lucide-icon [img]="Copy" class="size-3.5" aria-hidden="true" /> } <span>{{ copiedMd() ? 'Copied MD!' : 'Copy Markdown' }}</span> </button> <button ui-button variant="outline" size="sm" class="gap-1.5 text-xs" (click)="copyPolicy('html')"> @if (copiedHtml()) { <lucide-icon [img]="Check" class="text-success size-3.5" aria-hidden="true" /> } @else { <lucide-icon [img]="Code2" class="size-3.5" aria-hidden="true" /> } <span>{{ copiedHtml() ? 'Copied HTML!' : 'Copy HTML' }}</span> </button> </div> </div> </div> <div ui-card-content class="space-y-4"> <div class="border-border/80 bg-background/50 text-foreground/90 max-h-[720px] space-y-6 overflow-y-auto rounded-lg border p-5 text-sm leading-relaxed sm:p-6" > <div class="border-border space-y-2 border-b pb-5"> <div class="flex flex-wrap items-center justify-between gap-2"> <span ui-badge variant="outline" class="font-mono text-xs">Official Disclosure</span> <span class="text-muted-foreground text-xs"> Last updated: {{ effectiveDate() || 'August 21, 2026' }} </span> </div> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl"> Privacy Policy for {{ companyName() || 'UIPKGE Technologies Inc.' }} </h2> <div class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-1 text-xs"> <span><strong>Effective Date:</strong> {{ effectiveDate() || 'August 21, 2026' }}</span> <span>•</span> <span><strong>Website:</strong> {{ websiteUrl() || 'https://uipkge.dev' }}</span> <span>•</span> <span><strong>Contact:</strong> {{ contactEmail() || '[email protected]' }}</span> </div> </div> <section class="space-y-2.5"> <h3 class="text-foreground text-base font-semibold">1. Introduction & Scope</h3> <p class="text-muted-foreground text-xs sm:text-sm"> {{ companyName() || 'UIPKGE Technologies Inc.' }} (“we”, “our”, or “us”) is dedicated to safeguarding your personal data and ensuring transparent privacy practices. This Privacy Policy governs your use of <a [href]="websiteUrl() || 'https://uipkge.dev'" class="text-primary underline">{{ websiteUrl() || 'https://uipkge.dev' }}</a> (the “Website”) and all associated services and developer APIs. </p> <p class="text-muted-foreground text-xs sm:text-sm"> By accessing our Website, you acknowledge that you have read, understood, and agreed to the practices described in this policy. </p> <div class="border-primary/20 bg-primary/5 rounded-lg border p-3.5"> <div class="flex items-start gap-3"> <lucide-icon [img]="ShieldCheck" class="text-primary mt-0.5 size-4 shrink-0" aria-hidden="true" /> <div class="space-y-1 text-xs sm:text-sm"> <p class="text-foreground font-semibold">Core Privacy Commitments</p> <ul class="text-muted-foreground space-y-1 text-xs"> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3.5 shrink-0" aria-hidden="true" /> <span>We never sell or rent your personal information to third-party data brokers.</span> </li> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3.5 shrink-0" aria-hidden="true" /> <span>Industry-standard encryption: TLS 1.3 in transit and AES-256 at rest.</span> </li> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3.5 shrink-0" aria-hidden="true" /> <span>Guaranteed rights to data access, export portability, and permanent erasure.</span> </li> </ul> </div> </div> </div> </section> <section class="space-y-2.5"> <h3 class="text-foreground text-base font-semibold">2. Personal Information We Collect</h3> <p class="text-muted-foreground text-xs sm:text-sm"> We gather only the minimum information necessary to operate our platform securely: </p> <div class="space-y-2"> <div class="border-border bg-card rounded-md border p-3"> <p class="text-foreground text-xs font-semibold">Account & Inquiries</p> <p class="text-muted-foreground mt-0.5 text-xs"> When contacting us via {{ contactEmail() || '[email protected]' }} or registering a workspace, we collect names, email addresses, and communication logs. </p> </div> @if (processors().stripe || processors().paddle) { <div class="border-border bg-card rounded-md border p-3"> <p class="text-foreground text-xs font-semibold">Billing & Payment Data</p> <p class="text-muted-foreground mt-0.5 text-xs"> Payment processing is handled via tokenized PCI-DSS Level 1 compliant gateways ({{ paymentProcessorNames() }}). Raw credit card numbers are never stored on our infrastructure. </p> </div> } @if ( processors().ga4 || processors().posthog || processors().plausible || processors().sentry || processors().datadog ) { <div class="border-border bg-card rounded-md border p-3"> <p class="text-foreground text-xs font-semibold">Telemetry & System Health</p> <p class="text-muted-foreground mt-0.5 text-xs"> Aggregated telemetry, client user-agents, IP addresses, and error stack traces to guarantee uptime and API responsiveness. </p> </div> } </div> </section> <section class="space-y-2.5"> <div class="flex items-center justify-between"> <h3 class="text-foreground text-base font-semibold">3. Third-Party Sub-Processors</h3> <span ui-badge variant="secondary" class="font-mono text-xs"> {{ activeProcessorsList().length }} Disclosed </span> </div> <p class="text-muted-foreground text-xs sm:text-sm"> To operate our platform efficiently, we engage vetted sub-processors governed by strict Data Processing Agreements (DPAs): </p> @if (activeProcessorsList().length > 0) { <div class="border-border overflow-hidden rounded-lg border"> <div class="overflow-x-auto"> <table class="w-full text-left text-xs"> <thead class="bg-muted/50 border-border border-b font-semibold"> <tr> <th scope="col" class="px-3.5 py-2.5">Sub-Processor</th> <th scope="col" class="px-3.5 py-2.5">Category</th> <th scope="col" class="px-3.5 py-2.5">Purpose</th> <th scope="col" class="px-3.5 py-2.5">Processing Location</th> </tr> </thead> <tbody class="divide-border divide-y"> @for (proc of activeProcessorsList(); track proc.id) { <tr class="hover:bg-muted/20 transition-colors"> <td class="text-foreground px-3.5 py-2.5 font-medium whitespace-nowrap"> {{ proc.name }} </td> <td class="text-muted-foreground px-3.5 py-2.5"> <span ui-badge variant="outline" class="px-1.5 py-0 text-xs">{{ proc.category }}</span> </td> <td class="text-muted-foreground px-3.5 py-2.5">{{ proc.purpose }}</td> <td class="text-muted-foreground px-3.5 py-2.5 whitespace-nowrap"> {{ proc.location }} </td> </tr> } </tbody> </table> </div> </div> } @else { <div class="border-border bg-muted/20 text-muted-foreground rounded-lg border p-4 text-center text-xs" > No external third-party sub-processors are currently enabled. </div> } </section> <section class="space-y-2.5"> <h3 class="text-foreground text-base font-semibold">4. Cookies & Tracking Technologies</h3> @if (cookieTracking()) { <div class="space-y-2"> <p class="text-muted-foreground text-xs sm:text-sm"> We use cookies and local storage tokens to provide essential authentication and analyze site traffic patterns: </p> <ul class="text-muted-foreground list-disc space-y-1 pl-5 text-xs sm:text-sm"> <li> <strong class="text-foreground font-medium">Strictly Necessary Cookies:</strong> Session tokens, theme preference states, and security anti-CSRF verification. </li> @if (processors().ga4 || processors().posthog) { <li> <strong class="text-foreground font-medium">Analytics Cookies:</strong> Aggregated interaction counters to assess documentation usability and improve performance. </li> } </ul> <p class="text-muted-foreground text-xs"> You can configure your browser to block or alert you about cookies. Disabling strictly necessary cookies may degrade website functionality. </p> </div> } @else { <div class="border-success/30 bg-success/5 rounded-lg border p-3.5"> <div class="flex items-start gap-2.5"> <lucide-icon [img]="CheckCircle2" class="text-success mt-0.5 size-4 shrink-0" aria-hidden="true" /> <div> <p class="text-foreground text-success text-xs font-semibold"> Zero-Tracking Cookies Guarantee </p> <p class="text-muted-foreground mt-0.5 text-xs"> This website operates on a 100% cookieless privacy architecture. We do not store persistent tracking cookies, cross-site beacons, or marketing identifiers on your device. </p> </div> </div> </div> } </section> <section class="space-y-3"> <h3 class="text-foreground text-base font-semibold"> 5. Statutory Compliance & Your Privacy Rights </h3> @if (jurisdictions().gdpr) { <div class="border-border bg-card space-y-2 rounded-lg border p-4"> <div class="flex items-center justify-between"> <h4 class="text-foreground text-xs font-semibold">5.1 European Union (GDPR - EU 2016/679)</h4> <span ui-badge variant="outline" class="text-xs">EU / EEA</span> </div> <p class="text-muted-foreground text-xs"> If you reside in the European Economic Area, you possess statutory rights under GDPR Articles 15–22: </p> <ul class="text-muted-foreground grid grid-cols-1 gap-1.5 text-xs sm:grid-cols-2"> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3 shrink-0" aria-hidden="true" /> <span><strong>Access (Art. 15):</strong> Obtain copy of personal data.</span> </li> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3 shrink-0" aria-hidden="true" /> <span><strong>Rectification (Art. 16):</strong> Correct inaccurate records.</span> </li> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3 shrink-0" aria-hidden="true" /> <span><strong>Erasure (Art. 17):</strong> Request deletion of data.</span> </li> <li class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-primary size-3 shrink-0" aria-hidden="true" /> <span><strong>Portability (Art. 20):</strong> Export data in JSON format.</span> </li> </ul> <p class="text-muted-foreground pt-1 text-xs"> You also have the statutory right to lodge a formal complaint with your local EU Supervisory Authority. </p> </div> } @if (jurisdictions().ccpa) { <div class="border-border bg-card space-y-2 rounded-lg border p-4"> <div class="flex items-center justify-between"> <h4 class="text-foreground text-xs font-semibold"> 5.2 California Privacy Rights (CCPA / CPRA) </h4> <span ui-badge variant="outline" class="text-xs">California</span> </div> <p class="text-muted-foreground text-xs"> California residents are entitled to notice at collection and the following statutory protections: </p> <ul class="text-muted-foreground space-y-1 text-xs"> <li> • <strong>Right to Know & Access:</strong> Request categories and specific pieces of data collected in the past 12 months. </li> <li> • <strong>Right to Delete:</strong> Request deletion of personal records collected from you. </li> <li> • <strong>Zero Data Selling:</strong> We do not sell or share personal information with third-party data brokers. </li> <li> • <strong>Non-Discrimination:</strong> We do not alter service availability or pricing when you exercise privacy rights. </li> </ul> </div> } @if (jurisdictions().pipeda) { <div class="border-border bg-card space-y-2 rounded-lg border p-4"> <div class="flex items-center justify-between"> <h4 class="text-foreground text-xs font-semibold">5.3 Canadian Privacy Rights (PIPEDA)</h4> <span ui-badge variant="outline" class="text-xs">Canada</span> </div> <p class="text-muted-foreground text-xs"> In compliance with Canada’s PIPEDA, we adhere to the 10 Fair Information Principles ensuring accountability, consent, purpose limitation, and safeguard standards. Inquiries can be escalated to the Office of the Privacy Commissioner of Canada (OPC). </p> </div> } @if (jurisdictions().lgpd) { <div class="border-border bg-card space-y-2 rounded-lg border p-4"> <div class="flex items-center justify-between"> <h4 class="text-foreground text-xs font-semibold">5.4 Brazilian Privacy Rights (LGPD)</h4> <span ui-badge variant="outline" class="text-xs">Brazil</span> </div> <p class="text-muted-foreground text-xs"> Under Law No. 13.709/2018 (LGPD), Brazilian data subjects may confirm processing activities, request anonymization or blocking of non-compliant data, and revoke consent through our Data Protection Officer. </p> </div> } @if ( !jurisdictions().gdpr && !jurisdictions().ccpa && !jurisdictions().pipeda && !jurisdictions().lgpd ) { <div class="border-border bg-muted/20 text-muted-foreground rounded-lg border p-4 text-xs"> We adhere to global privacy principles. You may request access to, correction of, or erasure of your personal data at any time by contacting our team. </div> } </section> <section class="space-y-2.5"> <h3 class="text-foreground text-base font-semibold">6. Data Security & Storage</h3> <p class="text-muted-foreground text-xs sm:text-sm"> We enforce technical and organizational safeguards including TLS 1.3 encryption in transit, AES-256 encryption at rest, segmented cloud VPC environments, and strict least-privilege role-based access controls (RBAC). Data is retained only for as long as necessary to fulfill operational commitments or statutory legal obligations. </p> </section> <section class="border-border space-y-2.5 border-t pt-5"> <h3 class="text-foreground text-base font-semibold">7. Contact Information & Data Protection</h3> <p class="text-muted-foreground text-xs sm:text-sm"> To submit a data request, exercise your statutory privacy rights, or ask questions regarding this policy, please contact: </p> <div class="border-border bg-card rounded-lg border p-4"> <p class="text-foreground text-xs font-semibold sm:text-sm"> {{ companyName() || 'UIPKGE Technologies Inc.' }} </p> <p class="text-muted-foreground mt-0.5 text-xs">Attn: Privacy & Data Protection Officer</p> <div class="mt-2 flex items-center gap-2"> <lucide-icon [img]="Mail" class="text-primary size-3.5 shrink-0" aria-hidden="true" /> <a [href]="'mailto:' + (contactEmail() || '[email protected]')" class="text-primary inline-flex min-h-6 items-center text-xs font-medium hover:underline" > {{ contactEmail() || '[email protected]' }} </a> </div> </div> </section> </div> </div> <div ui-card-footer class="border-border flex flex-wrap items-center justify-between gap-3 border-t p-4 text-xs" > <div class="text-muted-foreground flex items-center gap-2"> <lucide-icon [img]="ShieldCheck" class="text-primary size-3.5" aria-hidden="true" /> <span> Generated for <strong>{{ companyName() || 'UIPKGE Technologies Inc.' }}</strong> </span> </div> <div class="flex items-center gap-2"> <span ui-badge variant="outline" class="font-mono text-xs"> {{ activeProcessorsList().length }} Processors </span> <span ui-badge variant="secondary" class="font-mono text-xs"> {{ activeJurisdictionsCount() }} Jurisdictions </span> </div> </div> </div> </div> </div> </div> `, }) export class UiPrivacyPolicyGeneratorComponent { protected readonly BarChart3 = BarChart3 protected readonly Bug = Bug protected readonly Building2 = Building2 protected readonly Check = Check protected readonly CheckCircle2 = CheckCircle2 protected readonly Code2 = Code2 protected readonly Cookie = Cookie protected readonly Copy = Copy protected readonly CreditCard = CreditCard protected readonly Download = Download protected readonly FileCode = FileCode protected readonly Mail = Mail protected readonly Scale = Scale protected readonly Server = Server protected readonly ShieldCheck = ShieldCheck protected readonly Sliders = Sliders protected readonly allProcessors = ALL_PROCESSORS protected readonly processorGroups = PROCESSOR_GROUPS @Input() set initialCompanyName(v: string | undefined) { if (v !== undefined) this.companyName.set(v) } @Input() set initialContactEmail(v: string | undefined) { if (v !== undefined) this.contactEmail.set(v) } @Input() set initialWebsiteUrl(v: string | undefined) { if (v !== undefined) this.websiteUrl.set(v) } @Input() set initialEffectiveDate(v: string | undefined) { if (v !== undefined) this.effectiveDate.set(v) } @Input('class') className?: string readonly companyName = signal('UIPKGE Technologies Inc.') readonly contactEmail = signal('[email protected]') readonly websiteUrl = signal('https://uipkge.dev') readonly effectiveDate = signal('August 21, 2026') readonly jurisdictions = signal<Record<JurisdictionKey, boolean>>({ gdpr: true, ccpa: true, pipeda: false, lgpd: false, }) readonly processors = signal<Record<ProcessorKey, boolean>>({ ga4: true, posthog: false, plausible: true, stripe: true, paddle: false, sentry: true, datadog: false, cloudflare: true, aws: true, }) readonly cookieTracking = signal(true) readonly copiedMd = signal(false) readonly copiedHtml = signal(false) private copyMdTimer: ReturnType<typeof setTimeout> | undefined private copyHtmlTimer: ReturnType<typeof setTimeout> | undefined constructor() { inject(DestroyRef).onDestroy(() => { clearTimeout(this.copyMdTimer) clearTimeout(this.copyHtmlTimer) }) } readonly activeProcessorsList = computed(() => ALL_PROCESSORS.filter((p) => this.processors()[p.id])) readonly activeJurisdictionsCount = computed(() => { const j = this.jurisdictions() return (j.gdpr ? 1 : 0) + (j.ccpa ? 1 : 0) + (j.pipeda ? 1 : 0) + (j.lgpd ? 1 : 0) }) readonly paymentProcessorNames = computed(() => [this.processors().stripe ? 'Stripe' : '', this.processors().paddle ? 'Paddle' : ''].filter(Boolean).join(', '), ) get rootClass(): string { return cn('mx-auto w-full max-w-7xl space-y-6', this.className) } setJurisdiction(key: JurisdictionKey, value: boolean): void { this.jurisdictions.update((prev) => ({ ...prev, [key]: value })) } setProcessor(key: ProcessorKey, value: boolean): void { this.processors.update((prev) => ({ ...prev, [key]: value })) } generateMarkdown(): string { const company = this.companyName().trim() || 'Our Company' const email = this.contactEmail().trim() || '[email protected]' const website = this.websiteUrl().trim() || 'https://example.com' const date = this.effectiveDate().trim() || 'August 21, 2026' const jurisdictions = this.jurisdictions() const processors = this.processors() const activeProcessors = this.activeProcessorsList() const cookieTracking = this.cookieTracking() let md = `# Privacy Policy for ${company}\n\n` md += `**Effective Date:** ${date} \n` md += `**Last Updated:** ${date} \n` md += `**Website:** [${website}](${website})\n\n` md += `---\n\n` md += `## 1. Introduction & Overview\n\n` md += `${company} ("we", "our", or "us") respects your privacy and is committed to protecting your personal information. This Privacy Policy details our practices concerning the collection, use, storage, and disclosure of personal data when you visit [${website}](${website}) (the "Website") or use our services.\n\n` md += `By accessing or using our Website, you consent to the data collection and processing methods outlined in this policy.\n\n` md += `## 2. Personal Information We Collect\n\n` md += `We collect information necessary to operate our platform, maintain security, and fulfill legal requirements:\n\n` md += `- **Direct Inquiries & Communication:** When you reach out to us at ${email}, we retain your contact details and message contents.\n` if (processors.stripe || processors.paddle) { md += `- **Payment & Billing Data:** For paid services, payment transactions are processed securely through PCI-DSS Level 1 compliant processors. We do not store complete raw credit card numbers on our servers.\n` } if (processors.ga4 || processors.posthog || processors.plausible || processors.sentry || processors.datadog) { md += `- **Telemetry & Usage Information:** Diagnostic telemetry, operating system details, browser version, and aggregated usage metrics are collected to maintain service reliability.\n` } md += `\n` md += `## 3. Third-Party Sub-Processors & Data Sharing\n\n` if (activeProcessors.length > 0) { md += `We partner with trusted third-party sub-processors to power our application infrastructure. Each partner operates under a Data Processing Agreement (DPA) adhering to standard contractual clauses:\n\n` md += `| Sub-Processor | Category | Purpose | Processing Location |\n` md += `| :--- | :--- | :--- | :--- |\n` for (const p of activeProcessors) { md += `| **${p.name}** | ${p.category} | ${p.purpose} | ${p.location} |\n` } md += `\n` } else { md += `We do not share your personal information with external commercial sub-processors.\n\n` } md += `## 4. Cookies & Tracking Technologies\n\n` if (cookieTracking) { md += `We use cookies and equivalent browser storage mechanisms to ensure core site navigation and analyze traffic:\n\n` md += `- **Strictly Necessary Cookies:** Essential for page routing, authentication tokens, and user preference persistence.\n` if (processors.ga4 || processors.posthog) { md += `- **Performance & Analytics Cookies:** Help us analyze traffic patterns to optimize layout performance.\n` } md += `\nYou may configure your browser to reject cookies. However, disabling certain necessary cookies may affect website functionality.\n\n` } else { md += `**Zero-Tracking Cookies Guarantee:** This website operates on a privacy-first basis and does not place tracking cookies, advertising beacons, or third-party marketing identifiers on your device.\n\n` } md += `## 5. Compliance & Statutory Privacy Rights\n\n` if (jurisdictions.gdpr) { md += `### 5.1 European Union (GDPR - Regulation EU 2016/679)\n\n` md += `If you reside in the European Economic Area (EEA), you possess specific rights under the GDPR:\n` md += `- **Right of Access (Art. 15):** Request confirmation and a portable copy of personal records held.\n` md += `- **Right to Rectification (Art. 16):** Update or rectify inaccurate or incomplete personal records.\n` md += `- **Right to Erasure (Art. 17):** Request permanent erasure of personal data under statutory conditions.\n` md += `- **Right to Data Portability (Art. 20):** Receive your personal data in a structured, machine-readable format.\n` md += `- **Supervisory Authority:** You have the statutory right to lodge a complaint with an EU Data Protection Authority.\n\n` } if (jurisdictions.ccpa) { md += `### 5.2 California Privacy Rights (CCPA / CPRA)\n\n` md += `Under the California Consumer Privacy Act and California Privacy Rights Act, California consumers have the right to:\n` md += `- **Know and Access:** Request details regarding personal data categories collected over the past 12 months.\n` md += `- **Delete Personal Information:** Request deletion of personal data collected directly from you.\n` md += `- **Do Not Sell or Share My Information:** ${company} does not sell, rent, or trade your personal data to third-party brokers.\n` md += `- **Non-Discrimination:** You will not receive discriminatory pricing or service degradation for exercising your privacy rights.\n\n` } if (jurisdictions.pipeda) { md += `### 5.3 Canadian Privacy Rights (PIPEDA)\n\n` md += `In compliance with Canada's Personal Information Protection and Electronic Documents Act (PIPEDA), we adhere to the 10 Fair Information Principles ensuring accountability and purpose limitation. Complaints may be directed to the Office of the Privacy Commissioner of Canada (OPC).\n\n` } if (jurisdictions.lgpd) { md += `### 5.4 Brazilian Privacy Rights (LGPD - Law No. 13.709/2018)\n\n` md += `Under the Brazilian General Data Protection Law (LGPD), Brazilian data subjects may request confirmation of processing, anonymization of non-essential records, and revocation of consent. Oversight is provided by the ANPD.\n\n` } if (!jurisdictions.gdpr && !jurisdictions.ccpa && !jurisdictions.pipeda && !jurisdictions.lgpd) { md += `We honor international data privacy best practices. You may request access to, correction of, or deletion of your personal records at any time.\n\n` } md += `## 6. Data Security & Storage\n\n` md += `We implement defense-in-depth technical safeguards including TLS 1.3 encryption for data in transit, AES-256 encryption for data at rest, and strict role-based access control (RBAC). Data is retained only for the duration required to fulfill contractual and legal compliance duties.\n\n` md += `## 7. Contact Information & Privacy Inquiries\n\n` md += `If you have questions, inquiries, or wish to exercise your statutory rights, contact our Data Protection representative:\n\n` md += `- **Entity:** ${company}\n` md += `- **Privacy Email:** [${email}](mailto:${email})\n` md += `- **Website:** [${website}](${website})\n` return md } generateHtml(): string { const company = this.companyName().trim() || 'Our Company' const email = this.contactEmail().trim() || '[email protected]' const website = this.websiteUrl().trim() || 'https://example.com' const date = this.effectiveDate().trim() || 'August 21, 2026' const jurisdictions = this.jurisdictions() const processors = this.processors() const activeProcessors = this.activeProcessorsList() const cookieTracking = this.cookieTracking() let html = `<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta charset="UTF-8" />\n <title>Privacy Policy - ${company}</title>\n <style>\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; max-width: 800px; margin: 40px auto; padding: 0 20px; color: #1e293b; }\n h1 { font-size: 2rem; border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; }\n h2 { font-size: 1.35rem; margin-top: 2rem; }\n h3 { font-size: 1.1rem; }\n table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 0.9rem; }\n th, td { border: 1px solid #cbd5e1; padding: 8px 12px; text-align: left; }\n th { background: #f8fafc; }\n .meta { color: #64748b; font-size: 0.9rem; margin-bottom: 24px; }\n a { color: #2563eb; text-decoration: underline; }\n </style>\n</head>\n<body>\n` html += ` <h1>Privacy Policy</h1>\n` html += ` <div class="meta">\n` html += ` <strong>Effective Date:</strong> ${date} • <strong>Organization:</strong> ${company} • <strong>Website:</strong> <a href="${website}">${website}</a>\n` html += ` </div>\n\n` html += ` <h2>1. Introduction & Overview</h2>\n` html += ` <p>${company} ("we", "our", or "us") respects your privacy and is committed to protecting your personal information. This Privacy Policy details our practices concerning the collection, use, storage, and disclosure of personal data when you visit <a href="${website}">${website}</a> (the "Website") or use our services.</p>\n\n` html += ` <h2>2. Personal Information We Collect</h2>\n` html += ` <p>We collect information necessary to operate our platform, maintain security, and fulfill legal requirements:</p>\n` html += ` <ul>\n` html += ` <li><strong>Direct Inquiries & Communication:</strong> When you reach out to us at ${email}, we retain your contact details and message records.</li>\n` if (processors.stripe || processors.paddle) { html += ` <li><strong>Payment & Billing Data:</strong> Payment transactions are processed securely through PCI-DSS Level 1 compliant processors. Complete raw credit card numbers are never stored on our servers.</li>\n` } if (processors.ga4 || processors.posthog || processors.plausible || processors.sentry || processors.datadog) { html += ` <li><strong>Telemetry & Usage Information:</strong> Diagnostic telemetry, operating system details, browser version, and aggregated usage metrics are collected to maintain service reliability.</li>\n` } html += ` </ul>\n\n` html += ` <h2>3. Third-Party Sub-Processors & Data Sharing</h2>\n` if (activeProcessors.length > 0) { html += ` <p>We partner with trusted third-party sub-processors to power our application infrastructure under strict Data Processing Agreements (DPAs):</p>\n` html += ` <table>\n <thead>\n <tr>\n <th>Sub-Processor</th>\n <th>Category</th>\n <th>Purpose</th>\n <th>Processing Location</th>\n </tr>\n </thead>\n <tbody>\n` for (const p of activeProcessors) { html += ` <tr>\n <td><strong>${p.name}</strong></td>\n <td>${p.category}</td>\n <td>${p.purpose}</td>\n <td>${p.location}</td>\n </tr>\n` } html += ` </tbody>\n </table>\n\n` } else { html += ` <p>We do not share your personal information with external commercial sub-processors.</p>\n\n` } html += ` <h2>4. Cookies & Tracking Technologies</h2>\n` if (cookieTracking) { html += ` <p>We use cookies and equivalent browser storage mechanisms to ensure core site navigation and analyze traffic:</p>\n` html += ` <ul>\n <li><strong>Strictly Necessary Cookies:</strong> Essential for page routing, authentication tokens, and user preference persistence.</li>\n` if (processors.ga4 || processors.posthog) { html += ` <li><strong>Performance & Analytics Cookies:</strong> Help us analyze traffic patterns to optimize layout performance.</li>\n` } html += ` </ul>\n` html += ` <p>You may configure your browser to reject cookies. However, disabling certain necessary cookies may affect website functionality.</p>\n\n` } else { html += ` <p><strong>Zero-Tracking Cookies Guarantee:</strong> This website operates on a privacy-first basis and does not place tracking cookies, advertising beacons, or third-party marketing identifiers on your device.</p>\n\n` } html += ` <h2>5. Compliance & Statutory Privacy Rights</h2>\n` if (jurisdictions.gdpr) { html += ` <h3>5.1 European Union (GDPR - Regulation EU 2016/679)</h3>\n` html += ` <p>If you reside in the European Economic Area (EEA), you possess rights under the GDPR including Right of Access (Art. 15), Right to Rectification (Art. 16), Right to Erasure (Art. 17), and Right to Data Portability (Art. 20). You may also lodge a complaint with your local EU Data Protection Authority.</p>\n` } if (jurisdictions.ccpa) { html += ` <h3>5.2 California Privacy Rights (CCPA / CPRA)</h3>\n` html += ` <p>Under the California Consumer Privacy Act (CCPA/CPRA), California residents may request details regarding personal data collected, request erasure of personal data, and are guaranteed that ${company} does not sell personal data to third parties.</p>\n` } if (jurisdictions.pipeda) { html += ` <h3>5.3 Canadian Privacy Rights (PIPEDA)</h3>\n` html += ` <p>In compliance with Canada's PIPEDA, we adhere to the 10 Fair Information Principles ensuring accountability, consent, and purpose limitation.</p>\n` } if (jurisdictions.lgpd) { html += ` <h3>5.4 Brazilian Privacy Rights (LGPD)</h3>\n` html += ` <p>Under the Brazilian General Data Protection Law (LGPD), Brazilian data subjects may request confirmation of processing, anonymization of non-essential records, and revocation of consent.</p>\n` } if (!jurisdictions.gdpr && !jurisdictions.ccpa && !jurisdictions.pipeda && !jurisdictions.lgpd) { html += ` <p>We honor international data privacy best practices. You may request access to, correction of, or deletion of your personal records at any time.</p>\n` } html += `\n <h2>6. Data Security & Storage</h2>\n` html += ` <p>We implement defense-in-depth technical safeguards including TLS 1.3 encryption for data in transit, AES-256 encryption for data at rest, and strict role-based access control (RBAC).</p>\n\n` html += ` <h2>7. Contact Information & Privacy Inquiries</h2>\n` html += ` <p>For any questions or statutory inquiries, contact our Data Protection team at <a href="mailto:${email}">${email}</a>.</p>\n` html += `</body>\n</html>` return html } downloadPolicy(format: 'md' | 'html'): void { const content = format === 'md' ? this.generateMarkdown() : this.generateHtml() const mimeType = format === 'md' ? 'text/markdown;charset=utf-8' : 'text/html;charset=utf-8' const ext = format === 'md' ? 'md' : 'html' const safeName = (this.companyName().trim() || 'uipkge') .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)/g, '') const filename = `${safeName}-privacy-policy.${ext}` const blob = new Blob([content], { type: mimeType }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = filename document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) } async copyPolicy(format: 'md' | 'html'): Promise<void> { const content = format === 'md' ? this.generateMarkdown() : this.generateHtml() try { const nav = navigator as Navigator & { clipboard?: { writeText(text: string): Promise<void> } } if (nav.clipboard && window.isSecureContext) { await nav.clipboard.writeText(content) } else { const textarea = document.createElement('textarea') textarea.value = content textarea.style.position = 'fixed' textarea.style.opacity = '0' document.body.appendChild(textarea) textarea.focus() textarea.select() document.execCommand('copy') document.body.removeChild(textarea) } if (format === 'md') { this.copiedMd.set(true) clearTimeout(this.copyMdTimer) this.copyMdTimer = setTimeout(() => this.copiedMd.set(false), 2000) } else { this.copiedHtml.set(true) clearTimeout(this.copyHtmlTimer) this.copyHtmlTimer = setTimeout(() => this.copiedHtml.set(false), 2000) } } catch (err) { console.error('Failed to copy policy:', err) } } }src/app/components/blocks/privacy-policy-generator/index.ts0.2 kB
export { UiPrivacyPolicyGeneratorComponent, type JurisdictionKey, type ProcessorKey, type ProcessorMeta, type ProcessorGroup, } from './privacy-policy-generator.component'
Raw manifest:https://uipkge.dev/r/angular/privacy-policy-generator.json