Legal Policy Page
blocklegalPrivacy Policy and legal documentation layout with top metadata header, version switcher, sticky table of contents navigation, and structured compliance sections for GDPR and CCPA.
Installation
$pnpm dlx uipkge-ng@latest add legal-policy-page$npx uipkge-ng@latest add legal-policy-page$yarn dlx uipkge-ng@latest add legal-policy-page$bunx uipkge-ng@latest add legal-policy-pageManifest:
https://uipkge.dev/r/angular/legal-policy-page.jsonInstalls to:src/app/components/blocks/legal-policy-page/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.
PolicyTocIteminterface PolicyTocItem {
id: string
label: string
}Files installed (2)
src/app/components/blocks/legal-policy-page/legal-policy-page.component.ts25.6 kB
// Privacy Policy and legal documentation layout with top metadata header, version switcher, // sticky table of contents navigation, and structured compliance sections for GDPR and CCPA. // Port of the React block 1:1. import { AfterViewInit, ChangeDetectionStrategy, Component, Input, OnDestroy, signal } from '@angular/core' import { CheckCircle2, FileText, LucideAngularModule, Mail, MapPin, Pencil, Printer, ShieldCheck, Sliders, Trash2, } 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, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, } from '@/ui/select/select.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' export interface PolicyTocItem { id: string label: string } export const tableOfContents: PolicyTocItem[] = [ { id: 'introduction', label: '1. Introduction & Scope' }, { id: 'information-collected', label: '2. Information We Collect' }, { id: 'data-usage', label: '3. How We Use Your Data' }, { id: 'third-parties', label: '4. Data Sharing & Third Parties' }, { id: 'your-rights', label: '5. Your Rights & Choices' }, { id: 'contact-dpo', label: '6. Contact & Data Protection Officer' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-legal-policy-page, [ui-legal-policy-page]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, UiSeparatorComponent, ], template: ` <div data-slot="legal-policy-page" [class]="rootClass"> <div class="mx-auto max-w-6xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8"> <!-- Top Header --> <div class="flex flex-col gap-6 sm:flex-row sm:items-end sm:justify-between"> <div class="space-y-2"> <span ui-badge variant="outline" class="font-mono text-xs">Legal & Compliance</span> <h1 class="text-3xl font-bold tracking-tight sm:text-4xl">Privacy Policy</h1> <p class="text-muted-foreground text-sm"> Effective date: August 15, 2026 • Current version: {{ selectedVersion() }} </p> </div> <div class="flex flex-wrap items-center gap-3"> <ui-select [value]="selectedVersion()" (valueChange)="selectedVersion.set($event)"> <button ui-select-trigger class="w-40 text-xs" aria-label="Select policy version"> <ui-select-value placeholder="Select version" /> </button> <ui-select-content> <ui-select-item value="v2.4">v2.4 (Current)</ui-select-item> <ui-select-item value="v2.3">v2.3 (July 2025)</ui-select-item> <ui-select-item value="v2.0">v2.0 (Jan 2025)</ui-select-item> </ui-select-content> </ui-select> <button ui-button variant="outline" size="sm" class="gap-1.5" (click)="handlePrint()"> <lucide-icon [img]="Printer" class="size-4" /> <span>Print / PDF</span> </button> </div> </div> <div ui-separator class="my-8"></div> <!-- 2-Column Documentation Layout --> <div class="grid grid-cols-1 gap-10 lg:grid-cols-12 lg:gap-12"> <!-- Sticky Sidebar Navigation --> <aside class="space-y-6 lg:sticky lg:top-8 lg:col-span-4 lg:self-start xl:col-span-3"> <div class="border-border bg-card rounded-lg border p-4 shadow-xs"> <p class="text-muted-foreground px-2 pb-2 text-xs font-semibold tracking-wider uppercase"> Table of Contents </p> <nav aria-label="Table of contents"> <ul class="space-y-1"> @for (item of toc; track item.id) { <li> <button type="button" [class]="tocBtnClass(item.id)" (click)="scrollToSection(item.id)"> {{ item.label }} </button> </li> } </ul> </nav> </div> <div ui-card class="bg-muted/30 border-border"> <div ui-card-header class="p-4 pb-2"> <div class="flex items-center gap-2"> <lucide-icon [img]="ShieldCheck" class="text-primary size-4" /> <h3 ui-card-title class="text-xs font-semibold">Privacy Questions?</h3> </div> </div> <div ui-card-content class="text-muted-foreground space-y-2 p-4 pt-0 text-xs"> <p>Need clarification on our data handling practices or DPAs?</p> <a href="mailto:privacy@acmecorp.com" class="text-primary flex min-h-6 items-center font-medium hover:underline" > privacy@acmecorp.com </a> </div> </div> </aside> <!-- Right Main Content --> <main class="space-y-12 lg:col-span-8 xl:col-span-9"> <section id="introduction" class="scroll-mt-8 space-y-4"> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">1. Introduction & Scope</h2> <p class="text-muted-foreground text-sm leading-relaxed"> At Acme Corp (“we”, “our”, or “us”), safeguarding your privacy and maintaining customer trust is core to everything we build. This Privacy Policy sets forth our policies regarding the collection, processing, storage, and disclosure of personal data when you access or use our developer platform, APIs, web interfaces, and documentation (collectively, the “Services”). </p> <p class="text-muted-foreground text-sm leading-relaxed"> This policy applies to all registered workspace members, API consumers, and website visitors. By accessing our Services, you acknowledge that you have read and understood this Privacy Policy. </p> <div class="border-primary/20 bg-primary/5 rounded-lg border p-4 sm:p-5"> <div class="flex items-start gap-3"> <div class="bg-primary/10 text-primary mt-0.5 rounded-md p-2"> <lucide-icon [img]="ShieldCheck" class="size-5 shrink-0" /> </div> <div class="space-y-2"> <h3 class="text-foreground text-sm font-semibold">Our Core Privacy Commitments</h3> <ul class="text-muted-foreground space-y-1.5 text-xs sm:text-sm"> <li class="flex items-center gap-2"> <lucide-icon [img]="CheckCircle2" class="text-primary size-4 shrink-0" /> <span> <strong>Zero Data Monetization:</strong> We never sell, rent, or trade your personal data to third-party brokers or advertisers. </span> </li> <li class="flex items-center gap-2"> <lucide-icon [img]="CheckCircle2" class="text-primary size-4 shrink-0" /> <span> <strong>End-to-End Encryption:</strong> Customer databases and authentication keys are protected with AES-256 at rest and TLS 1.3 in transit. </span> </li> <li class="flex items-center gap-2"> <lucide-icon [img]="CheckCircle2" class="text-primary size-4 shrink-0" /> <span> <strong>Portability & Erasure:</strong> Export your data archive in standard JSON or trigger permanent account deletion at any time. </span> </li> </ul> </div> </div> </div> </section> <section id="information-collected" class="scroll-mt-8 space-y-4"> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">2. Information We Collect</h2> <p class="text-muted-foreground text-sm leading-relaxed"> We collect information strictly necessary to fulfill our service commitments, secure account access, and meet statutory compliance obligations. </p> <div class="border-border overflow-hidden rounded-lg border"> <div class="overflow-x-auto"> <table class="w-full text-left text-xs sm:text-sm"> <thead class="bg-muted/50 border-border border-b font-semibold"> <tr> <th scope="col" class="px-4 py-3">Data Category</th> <th scope="col" class="px-4 py-3">Collected Information</th> <th scope="col" class="px-4 py-3">Purpose & Legal Basis</th> </tr> </thead> <tbody class="divide-border divide-y"> <tr class="hover:bg-muted/30 transition-colors"> <td class="text-foreground px-4 py-3 align-top font-medium whitespace-nowrap"> Account Identity </td> <td class="text-muted-foreground px-4 py-3 align-top"> Full name, email address, password hash, organization name, role, API keys. </td> <td class="text-muted-foreground px-4 py-3 align-top"> Authentication and contractual service delivery (GDPR Art. 6(1)(b)). </td> </tr> <tr class="hover:bg-muted/30 transition-colors"> <td class="text-foreground px-4 py-3 align-top font-medium whitespace-nowrap"> Billing & Payments </td> <td class="text-muted-foreground px-4 py-3 align-top"> Billing address, tax ID, payment transaction tokens (card numbers tokenized by Stripe). </td> <td class="text-muted-foreground px-4 py-3 align-top"> Payment processing, invoice generation, tax compliance (GDPR Art. 6(1)(c)). </td> </tr> <tr class="hover:bg-muted/30 transition-colors"> <td class="text-foreground px-4 py-3 align-top font-medium whitespace-nowrap"> Usage & Telemetry </td> <td class="text-muted-foreground px-4 py-3 align-top"> IP address, client user agent, API endpoint invocations, response latencies, error logs. </td> <td class="text-muted-foreground px-4 py-3 align-top"> Security monitoring, rate limiting, and system stability (GDPR Art. 6(1)(f)). </td> </tr> <tr class="hover:bg-muted/30 transition-colors"> <td class="text-foreground px-4 py-3 align-top font-medium whitespace-nowrap"> Cookies & Storage </td> <td class="text-muted-foreground px-4 py-3 align-top"> Session authentication tokens, theme preference state, consent audit records. </td> <td class="text-muted-foreground px-4 py-3 align-top"> Session persistence, interface customization, and consent tracking. </td> </tr> </tbody> </table> </div> </div> </section> <section id="data-usage" class="scroll-mt-8 space-y-4"> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">3. How We Use Your Data</h2> <p class="text-muted-foreground text-sm leading-relaxed"> We process personal data only when an explicit legal basis exists under applicable regulations. Specifically, your information is used for: </p> <ul class="grid grid-cols-1 gap-3 sm:grid-cols-2"> @for (use of dataUses; track use.title) { <li class="border-border bg-card flex items-start gap-3 rounded-lg border p-3.5 shadow-xs"> <lucide-icon [img]="CheckCircle2" class="text-primary mt-0.5 size-4 shrink-0" /> <div class="text-xs sm:text-sm"> <strong class="text-foreground font-semibold">{{ use.title }}:</strong> <p class="text-muted-foreground mt-0.5">{{ use.body }}</p> </div> </li> } </ul> </section> <section id="third-parties" class="scroll-mt-8 space-y-4"> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl"> 4. Data Sharing & Third Parties (Sub-Processors) </h2> <p class="text-muted-foreground text-sm leading-relaxed"> We do not sell user data. To maintain high-availability infrastructure, we engage vetted sub-processors bound by Data Processing Agreements (DPAs) incorporating standard contractual clauses (SCCs). </p> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> @for (sub of subProcessors; track sub.name) { <div ui-card class="border-border bg-card"> <div ui-card-header class="p-4 pb-2"> <div class="flex items-center justify-between"> <h3 ui-card-title class="text-sm font-semibold">{{ sub.name }}</h3> <span ui-badge variant="secondary" class="text-xs font-normal">{{ sub.tag }}</span> </div> <p ui-card-description class="text-xs">{{ sub.blurb }}</p> </div> <div ui-card-content class="text-muted-foreground space-y-1 p-4 pt-1 text-xs"> <div class="flex justify-between"> <span class="text-foreground font-medium">Location:</span> <span>{{ sub.location }}</span> </div> <div class="flex justify-between"> <span class="text-foreground font-medium">Data Shared:</span> <span>{{ sub.shared }}</span> </div> </div> </div> } </div> </section> <section id="your-rights" class="scroll-mt-8 space-y-4"> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">5. Your Rights & Choices</h2> <p class="text-muted-foreground text-sm leading-relaxed"> Under GDPR (Articles 15–22), CCPA/CPRA, and related global regulations, you possess specific enforceable rights regarding your personal records. </p> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div ui-card class="border-border bg-card"> <div ui-card-header class="p-4 pb-2"> <div class="flex items-center gap-2"> <lucide-icon [img]="FileText" class="text-primary size-4" /> <h3 ui-card-title class="text-sm font-semibold">Right to Access & Portability</h3> </div> </div> <div ui-card-content class="text-muted-foreground p-4 pt-1 text-xs sm:text-sm"> Request a complete machine-readable copy (JSON) of your account data, logs, and billing history. </div> </div> <div ui-card class="border-border bg-card"> <div ui-card-header class="p-4 pb-2"> <div class="flex items-center gap-2"> <lucide-icon [img]="Pencil" class="text-primary size-4" /> <h3 ui-card-title class="text-sm font-semibold">Right to Rectification</h3> </div> </div> <div ui-card-content class="text-muted-foreground p-4 pt-1 text-xs sm:text-sm"> Update or correct inaccurate profile details, billing addresses, and organization contacts in settings. </div> </div> <div ui-card class="border-border bg-card"> <div ui-card-header class="p-4 pb-2"> <div class="flex items-center gap-2"> <lucide-icon [img]="Trash2" class="text-destructive size-4" /> <h3 ui-card-title class="text-sm font-semibold">Right to Erasure (To Be Forgotten)</h3> </div> </div> <div ui-card-content class="text-muted-foreground p-4 pt-1 text-xs sm:text-sm"> Request permanent deletion of your account and personal identifiers within 30 calendar days. </div> </div> <div ui-card class="border-border bg-card"> <div ui-card-header class="p-4 pb-2"> <div class="flex items-center gap-2"> <lucide-icon [img]="Sliders" class="text-primary size-4" /> <h3 ui-card-title class="text-sm font-semibold">Right to Restrict & Opt-Out</h3> </div> </div> <div ui-card-content class="text-muted-foreground p-4 pt-1 text-xs sm:text-sm"> Opt out of non-essential telemetry, unsubscribe from product updates, and configure cookie preferences. </div> </div> </div> </section> <section id="contact-dpo" class="scroll-mt-8 space-y-4"> <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl"> 6. Contact & Data Protection Officer </h2> <p class="text-muted-foreground text-sm leading-relaxed"> If you have inquiries, complaints, or formal requests regarding this policy, contact our designated Data Protection Officer. </p> <div ui-card class="border-border bg-card"> <div ui-card-header class="p-5 pb-3"> <div class="flex flex-wrap items-center justify-between gap-2"> <h3 ui-card-title class="text-base font-semibold">Acme Data Protection Officer</h3> <span ui-badge variant="outline" class="font-mono text-xs">30-Day Response SLA</span> </div> <p ui-card-description class="text-xs sm:text-sm"> Designated compliance contact under GDPR Article 37 and international privacy statutes. </p> </div> <div ui-card-content class="text-muted-foreground space-y-4 p-5 pt-0 text-xs sm:text-sm"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="flex items-start gap-2.5"> <lucide-icon [img]="Mail" class="text-muted-foreground mt-0.5 size-4 shrink-0" /> <div> <p class="text-foreground font-medium">Email Inquiries</p> <a href="mailto:dpo@acmecorp.com" class="text-primary inline-flex min-h-6 items-center font-medium hover:underline" > dpo@acmecorp.com </a> <p class="text-muted-foreground mt-0.5 text-xs">PGP key available upon request</p> </div> </div> <div class="flex items-start gap-2.5"> <lucide-icon [img]="MapPin" class="text-muted-foreground mt-0.5 size-4 shrink-0" /> <div> <p class="text-foreground font-medium">Physical Address</p> <p>Acme Corporation</p> <p>Attn: Legal & Compliance</p> <p>500 Market St, Suite 300</p> <p>San Francisco, CA 94105, USA</p> </div> </div> </div> <div ui-separator></div> <p class="text-muted-foreground text-xs leading-relaxed"> If you reside in the European Economic Area (EEA) and believe that our data processing infringes applicable data protection law, you have the statutory right to lodge a complaint with your local supervisory authority. </p> </div> </div> </section> </main> </div> </div> </div> `, }) export class UiLegalPolicyPageComponent implements AfterViewInit, OnDestroy { protected readonly CheckCircle2 = CheckCircle2 protected readonly FileText = FileText protected readonly Mail = Mail protected readonly MapPin = MapPin protected readonly Pencil = Pencil protected readonly Printer = Printer protected readonly ShieldCheck = ShieldCheck protected readonly Sliders = Sliders protected readonly Trash2 = Trash2 protected readonly toc = tableOfContents protected readonly dataUses = [ { title: 'Service Operation', body: 'Managing user authentication, executing API workloads, and delivering developer tooling.', }, { title: 'Security & Fraud Prevention', body: 'Detecting suspicious sign-ins, preventing credential abuse, and enforcing rate limits.', }, { title: 'Billing & Invoicing', body: 'Processing subscription payments, issuing VAT invoices, and maintaining financial records.', }, { title: 'Transactional Communications', body: 'Sending password reset links, security audit notices, and critical maintenance alerts.', }, { title: 'Performance Analytics', body: 'Analyzing aggregated telemetry to reduce edge query latency and improve uptime.', }, { title: 'Statutory Compliance', body: 'Fulfilling mandatory tax disclosures, court orders, and regulatory audit standards.', }, ] protected readonly subProcessors = [ { name: 'Amazon Web Services', tag: 'Cloud Hosting', blurb: 'Database storage and edge computation', location: 'US & EU (Frankfurt)', shared: 'Encrypted databases, backups', }, { name: 'Stripe, Inc.', tag: 'Payments', blurb: 'PCI-DSS Level 1 payment gateway', location: 'United States / Global', shared: 'Billing info, payment tokens', }, { name: 'Postmark', tag: 'Transactional Email', blurb: 'System emails & security alerts', location: 'United States', shared: 'Recipient email, delivery logs', }, { name: 'Sentry / Datadog', tag: 'Observability', blurb: 'Application monitoring & crash tracing', location: 'US & European Union', shared: 'Anonymized crash traces, IP hash', }, ] @Input('class') className?: string readonly activeSection = signal('introduction') readonly selectedVersion = signal('v2.4') private observer: IntersectionObserver | null = null get rootClass(): string { return cn('bg-background text-foreground w-full', this.className) } tocBtnClass(id: string): string { return cn( 'focus-visible:ring-ring w-full rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none', this.activeSection() === id ? 'bg-primary/10 text-primary font-semibold' : 'text-muted-foreground hover:bg-muted/60 hover:text-foreground', ) } scrollToSection(id: string): void { this.activeSection.set(id) const el = document.getElementById(id) as HTMLElement | null el?.scrollIntoView?.({ behavior: 'smooth' }) } handlePrint(): void { if (typeof window !== 'undefined') window.print() } ngAfterViewInit(): void { if (typeof window === 'undefined' || !('IntersectionObserver' in window)) return this.observer = new IntersectionObserver( (entries) => { for (const entry of entries) { if (entry.isIntersecting) this.activeSection.set(entry.target.id) } }, { rootMargin: '-10% 0px -70% 0px' }, ) for (const item of tableOfContents) { const el = document.getElementById(item.id) if (el) this.observer.observe(el) } } ngOnDestroy(): void { this.observer?.disconnect() this.observer = null } }src/app/components/blocks/legal-policy-page/index.ts0.1 kB
export { UiLegalPolicyPageComponent, tableOfContents, type PolicyTocItem } from './legal-policy-page.component'
Raw manifest:https://uipkge.dev/r/angular/legal-policy-page.json