Menu

Change language

GitHub repo

Compliance Soc2 Readiness

blocklegal

Vanta/Drata-style SOC 2 Type II, ISO 27001, and HIPAA compliance readiness dashboard with framework selector tabs, audit readiness scorecards, 4 control pillar health bars, continuous automated evidence test tables with verified artifact badges, and auditor trust center portal controls.

Installation

$npx uipkge-ng@latest add compliance-soc2-readiness
Manifest:https://uipkge.dev/r/angular/compliance-soc2-readiness.jsonInstalls to:src/app/components/blocks/compliance-soc2-readiness/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

PillarData
interface PillarData {
  code: string
  title: string
  description: string
  score: number
  passed: number
  total: number
  status: string
  statusVariant: 'success' | 'warning' | 'secondary'
  highlights: string[]
  iconType: 'key' | 'shield' | 'activity' | 'users' | 'lock' | 'server' | 'file'
}
EvidenceTestItem
interface EvidenceTestItem {
  id: string
  code: string
  name: string
  description: string
  category: 'Identity & Auth' | 'Cloud Infrastructure' | 'Security Operations' | 'DevOps & CI/CD' | 'People & HR'
  frequency: 'Continuous (1h)' | 'Daily' | 'Monthly' | 'Quarterly'
  status: 'Passing' | 'Needs Attention'
  statusVariant: 'success' | 'warning'
  artifact: string
  verified: boolean
  lastTested: string
  auditorNotes: string
}
FrameworkConfig
interface FrameworkConfig {
  id: string
  tabLabel: string
  name: string
  badge: string
  standard: string
  score: number
  readinessLabel: string
  period: string
  auditor: string
  totalPassing: number
  totalTests: number
  pillars: PillarData[]
  tests: EvidenceTestItem[]
  trustCenterUrl: string
}

