'use client' import { useState } from 'react' import { BookOpen, ExternalLink, Scale, ChevronDown, ChevronUp, Info } from 'lucide-react' import { DSFALicenseCode, DSFA_LICENSE_LABELS, SourceAttributionProps } from '@/lib/sdk/types' /** * Get license badge color based on license type */ function getLicenseBadgeColor(licenseCode: DSFALicenseCode): string { switch (licenseCode) { case 'DL-DE-BY-2.0': case 'DL-DE-ZERO-2.0': return 'bg-blue-100 text-blue-700 border-blue-200' case 'CC-BY-4.0': return 'bg-green-100 text-green-700 border-green-200' case 'EDPB-LICENSE': return 'bg-purple-100 text-purple-700 border-purple-200' case 'PUBLIC_DOMAIN': return 'bg-gray-100 text-gray-700 border-gray-200' case 'PROPRIETARY': return 'bg-amber-100 text-amber-700 border-amber-200' default: return 'bg-slate-100 text-slate-700 border-slate-200' } } /** * Get license URL based on license code */ function getLicenseUrl(licenseCode: DSFALicenseCode): string | null { switch (licenseCode) { case 'DL-DE-BY-2.0': return 'https://www.govdata.de/dl-de/by-2-0' case 'DL-DE-ZERO-2.0': return 'https://www.govdata.de/dl-de/zero-2-0' case 'CC-BY-4.0': return 'https://creativecommons.org/licenses/by/4.0/' case 'EDPB-LICENSE': return 'https://edpb.europa.eu/about-edpb/legal-notice_en' default: return null } } /** * License badge component */ function LicenseBadge({ licenseCode }: { licenseCode: DSFALicenseCode }) { const licenseUrl = getLicenseUrl(licenseCode) const colorClass = getLicenseBadgeColor(licenseCode) const label = DSFA_LICENSE_LABELS[licenseCode] || licenseCode if (licenseUrl) { return ( {label} ) } return ( {label} ) } /** * Single source item in the attribution list */ function SourceItem({ source, index, showScore }: { source: SourceAttributionProps['sources'][0] index: number showScore: boolean }) { return (
  • {index + 1}.
    {source.sourceUrl ? ( {source.sourceName} ) : ( {source.sourceName} )} {showScore && source.score !== undefined && ( ({(source.score * 100).toFixed(0)}%) )}

    {source.attributionText}

  • ) } /** * Compact source badge for inline display */ function CompactSourceBadge({ source }: { source: SourceAttributionProps['sources'][0] }) { return ( {source.sourceCode} ) } /** * SourceAttribution component - displays source/license information for DSFA RAG results * * @example * ```tsx * * ``` */ export function SourceAttribution({ sources, compact = false, showScores = false }: SourceAttributionProps) { const [isExpanded, setIsExpanded] = useState(!compact) if (!sources || sources.length === 0) { return null } // Compact mode - just show badges if (compact && !isExpanded) { return (
    {sources.slice(0, 3).map((source, i) => ( ))} {sources.length > 3 && ( +{sources.length - 3} )}
    ) } return (

    Quellen & Lizenzen

    {compact && ( )}
    {/* Aggregated license notice */} {sources.length > 1 && (

    Hinweis: Die angezeigten Inhalte stammen aus {sources.length} verschiedenen Quellen mit unterschiedlichen Lizenzen. Bitte beachten Sie die jeweiligen Attributionsanforderungen.

    )}
    ) } /** * Inline source reference for use within text */ export function InlineSourceRef({ sourceCode, sourceName, sourceUrl }: { sourceCode: string sourceName: string sourceUrl?: string }) { if (sourceUrl) { return ( [{sourceCode}] ) } return ( [{sourceCode}] ) } /** * Attribution footer for generated documents */ export function AttributionFooter({ sources, generatedAt }: { sources: SourceAttributionProps['sources'] generatedAt?: Date }) { if (!sources || sources.length === 0) { return null } // Group by license const byLicense = sources.reduce((acc, source) => { const key = source.licenseCode if (!acc[key]) acc[key] = [] acc[key].push(source) return acc }, {} as Record) return ( ) } export default SourceAttribution