Files installed (2)

  • src/app/components/blocks/compliance-soc2-readiness/compliance-soc2-readiness.component.ts54.1 kB
    // Vanta/Drata-style SOC 2 Type II, ISO 27001, and HIPAA compliance readiness dashboard
    // with framework selector tabs, audit readiness scorecards, 4 control pillar health bars,
    // continuous automated evidence test tables with verified artifact badges, and auditor
    // trust center portal controls. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import {
      AlertCircle,
      AlertTriangle,
      ArrowUpRight,
      BadgeCheck,
      Calendar,
      Check,
      CheckCircle2,
      Clock,
      Download,
      Eye,
      FileDown,
      FileText,
      Filter,
      KeyRound,
      Lock,
      LucideAngularModule,
      RefreshCw,
      Search,
      Server,
      Shield,
      ShieldCheck,
      Users,
      X,
      Zap,
    } 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 { UiProgressComponent } from '@/ui/progress/progress.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    import {
      UiTableBodyComponent,
      UiTableCellComponent,
      UiTableComponent,
      UiTableHeadComponent,
      UiTableHeaderComponent,
      UiTableRowComponent,
    } from '@/ui/table/table.component'
    import { UiTabsComponent, UiTabsListComponent, UiTabsTriggerComponent } from '@/ui/tabs/tabs.component'
    
    export interface PillarData {
      code: string
      title: string
      description: string
      score: number
      passed: number
      total: number
      status: string
      statusVariant: 'success' | 'warning' | 'secondary'
      highlights: string[]
      iconType: 'key' | 'shield' | 'activity' | 'users' | 'lock' | 'server' | 'file'
    }
    
    export interface EvidenceTestItem {
      id: string
      code: string
      name: string
      description: string
      category: 'Identity & Auth' | 'Cloud Infrastructure' | 'Security Operations' | 'DevOps & CI/CD' | 'People & HR'
      frequency: 'Continuous (1h)' | 'Daily' | 'Monthly' | 'Quarterly'
      status: 'Passing' | 'Needs Attention'
      statusVariant: 'success' | 'warning'
      artifact: string
      verified: boolean
      lastTested: string
      auditorNotes: string
    }
    
    export interface FrameworkConfig {
      id: string
      tabLabel: string
      name: string
      badge: string
      standard: string
      score: number
      readinessLabel: string
      period: string
      auditor: string
      totalPassing: number
      totalTests: number
      pillars: PillarData[]
      tests: EvidenceTestItem[]
      trustCenterUrl: string
    }
    
    const frameworksData: Record<string, FrameworkConfig> = {
      soc2: {
        id: 'soc2',
        tabLabel: 'SOC 2 Type II',
        name: 'SOC 2 Type II Readiness',
        badge: 'Security & Confidentiality',
        standard: 'AICPA Trust Services Criteria (CC1–CC9)',
        score: 96,
        readinessLabel: '96% Audit Ready',
        period: 'Jan 1, 2026 – Dec 31, 2026',
        auditor: 'KPMG LLP · Sarah Jenkins, CPA/CISA',
        totalPassing: 69,
        totalTests: 71,
        pillars: [
          {
            code: 'CC1',
            title: 'Access Control & IAM',
            description: 'Identity federation, MFA enforcement, and least-privilege role provisioning.',
            score: 100,
            passed: 24,
            total: 24,
            status: '100% Passed · 24/24 tests',
            statusVariant: 'success',
            highlights: ['FIDO2/WebAuthn Hardware MFA', 'Just-In-Time Role Elevation'],
            iconType: 'key',
          },
          {
            code: 'CC2',
            title: 'Encryption & Key Management',
            description: 'Data encryption in-transit & at rest with automated CMK key rotation.',
            score: 98,
            passed: 18,
            total: 18,
            status: '98% Passed · 18/18 tests',
            statusVariant: 'success',
            highlights: ['AWS KMS Envelope CMKs', 'TLS 1.3 Strict Cipher Suites'],
            iconType: 'shield',
          },
          {
            code: 'CC3',
            title: 'Incident Response & Logging',
            description: 'Immutable audit trail ingesters, P1 runbooks, and real-time SIEM alerts.',
            score: 94,
            passed: 15,
            total: 16,
            status: '94% Passed · 15/16 tests',
            statusVariant: 'warning',
            highlights: ['Immutable WORM S3 Bucket', 'Quarterly Drill Executed'],
            iconType: 'activity',
          },
          {
            code: 'CC4',
            title: 'Employee Security & Vendor Risk',
            description: 'Background checks, awareness training, and third-party vendor DPA reviews.',
            score: 92,
            passed: 12,
            total: 13,
            status: '92% Passed · 12/13 tests',
            statusVariant: 'secondary',
            highlights: ['100% Staff Security Training', 'Tier 1 Vendor DPA Signed'],
            iconType: 'users',
          },
        ],
        tests: [
          {
            id: 'test-soc2-1',
            code: 'CC6.1',
            name: 'MFA Enforced for All Staff',
            description: 'Continuous sync with Okta IAM confirming hardware/TOTP MFA active on all active seats.',
            category: 'Identity & Auth',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'okta-mfa-sync-v2.json',
            verified: true,
            lastTested: '4m ago',
            auditorNotes: '100% of 248 accounts verified with WebAuthn/FIDO2 hardware key enforcement.',
          },
          {
            id: 'test-soc2-2',
            code: 'CC6.6',
            name: 'DB Storage Encrypted with KMS',
            description: 'All production RDS PostgreSQL and Redis storage volumes encrypted at rest with AWS KMS.',
            category: 'Cloud Infrastructure',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'aws-kms-rds-audit-prod.json',
            verified: true,
            lastTested: '12m ago',
            auditorNotes: 'AES-256 GCM encryption validated across primary cluster and 4 replica volumes.',
          },
          {
            id: 'test-soc2-3',
            code: 'CC7.2',
            name: 'Quarterly Penetration Test Completed',
            description: 'Independent gray-box network and web application penetration test with zero open criticals.',
            category: 'Security Operations',
            frequency: 'Quarterly',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'BishopFox-Pentest-Q2-2026.pdf',
            verified: true,
            lastTested: 'Aug 12, 2026',
            auditorNotes: 'Full remediation verified within 14-day SLA. Attestation signed by Bishop Fox.',
          },
          {
            id: 'test-soc2-4',
            code: 'CC6.7',
            name: 'Automated Backup Verification Daily',
            description: 'Daily point-in-time cross-region database snapshot validation and SHA-256 checksum test.',
            category: 'Cloud Infrastructure',
            frequency: 'Daily',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 's3-replica-checksum-20260821.log',
            verified: true,
            lastTested: '2h ago',
            auditorNotes: 'Automated restore drill executed with RPO < 5m and RTO < 15m.',
          },
          {
            id: 'test-soc2-5',
            code: 'CC8.1',
            name: 'Branch Protection & PR Reviews Enforced',
            description: 'GitHub repository rulesets require min 2 peer reviews, signed commits, and passing CI.',
            category: 'DevOps & CI/CD',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'github-branch-policy-main.json',
            verified: true,
            lastTested: '18m ago',
            auditorNotes: 'Zero bypass permissions granted. Administrator force-push strictly disabled.',
          },
          {
            id: 'test-soc2-6',
            code: 'CC9.2',
            name: 'Third-Party Vendor Risk Assessment',
            description: 'Annual SOC 2 Type II report collection and DPA compliance review for active SaaS vendors.',
            category: 'People & HR',
            frequency: 'Monthly',
            status: 'Needs Attention',
            statusVariant: 'warning',
            artifact: 'vendor-risk-matrix-2026.csv',
            verified: false,
            lastTested: 'Yesterday',
            auditorNotes: '1 vendor report renewal pending (Datadog EU renewal contract awaiting counter-signature).',
          },
        ],
        trustCenterUrl: 'https://trust.uipkge.dev',
      },
      iso27001: {
        id: 'iso27001',
        tabLabel: 'ISO 27001',
        name: 'ISO/IEC 27001:2022 ISMS',
        badge: 'Information Security Management',
        standard: 'Annex A Controls (A.5 – A.8) · Stage 2 Ready',
        score: 94,
        readinessLabel: '94% Stage 2 Ready',
        period: 'Audit Cycle 2026–2027',
        auditor: 'BSI Group · Henrik Lindqvist, Lead Auditor',
        totalPassing: 88,
        totalTests: 93,
        pillars: [
          {
            code: 'A.5',
            title: 'Organizational Controls',
            description: 'Information security policies, supplier governance, and asset inventories.',
            score: 96,
            passed: 35,
            total: 37,
            status: '96% Passed · 35/37 controls',
            statusVariant: 'success',
            highlights: ['Approved Security Policies', 'Supplier Risk Classification'],
            iconType: 'shield',
          },
          {
            code: 'A.6',
            title: 'People Controls',
            description: 'Screening, remote work guidelines, and continuous infosec education.',
            score: 92,
            passed: 8,
            total: 8,
            status: '92% Passed · 8/8 controls',
            statusVariant: 'success',
            highlights: ['Annual Training Certification', 'Formal Disciplinary Rules'],
            iconType: 'users',
          },
          {
            code: 'A.7',
            title: 'Physical Controls',
            description: 'Physical security perimeters, clear desk, and device screen lock policies.',
            score: 100,
            passed: 14,
            total: 14,
            status: '100% Passed · 14/14 controls',
            statusVariant: 'success',
            highlights: ['Zero Trust Cloud Infrastructure', 'Automated Screen Locking'],
            iconType: 'lock',
          },
          {
            code: 'A.8',
            title: 'Technological Controls',
            description: 'Endpoint protection, access rights, network security, and vulnerability management.',
            score: 93,
            passed: 31,
            total: 34,
            status: '93% Passed · 31/34 controls',
            statusVariant: 'warning',
            highlights: ['EDR on 100% Managed Laptops', 'Zero Trust Network Architecture'],
            iconType: 'server',
          },
        ],
        tests: [
          {
            id: 'test-iso-1',
            code: 'A.8.2',
            name: 'Privileged Access Rights Review',
            description: 'Quarterly review and revocation of administrative privileges on AWS and cloud IdPs.',
            category: 'Identity & Auth',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'iam-privileged-access-matrix.json',
            verified: true,
            lastTested: '8m ago',
            auditorNotes: 'All root credentials locked behind hardware token in safe storage.',
          },
          {
            id: 'test-iso-2',
            code: 'A.8.24',
            name: 'Use of Cryptography & Key Management',
            description: 'Enforcement of cryptographic standard policies for data in transit and at rest.',
            category: 'Cloud Infrastructure',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'crypto-policy-attestation.json',
            verified: true,
            lastTested: '15m ago',
            auditorNotes: 'TLS 1.3 enforced globally. Insecure TLS 1.0/1.1 disabled.',
          },
          {
            id: 'test-iso-3',
            code: 'A.5.24',
            name: 'Infosec Incident Management Plan',
            description: 'Annual test of incident response playbooks and SIEM real-time alerting integration.',
            category: 'Security Operations',
            frequency: 'Quarterly',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'incident-drill-simulation-2026.pdf',
            verified: true,
            lastTested: 'Aug 04, 2026',
            auditorNotes: 'Mean time to acknowledge (MTTA) under 3 minutes recorded during drill.',
          },
          {
            id: 'test-iso-4',
            code: 'A.8.9',
            name: 'Configuration Management Validation',
            description: 'Infrastructure-as-Code Terraform state tracking and drift detection alerts.',
            category: 'DevOps & CI/CD',
            frequency: 'Daily',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'terraform-drift-scan-log.json',
            verified: true,
            lastTested: '1h ago',
            auditorNotes: '100% cloud resources provisioned strictly via verified CI pipelines.',
          },
          {
            id: 'test-iso-5',
            code: 'A.6.3',
            name: 'Security Awareness & Training',
            description: 'Tracking employee onboarding training completion and phishing simulation resilience.',
            category: 'People & HR',
            frequency: 'Monthly',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'knowbe4-training-summary-q2.csv',
            verified: true,
            lastTested: 'Aug 18, 2026',
            auditorNotes: '98.4% staff passed quarterly spear-phishing test simulation.',
          },
          {
            id: 'test-iso-6',
            code: 'A.5.19',
            name: 'Supplier Security Governance',
            description: 'Formal security evaluation and risk scoring for external vendors processing company data.',
            category: 'People & HR',
            frequency: 'Monthly',
            status: 'Needs Attention',
            statusVariant: 'warning',
            artifact: 'iso-vendor-evaluation-register.csv',
            verified: false,
            lastTested: '2 days ago',
            auditorNotes: 'Annual security questionnaire overdue for 2 low-risk sub-contractors.',
          },
        ],
        trustCenterUrl: 'https://trust.uipkge.dev',
      },
      hipaa: {
        id: 'hipaa',
        tabLabel: 'HIPAA Security Rule',
        name: 'HIPAA Security & Privacy Rule',
        badge: 'Healthcare & ePHI Protection',
        standard: '45 CFR Part 160 & Part 164 Subparts A, C, E',
        score: 98,
        readinessLabel: '98% Compliant',
        period: 'Active Assessment · 2026',
        auditor: 'Coalfire Systems · HIPAA Assessor',
        totalPassing: 42,
        totalTests: 43,
        pillars: [
          {
            code: '164.308',
            title: 'Administrative Safeguards',
            description: 'Security management process, workforce clearance, and contingency operations.',
            score: 97,
            passed: 18,
            total: 18,
            status: '97% Passed · 18/18 tests',
            statusVariant: 'success',
            highlights: ['Emergency Mode Operations', 'Violation Sanction Policy'],
            iconType: 'shield',
          },
          {
            code: '164.310',
            title: 'Physical Safeguards',
            description: 'Facility access controls, workstation security, and device media control.',
            score: 100,
            passed: 10,
            total: 10,
            status: '100% Passed · 10/10 tests',
            statusVariant: 'success',
            highlights: ['Encrypted SSD Storage Media', 'Biometric Facility Access'],
            iconType: 'lock',
          },
          {
            code: '164.312',
            title: 'Technical Safeguards',
            description: 'ePHI encryption, unique user identification, and automated session timeout.',
            score: 96,
            passed: 12,
            total: 13,
            status: '96% Passed · 12/13 tests',
            statusVariant: 'warning',
            highlights: ['15-Minute Auto-Lock Policy', 'SHA-256 Audit Trail Integrity'],
            iconType: 'server',
          },
          {
            code: '164.314',
            title: 'Organizational Requirements',
            description: 'Business Associate Agreements (BAAs) with all vendors handling ePHI.',
            score: 100,
            passed: 2,
            total: 2,
            status: '100% Passed · 2/2 BAAs',
            statusVariant: 'success',
            highlights: ['AWS HIPAA BAA Signed', 'Cloudflare HIPAA BAA Signed'],
            iconType: 'users',
          },
        ],
        tests: [
          {
            id: 'test-hipaa-1',
            code: '164.312(a)(1)',
            name: 'Unique User ePHI Access Identification',
            description: 'Every healthcare provider and system operator assigned a unique non-shared login.',
            category: 'Identity & Auth',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'hipaa-identity-access-log.json',
            verified: true,
            lastTested: '10m ago',
            auditorNotes: 'Zero generic or shared accounts found across electronic health records.',
          },
          {
            id: 'test-hipaa-2',
            code: '164.312(a)(2)(iii)',
            name: 'Automatic Logoff Configuration',
            description: 'System automatically terminates an electronic session after 15 minutes of inactivity.',
            category: 'Cloud Infrastructure',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'session-timeout-policy-test.json',
            verified: true,
            lastTested: '22m ago',
            auditorNotes: 'Verified on web, desktop app, and clinician mobile portals.',
          },
          {
            id: 'test-hipaa-3',
            code: '164.312(b)',
            name: 'Audit Controls & 6-Year Retention',
            description: 'Immutable audit logs recording access, modification, and export of electronic patient data.',
            category: 'Security Operations',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'audit-worm-retention-spec.json',
            verified: true,
            lastTested: '5m ago',
            auditorNotes: 'AWS S3 Object Lock in Compliance Mode active with 6-year retention policy.',
          },
          {
            id: 'test-hipaa-4',
            code: '164.312(e)(1)',
            name: 'Transmission Security TLS 1.3',
            description: 'ePHI in transit encrypted across public networks using TLS 1.3 with HSTS enabled.',
            category: 'Cloud Infrastructure',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'tls-cipher-scan-report.json',
            verified: true,
            lastTested: '30m ago',
            auditorNotes: 'A+ SSL Labs score verified on all public API endpoints.',
          },
          {
            id: 'test-hipaa-5',
            code: '164.314(a)(1)',
            name: 'Active BAAs for All Cloud Vendors',
            description: 'Executed Business Associate Agreements signed with all subprocessors with access to ePHI.',
            category: 'People & HR',
            frequency: 'Monthly',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'signed-baa-register-2026.pdf',
            verified: true,
            lastTested: 'Aug 15, 2026',
            auditorNotes: 'BAA signed with AWS, Twilio, SendGrid, and Datadog.',
          },
          {
            id: 'test-hipaa-6',
            code: '164.308(a)(7)',
            name: 'Contingency & Disaster Recovery Plan',
            description: 'Documented emergency mode operations and annual disaster recovery failover drill.',
            category: 'Security Operations',
            frequency: 'Quarterly',
            status: 'Needs Attention',
            statusVariant: 'warning',
            artifact: 'dr-failover-drill-q2.pdf',
            verified: false,
            lastTested: '3 days ago',
            auditorNotes: 'Q3 secondary region failover drill scheduled for next Tuesday.',
          },
        ],
        trustCenterUrl: 'https://trust.uipkge.dev',
      },
      gdpr: {
        id: 'gdpr',
        tabLabel: 'GDPR',
        name: 'General Data Protection Regulation',
        badge: 'Data Privacy & Subject Rights',
        standard: 'Regulation (EU) 2016/679 · DPO Supervised',
        score: 95,
        readinessLabel: '95% Compliant',
        period: 'Annual Privacy Review · 2026',
        auditor: 'Internal DPO & Lead Data Authority',
        totalPassing: 32,
        totalTests: 34,
        pillars: [
          {
            code: 'Art 25',
            title: 'Data Protection by Design',
            description: 'Pseudonymization, data minimization, and automated retention limits.',
            score: 94,
            passed: 9,
            total: 10,
            status: '94% Passed · 9/10 tests',
            statusVariant: 'success',
            highlights: ['Automated 90-Day PII Purge', 'Data Minimization Schemas'],
            iconType: 'shield',
          },
          {
            code: 'Art 30',
            title: 'Records of Processing (RoPA)',
            description: 'Complete registry of personal data processing categories and legal purposes.',
            score: 100,
            passed: 8,
            total: 8,
            status: '100% Passed · 8/8 records',
            statusVariant: 'success',
            highlights: ['Dynamic RoPA Data Flow', 'Lawful Basis Cataloged'],
            iconType: 'file',
          },
          {
            code: 'Art 32',
            title: 'Security of Processing',
            description: 'Technical safeguards, encryption, and regular vulnerability assessments.',
            score: 96,
            passed: 11,
            total: 11,
            status: '100% Passed · 11/11 tests',
            statusVariant: 'success',
            highlights: ['Field-Level PII Encryption', 'Zero-Trust Bastion Access'],
            iconType: 'lock',
          },
          {
            code: 'Art 33/34',
            title: 'Breach Notification Protocol',
            description: '72-hour supervisory authority notification process and risk assessments.',
            score: 90,
            passed: 4,
            total: 5,
            status: '90% Passed · 4/5 tests',
            statusVariant: 'warning',
            highlights: ['72-Hour DPA Escalation SLA', 'Subject Notification Workflow'],
            iconType: 'activity',
          },
        ],
        tests: [
          {
            id: 'test-gdpr-1',
            code: 'Art 17',
            name: 'Right to Erasure / DSR Pipeline',
            description: 'Automated user data deletion across database tables and 3rd party integrations in < 30 days.',
            category: 'DevOps & CI/CD',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'dsr-erasure-orchestrator.json',
            verified: true,
            lastTested: '14m ago',
            auditorNotes: 'Mean time to complete erasure request: 4.2 hours.',
          },
          {
            id: 'test-gdpr-2',
            code: 'Art 30',
            name: 'RoPA Inventory Live Synchronization',
            description:
              'Data classification tags synced with schema catalog to ensure full records of processing coverage.',
            category: 'Identity & Auth',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'ropa-schema-mapping-v3.json',
            verified: true,
            lastTested: '6m ago',
            auditorNotes: '100% of PII data fields mapped to lawful basis.',
          },
          {
            id: 'test-gdpr-3',
            code: 'Art 28',
            name: 'Data Processing Agreements (DPAs)',
            description: 'Standard Contractual Clauses (SCCs) and DPAs executed with all EU data processors.',
            category: 'People & HR',
            frequency: 'Monthly',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'dpa-register-eu-2026.pdf',
            verified: true,
            lastTested: 'Aug 10, 2026',
            auditorNotes: 'All sub-processors compliant with EU-US Data Privacy Framework.',
          },
          {
            id: 'test-gdpr-4',
            code: 'Art 7',
            name: 'Cookie Consent & Preference CMP',
            description: 'Explicit opt-in banner blocking non-essential tracking prior to affirmative user consent.',
            category: 'Cloud Infrastructure',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'cmp-consent-telemetry.json',
            verified: true,
            lastTested: '1h ago',
            auditorNotes: 'Zero tracking cookies loaded prior to consent confirmation.',
          },
          {
            id: 'test-gdpr-5',
            code: 'Art 32(1)(a)',
            name: 'PII Field-Level Encryption at Rest',
            description: 'Names, emails, and financial identifiers hashed or encrypted with AES-256 before storage.',
            category: 'Cloud Infrastructure',
            frequency: 'Continuous (1h)',
            status: 'Passing',
            statusVariant: 'success',
            artifact: 'field-encryption-audit.json',
            verified: true,
            lastTested: '25m ago',
            auditorNotes: 'KMS envelope encryption active on all customer records.',
          },
          {
            id: 'test-gdpr-6',
            code: 'Art 33',
            name: '72-Hour Breach Notification Drill',
            description: 'Tabletop simulation testing supervisory authority DPA notification within statutory 72h window.',
            category: 'Security Operations',
            frequency: 'Quarterly',
            status: 'Needs Attention',
            statusVariant: 'warning',
            artifact: 'gdpr-breach-playbook-2026.pdf',
            verified: false,
            lastTested: '4 days ago',
            auditorNotes: 'DPO escalation contact list updated; tabletop review due in Q3.',
          },
        ],
        trustCenterUrl: 'https://trust.uipkge.dev',
      },
    }
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-compliance-soc2-readiness, [ui-compliance-soc2-readiness]',
      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,
        UiCardHeaderComponent,
        UiCardTitleComponent,
        UiProgressComponent,
        UiSeparatorComponent,
        UiTableBodyComponent,
        UiTableCellComponent,
        UiTableComponent,
        UiTableHeadComponent,
        UiTableHeaderComponent,
        UiTableRowComponent,
        UiTabsComponent,
        UiTabsListComponent,
        UiTabsTriggerComponent,
      ],
      template: `
        <div data-slot="compliance-soc2-readiness" [class]="rootClass">
          <!-- Framework Selector Tabs -->
          <ui-tabs [value]="activeFrameworkId()" (valueChange)="activeFrameworkId.set($event)" class="w-full">
            <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
              <ui-tabs-list class="grid w-full grid-cols-2 md:w-auto md:grid-cols-4">
                <button ui-tabs-trigger value="soc2" class="text-xs font-medium">SOC 2 Type II</button>
                <button ui-tabs-trigger value="iso27001" class="text-xs font-medium">ISO 27001</button>
                <button ui-tabs-trigger value="hipaa" class="text-xs font-medium">HIPAA Security Rule</button>
                <button ui-tabs-trigger value="gdpr" class="text-xs font-medium">GDPR</button>
              </ui-tabs-list>
    
              <div class="text-muted-foreground flex items-center gap-2 text-xs">
                <span class="bg-success inline-flex size-2 rounded-full"></span>
                <span>Real-time Telemetry Ingestion Active</span>
              </div>
            </div>
    
            <!-- Executive Overview Card -->
            <div ui-card class="border-border bg-card mt-4 shadow-xs">
              <div ui-card-content class="p-6">
                <div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
                  <!-- Left: Framework info -->
                  <div class="space-y-2">
                    <div class="flex flex-wrap items-center gap-2.5">
                      <h1 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">
                        {{ currentFramework().name }}
                      </h1>
                      <span ui-badge variant="secondary" class="font-medium">
                        {{ currentFramework().badge }}
                      </span>
                    </div>
                    <p class="text-muted-foreground text-xs sm:text-sm">
                      {{ currentFramework().standard }}
                    </p>
                    <div class="text-muted-foreground flex flex-wrap items-center gap-3 pt-1 text-xs">
                      <span ui-badge variant="outline" class="gap-1.5 font-normal">
                        <lucide-icon [img]="Calendar" class="size-3.5" />
                        {{ currentFramework().period }}
                      </span>
                      <div ui-separator orientation="vertical" class="hidden h-3.5 sm:block"></div>
                      <span>
                        Lead Assessor: <strong class="text-foreground font-medium">{{ currentFramework().auditor }}</strong>
                      </span>
                    </div>
                  </div>
    
                  <!-- Right: Big Score Card & Action Buttons -->
                  <div class="flex flex-col gap-4 sm:flex-row sm:items-center">
                    <!-- Score Badge -->
                    <div
                      class="border-success/30 bg-success/10 flex items-center gap-3.5 rounded-xl border p-3.5 shadow-xs"
                    >
                      <div class="bg-success/20 text-success flex size-11 shrink-0 items-center justify-center rounded-lg">
                        <lucide-icon [img]="ShieldCheck" class="size-6" />
                      </div>
                      <div class="space-y-0.5">
                        <div class="flex items-baseline gap-2">
                          <span class="text-success text-2xl font-bold tracking-tight tabular-nums sm:text-3xl">
                            {{ currentFramework().score }}%
                          </span>
                          <span ui-badge variant="success" class="gap-1 px-2 text-xs font-semibold">
                            <lucide-icon [img]="CheckCircle2" class="size-3" />
                            {{ readinessShort() }}
                          </span>
                        </div>
                        <p class="text-muted-foreground text-xs">
                          {{ currentFramework().totalPassing }}/{{ currentFramework().totalTests }} Continuous Controls
                          Passing
                        </p>
                      </div>
                    </div>
    
                    <!-- Action Buttons -->
                    <div class="flex flex-wrap gap-2.5 sm:flex-col">
                      <button
                        ui-button
                        variant="default"
                        size="sm"
                        class="gap-2 shadow-xs"
                        (click)="handleScheduleReview()"
                      >
                        @if (scheduleRequested()) {
                          <lucide-icon [img]="Check" class="text-success size-4" />
                        } @else {
                          <lucide-icon [img]="Calendar" class="size-4" />
                        }
                        <span>{{ scheduleRequested() ? 'Review Requested!' : 'Schedule Auditor Review' }}</span>
                      </button>
    
                      <button ui-button variant="outline" size="sm" class="gap-2 shadow-xs" (click)="handleExportPackage()">
                        @if (exportSuccess()) {
                          <lucide-icon [img]="Check" class="text-success size-4" />
                        } @else {
                          <lucide-icon [img]="FileDown" class="size-4" />
                        }
                        <span>{{ exportSuccess() ? 'Package Exported!' : 'Export Compliance Package' }}</span>
                      </button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
    
            <!-- 4 Compliance Control Pillar Cards -->
            <div class="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
              @for (pillar of currentFramework().pillars; track pillar.code) {
                <div ui-card class="border-border bg-card hover:border-border shadow-xs transition-colors">
                  <div ui-card-header class="p-4 pb-2">
                    <div class="flex items-start justify-between gap-2">
                      <div class="flex items-center gap-2">
                        <div
                          class="bg-muted text-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border"
                        >
                          <lucide-icon [img]="pillarIcon(pillar.iconType)" class="size-4" />
                        </div>
                        <span ui-badge variant="outline" class="font-mono text-xs font-semibold">
                          {{ pillar.code }}
                        </span>
                      </div>
    
                      <span ui-badge [variant]="pillar.statusVariant" class="text-xs font-medium">
                        {{ pillarChip(pillar) }}
                      </span>
                    </div>
    
                    <h3 ui-card-title class="mt-2 text-sm leading-snug font-semibold">
                      {{ pillar.title }}
                    </h3>
                    <p ui-card-description class="line-clamp-2 text-xs">
                      {{ pillar.description }}
                    </p>
                  </div>
    
                  <div ui-card-content class="space-y-3 p-4 pt-1">
                    <div class="space-y-1.5">
                      <div class="flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 text-xs">
                        <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">
                          {{ pillar.score }}%
                        </span>
                        <span class="text-muted-foreground font-medium tabular-nums">
                          {{ pillar.passed }}/{{ pillar.total }} tests
                        </span>
                      </div>
                      <div ui-progress [value]="pillar.score" class="h-1.5 w-full"></div>
                    </div>
    
                    <!-- Bullet highlights -->
                    <div class="bg-muted/40 border-border/60 space-y-1 rounded-md border p-2 text-xs">
                      @for (highlight of pillar.highlights; track highlight) {
                        <div class="text-muted-foreground flex items-center gap-1.5">
                          <lucide-icon [img]="CheckCircle2" class="text-success size-3 shrink-0" />
                          <span class="truncate">{{ highlight }}</span>
                        </div>
                      }
                    </div>
                  </div>
                </div>
              }
            </div>
    
            <!-- Automated Evidence Tests Table Card -->
            <div ui-card class="border-border bg-card mt-6 shadow-xs">
              <div ui-card-header class="flex flex-col gap-4 pb-4 sm:flex-row sm:items-center sm:justify-between">
                <div>
                  <div class="flex items-center gap-2">
                    <h3 ui-card-title class="text-base font-semibold">Automated Evidence Tests</h3>
                    <span ui-badge variant="secondary" class="font-mono text-xs">
                      {{ filteredTests().length }} Active
                    </span>
                  </div>
                  <p ui-card-description class="text-xs">
                    Continuous synthetic and policy compliance evaluation across infrastructure and IAM.
                  </p>
                </div>
    
                <div class="flex flex-wrap items-center gap-2">
                  <button
                    ui-button
                    variant="outline"
                    size="sm"
                    class="gap-1.5 text-xs shadow-xs"
                    [disabled]="isTestingAll()"
                    (click)="handleRunAllTests()"
                  >
                    <lucide-icon [img]="RefreshCw" [class]="runAllIconClass" />
                    <span>{{ isTestingAll() ? 'Evaluating Tests...' : 'Run All Automated Tests' }}</span>
                  </button>
                </div>
              </div>
    
              <!-- Filter & Search Toolbar -->
              <div class="border-border/60 border-t p-4 pt-3 pb-3">
                <div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                  <div class="relative flex-1">
                    <lucide-icon
                      [img]="Search"
                      class="text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-3.5 -translate-y-1/2"
                    />
                    <input
                      [value]="searchQuery()"
                      (input)="searchQuery.set($any($event.target).value)"
                      type="text"
                      placeholder="Filter by control code, name, category, or evidence artifact..."
                      class="border-border bg-background placeholder:text-muted-foreground focus-visible:ring-ring h-8 w-full rounded-md border px-3 pl-8 text-xs focus-visible:ring-2 focus-visible:outline-none"
                    />
                  </div>
    
                  <div class="flex flex-wrap items-center gap-1.5">
                    <span class="text-muted-foreground mr-1 flex items-center gap-1 text-xs font-medium">
                      <lucide-icon [img]="Filter" class="size-3" />
                      Category:
                    </span>
                    @for (cat of availableCategories(); track cat) {
                      <button type="button" [class]="categoryPillClass(cat)" (click)="selectedCategory.set(cat)">
                        {{ cat === 'all' ? 'All Categories' : cat }}
                      </button>
                    }
                  </div>
                </div>
              </div>
    
              <!-- Table Container -->
              <div ui-card-content class="p-0">
                <div class="border-border/60 overflow-x-auto border-t">
                  <table ui-table>
                    <thead ui-table-header class="bg-muted/50">
                      <tr ui-table-row>
                        <th ui-table-head class="min-w-[220px] text-xs">Control & Rule Name</th>
                        <th ui-table-head class="text-xs">Domain Category</th>
                        <th ui-table-head class="text-xs">Frequency</th>
                        <th ui-table-head class="text-xs">Status</th>
                        <th ui-table-head class="text-xs">Evidence Artifact</th>
                        <th ui-table-head class="text-right text-xs">Actions</th>
                      </tr>
                    </thead>
                    <tbody ui-table-body>
                      @for (test of filteredTests(); track test.id) {
                        <tr ui-table-row class="hover:bg-muted/20">
                          <!-- Control Code & Name -->
                          <td ui-table-cell class="py-3">
                            <div class="space-y-0.5">
                              <div class="flex items-center gap-2">
                                <span class="text-foreground font-mono text-xs font-bold">{{ test.code }}</span>
                                <span class="text-foreground text-xs font-semibold">{{ test.name }}</span>
                              </div>
                              <p class="text-muted-foreground max-w-sm text-xs leading-relaxed sm:max-w-md">
                                {{ test.description }}
                              </p>
                            </div>
                          </td>
    
                          <!-- Category -->
                          <td ui-table-cell class="py-3">
                            <span ui-badge variant="outline" class="text-xs font-normal">
                              {{ test.category }}
                            </span>
                          </td>
    
                          <!-- Frequency -->
                          <td ui-table-cell class="text-muted-foreground py-3 text-xs">
                            <div class="flex items-center gap-1.5 font-medium">
                              <lucide-icon [img]="Clock" class="size-3.5" />
                              <span>{{ test.frequency }}</span>
                            </div>
                            <span class="text-muted-foreground/70 block text-xs"> Tested: {{ lastTestedFor(test) }} </span>
                          </td>
    
                          <!-- Status -->
                          <td ui-table-cell class="py-3">
                            <span ui-badge [variant]="test.statusVariant" class="gap-1 text-xs font-medium">
                              @if (test.status === 'Passing') {
                                <lucide-icon [img]="CheckCircle2" class="size-3" />
                              } @else {
                                <lucide-icon [img]="AlertTriangle" class="size-3" />
                              }
                              {{ test.status }}
                            </span>
                          </td>
    
                          <!-- Evidence Artifact Link -->
                          <td ui-table-cell class="py-3">
                            <button
                              type="button"
                              class="hover:border-primary/40 hover:bg-muted/60 focus-visible:ring-ring group border-border/80 bg-muted/30 flex items-center gap-1.5 rounded-md border px-2 py-1 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none"
                              (click)="viewEvidence(test)"
                            >
                              <lucide-icon
                                [img]="FileText"
                                class="text-muted-foreground group-hover:text-foreground size-3.5 shrink-0"
                              />
                              <span class="text-foreground max-w-[150px] truncate font-mono text-xs sm:max-w-[200px]">
                                {{ test.artifact }}
                              </span>
                              @if (test.verified) {
                                <lucide-icon [img]="CheckCircle2" class="text-success size-3 shrink-0" />
                              }
                            </button>
                          </td>
    
                          <!-- Action Buttons -->
                          <td ui-table-cell class="py-3 text-right">
                            <div class="flex items-center justify-end gap-1.5">
                              <button
                                ui-button
                                variant="ghost"
                                size="xs"
                                class="h-7 gap-1 text-xs"
                                [disabled]="isRunning(test.id)"
                                (click)="handleRunTest(test.id)"
                              >
                                <lucide-icon [img]="RefreshCw" [class]="runIconClass(test.id)" />
                                <span class="hidden sm:inline">Test Now</span>
                              </button>
    
                              <button
                                ui-button
                                variant="outline"
                                size="xs"
                                class="h-7 gap-1 text-xs"
                                (click)="viewEvidence(test)"
                              >
                                <lucide-icon [img]="Eye" class="size-3" />
                                <span>Evidence</span>
                              </button>
                            </div>
                          </td>
                        </tr>
                      }
    
                      @if (filteredTests().length === 0) {
                        <tr ui-table-row>
                          <td ui-table-cell colspan="6" class="py-8 text-center">
                            <p class="text-muted-foreground text-xs">No automated compliance tests match your query.</p>
                          </td>
                        </tr>
                      }
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
    
            <!-- Auditor Access & Trust Center Preview Strip -->
            <div ui-card class="border-border bg-card mt-6 overflow-hidden shadow-xs">
              <div ui-card-content class="p-6">
                <div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
                  <div class="flex items-start gap-4">
                    <div
                      class="bg-primary/10 text-primary border-primary/20 flex size-11 shrink-0 items-center justify-center rounded-xl border"
                    >
                      <lucide-icon [img]="BadgeCheck" class="size-6" />
                    </div>
                    <div class="space-y-1">
                      <div class="flex flex-wrap items-center gap-2">
                        <h3 class="text-foreground text-base font-semibold">Auditor Access & Public Trust Center</h3>
                        <span ui-badge variant="outline" class="gap-1 font-mono text-xs">
                          <span class="bg-success size-1.5 rounded-full"></span>
                          Vault Online
                        </span>
                      </div>
                      <p class="text-muted-foreground text-xs leading-relaxed sm:text-sm">
                        Cryptographically verifiable evidence room configured for continuous compliance review by
                        <strong class="text-foreground font-medium">{{ currentFramework().auditor }}</strong
                        >.
                      </p>
                      <div class="text-muted-foreground flex flex-wrap items-center gap-3 pt-1 text-xs">
                        <span>Access Grant: <strong class="text-foreground font-medium">Valid for 14 days</strong></span>
                        <div ui-separator orientation="vertical" class="hidden h-3 sm:block"></div>
                        <span
                          >Trust Portal:
                          <span class="text-foreground font-mono">{{ currentFramework().trustCenterUrl }}</span></span
                        >
                      </div>
                    </div>
                  </div>
    
                  <div class="flex flex-wrap items-center gap-2.5 sm:self-start lg:self-center">
                    <a [href]="currentFramework().trustCenterUrl" target="_blank" rel="noreferrer" class="inline-flex">
                      <button ui-button variant="outline" size="sm" class="gap-1.5 shadow-xs">
                        <lucide-icon [img]="ArrowUpRight" class="size-4" />
                        <span>Preview Live Trust Center</span>
                      </button>
                    </a>
    
                    <button ui-button variant="secondary" size="sm" class="gap-1.5 shadow-xs">
                      <lucide-icon [img]="KeyRound" class="size-4" />
                      <span>Manage Auditor Access</span>
                    </button>
                  </div>
                </div>
              </div>
    
              <!-- Bottom Audit Ingestion Watermark -->
              <div
                class="bg-muted/40 border-border flex flex-col gap-2 border-t px-6 py-2.5 text-xs sm:flex-row sm:items-center sm:justify-between"
              >
                <div class="text-muted-foreground flex items-center gap-2 font-mono">
                  <span class="bg-success size-1.5 rounded-full"></span>
                  <span>Ledger Stamp: 0x9b4a8e217f0a4421b8c1992019a · SHA-256 Verified</span>
                </div>
                <span class="text-muted-foreground font-mono text-xs">
                  Framework: {{ currentFramework().tabLabel }} · Last Evaluation: Just now
                </span>
              </div>
            </div>
          </ui-tabs>
    
          <!-- Modal / Sheet Preview for Evidence Artifact -->
          @if (activeEvidenceModal(); as modal) {
            <div
              role="dialog"
              aria-modal="true"
              class="bg-background/80 fixed inset-0 z-50 flex items-center justify-center p-4 backdrop-blur-sm"
              (click)="closeEvidenceModal()"
            >
              <div ui-card class="border-border bg-card w-full max-w-lg shadow-lg" (click)="$event.stopPropagation()">
                <div ui-card-header class="flex flex-row items-center justify-between pb-3">
                  <div class="space-y-1">
                    <div class="flex items-center gap-2">
                      <span ui-badge variant="outline" class="font-mono text-xs">{{ modal.code }}</span>
                      <h3 ui-card-title class="text-base font-semibold">Evidence Verification Details</h3>
                    </div>
                    <p ui-card-description class="text-xs">{{ modal.name }}</p>
                  </div>
                  <button
                    ui-button
                    variant="ghost"
                    size="icon-sm"
                    class="size-7"
                    aria-label="Close evidence modal"
                    (click)="closeEvidenceModal()"
                  >
                    <lucide-icon [img]="X" class="size-4" />
                  </button>
                </div>
    
                <div ui-card-content class="space-y-4 text-xs">
                  <div class="bg-muted/40 border-border/80 space-y-2 rounded-lg border p-3">
                    <div class="flex items-center justify-between">
                      <span class="text-muted-foreground font-medium">Artifact File:</span>
                      <span class="text-foreground font-mono font-semibold">{{ modal.artifact }}</span>
                    </div>
                    <div class="flex items-center justify-between">
                      <span class="text-muted-foreground font-medium">Verification Status:</span>
                      <span ui-badge [variant]="modal.statusVariant" class="gap-1 text-xs">
                        @if (modal.verified) {
                          <lucide-icon [img]="CheckCircle2" class="size-3" />
                        } @else {
                          <lucide-icon [img]="AlertCircle" class="size-3" />
                        }
                        {{ modal.verified ? 'Cryptographically Verified' : 'Action Required' }}
                      </span>
                    </div>
                    <div class="flex items-center justify-between">
                      <span class="text-muted-foreground font-medium">Test Frequency:</span>
                      <span class="text-foreground font-mono">{{ modal.frequency }}</span>
                    </div>
                  </div>
    
                  <div class="space-y-1.5">
                    <span class="text-muted-foreground font-medium">Auditor Evaluation Notes:</span>
                    <p class="bg-muted/20 border-border text-foreground rounded-md border p-2.5 leading-relaxed">
                      {{ modal.auditorNotes }}
                    </p>
                  </div>
    
                  <div class="flex items-center justify-end gap-2 pt-2">
                    <button
                      aria-label="Close evidence modal"
                      ui-button
                      variant="outline"
                      size="sm"
                      class="text-xs"
                      (click)="closeEvidenceModal()"
                    >
                      Close
                    </button>
                    <button
                      aria-label="Close evidence modal"
                      ui-button
                      variant="default"
                      size="sm"
                      class="gap-1.5 text-xs"
                      (click)="closeEvidenceModal()"
                    >
                      <lucide-icon [img]="Download" class="size-3.5" />
                      Download Artifact
                    </button>
                  </div>
                </div>
              </div>
            </div>
          }
        </div>
      `,
    })
    export class UiComplianceSoc2ReadinessComponent {
      protected readonly AlertCircle = AlertCircle
      protected readonly AlertTriangle = AlertTriangle
      protected readonly ArrowUpRight = ArrowUpRight
      protected readonly BadgeCheck = BadgeCheck
      protected readonly Calendar = Calendar
      protected readonly Check = Check
      protected readonly CheckCircle2 = CheckCircle2
      protected readonly Clock = Clock
      protected readonly Download = Download
      protected readonly Eye = Eye
      protected readonly FileDown = FileDown
      protected readonly FileText = FileText
      protected readonly Filter = Filter
      protected readonly KeyRound = KeyRound
      protected readonly Lock = Lock
      protected readonly RefreshCw = RefreshCw
      protected readonly Search = Search
      protected readonly Server = Server
      protected readonly Shield = Shield
      protected readonly ShieldCheck = ShieldCheck
      protected readonly Users = Users
      protected readonly X = X
      protected readonly Zap = Zap
    
      @Input() set defaultFramework(v: string | undefined) {
        if (v) this.activeFrameworkId.set(v)
      }
      @Input('class') className?: string
    
      readonly activeFrameworkId = signal('soc2')
      readonly searchQuery = signal('')
      readonly selectedCategory = signal<string>('all')
      readonly runningTestIds = signal<Record<string, boolean>>({})
      readonly lastTestedMap = signal<Record<string, string>>({})
      readonly activeEvidenceModal = signal<EvidenceTestItem | null>(null)
      readonly isTestingAll = signal(false)
      readonly scheduleRequested = signal(false)
      readonly exportSuccess = signal(false)
    
      readonly currentFramework = computed(() => frameworksData[this.activeFrameworkId()] ?? frameworksData['soc2']!)
    
      readonly readinessShort = computed(() => {
        const fw = this.currentFramework()
        return fw.readinessLabel.replace(`${fw.score}% `, '')
      })
    
      readonly availableCategories = computed(() => {
        const cats = new Set<string>()
        this.currentFramework().tests.forEach((t) => cats.add(t.category))
        return ['all', ...Array.from(cats)]
      })
    
      readonly filteredTests = computed(() => {
        const q = this.searchQuery().trim().toLowerCase()
        const cat = this.selectedCategory()
        return this.currentFramework().tests.filter((test) => {
          const matchesCat = cat === 'all' || test.category === cat
          const matchesQuery =
            !q ||
            test.code.toLowerCase().includes(q) ||
            test.name.toLowerCase().includes(q) ||
            test.description.toLowerCase().includes(q) ||
            test.artifact.toLowerCase().includes(q) ||
            test.category.toLowerCase().includes(q)
          return matchesCat && matchesQuery
        })
      })
    
      get rootClass(): string {
        return cn('w-full space-y-6', this.className)
      }
    
      get runAllIconClass(): string {
        return cn('size-3.5', this.isTestingAll() && 'animate-spin')
      }
    
      runIconClass(id: string): string {
        return cn('size-3', this.isRunning(id) && 'animate-spin')
      }
    
      categoryPillClass(cat: string): string {
        return cn(
          'focus-visible:ring-ring inline-flex items-center rounded-full border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none',
          this.selectedCategory() === cat
            ? 'border-primary bg-primary text-primary-foreground shadow-xs'
            : 'border-border bg-card text-muted-foreground hover:bg-accent hover:text-foreground',
        )
      }
    
      pillarIcon(iconType: string): unknown {
        switch (iconType) {
          case 'key':
            return KeyRound
          case 'shield':
            return ShieldCheck
          case 'activity':
            return Zap
          case 'users':
            return Users
          case 'lock':
            return Lock
          case 'server':
            return Server
          case 'file':
            return FileText
          default:
            return Shield
        }
      }
    
      pillarChip(pillar: PillarData): string {
        if (pillar.score === 100) return 'Zero Gaps'
        if (pillar.statusVariant === 'warning') return '1 Warning'
        return 'Passing'
      }
    
      isRunning(id: string): boolean {
        return !!this.runningTestIds()[id]
      }
    
      lastTestedFor(test: EvidenceTestItem): string {
        return this.lastTestedMap()[test.id] ?? test.lastTested
      }
    
      handleRunTest(testId: string): void {
        this.runningTestIds.update((prev) => ({ ...prev, [testId]: true }))
        setTimeout(() => {
          this.runningTestIds.update((prev) => ({ ...prev, [testId]: false }))
          this.lastTestedMap.update((prev) => ({ ...prev, [testId]: 'Just now' }))
        }, 500)
      }
    
      handleRunAllTests(): void {
        this.isTestingAll.set(true)
        setTimeout(() => {
          this.isTestingAll.set(false)
          const next: Record<string, string> = {}
          this.currentFramework().tests.forEach((t) => {
            next[t.id] = 'Just now'
          })
          this.lastTestedMap.update((prev) => ({ ...prev, ...next }))
        }, 700)
      }
    
      handleScheduleReview(): void {
        this.scheduleRequested.set(true)
        setTimeout(() => this.scheduleRequested.set(false), 3000)
      }
    
      handleExportPackage(): void {
        this.exportSuccess.set(true)
        setTimeout(() => this.exportSuccess.set(false), 3000)
      }
    
      viewEvidence(test: EvidenceTestItem): void {
        this.activeEvidenceModal.set(test)
      }
    
      closeEvidenceModal(): void {
        this.activeEvidenceModal.set(null)
      }
    }
    
  • src/app/components/blocks/compliance-soc2-readiness/index.ts0.2 kB

Raw manifest:https://uipkge.dev/r/angular/compliance-soc2-readiness.json