Fix: Remove broken getKlausurApiUrl and clean up empty lines
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
sed replacement left orphaned hostname references in story page and empty lines in getApiBase functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
373
admin-lehrer/app/(admin)/ai/rag/_components/MapTab.tsx
Normal file
373
admin-lehrer/app/(admin)/ai/rag/_components/MapTab.tsx
Normal file
@@ -0,0 +1,373 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
REGULATIONS,
|
||||
DOC_TYPES,
|
||||
INDUSTRIES_LIST,
|
||||
INDUSTRIES,
|
||||
INDUSTRY_REGULATION_MAP,
|
||||
TYPE_COLORS,
|
||||
THEMATIC_GROUPS,
|
||||
KEY_INTERSECTIONS,
|
||||
RAG_DOCUMENTS,
|
||||
isInRag,
|
||||
} from '../rag-data'
|
||||
import type { UseRAGPageReturn } from '../_hooks/useRAGPage'
|
||||
import {
|
||||
FutureOutlookSection,
|
||||
RagCoverageSection,
|
||||
FutureRegulationsSection,
|
||||
LegalBasisSection,
|
||||
} from './MapTabSections'
|
||||
|
||||
interface MapTabProps {
|
||||
hook: UseRAGPageReturn
|
||||
}
|
||||
|
||||
export function MapTab({ hook }: MapTabProps) {
|
||||
const {
|
||||
expandedRegulation,
|
||||
setExpandedRegulation,
|
||||
expandedDocTypes,
|
||||
setExpandedDocTypes,
|
||||
expandedMatrixDoc,
|
||||
setExpandedMatrixDoc,
|
||||
setActiveTab,
|
||||
} = hook
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Industry Filter */}
|
||||
<IndustryFilter
|
||||
expandedRegulation={expandedRegulation}
|
||||
setExpandedRegulation={setExpandedRegulation}
|
||||
/>
|
||||
|
||||
{/* Thematic Groups */}
|
||||
<ThematicGroupsSection setActiveTab={setActiveTab} setExpandedRegulation={setExpandedRegulation} />
|
||||
|
||||
{/* Key Intersections */}
|
||||
<KeyIntersectionsSection />
|
||||
|
||||
{/* Regulation Matrix */}
|
||||
<RegulationMatrix
|
||||
expandedDocTypes={expandedDocTypes}
|
||||
setExpandedDocTypes={setExpandedDocTypes}
|
||||
expandedMatrixDoc={expandedMatrixDoc}
|
||||
setExpandedMatrixDoc={setExpandedMatrixDoc}
|
||||
/>
|
||||
|
||||
{/* Future Outlook Section */}
|
||||
<FutureOutlookSection />
|
||||
|
||||
{/* RAG Coverage Overview */}
|
||||
<RagCoverageSection />
|
||||
|
||||
{/* Potential Future Regulations */}
|
||||
<FutureRegulationsSection />
|
||||
|
||||
{/* Legal Basis Info */}
|
||||
<LegalBasisSection />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// --- Sub-components ---
|
||||
|
||||
function IndustryFilter({
|
||||
expandedRegulation,
|
||||
setExpandedRegulation,
|
||||
}: {
|
||||
expandedRegulation: string | null
|
||||
setExpandedRegulation: (v: string | null) => void
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
||||
<h3 className="font-semibold text-slate-900 mb-4">Regulierungen nach Branche</h3>
|
||||
<p className="text-sm text-slate-500 mb-4">
|
||||
Waehlen Sie Ihre Branche, um relevante Regulierungen zu sehen.
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
|
||||
{INDUSTRIES.map((industry) => {
|
||||
const regs = INDUSTRY_REGULATION_MAP[industry.id] || []
|
||||
return (
|
||||
<button
|
||||
key={industry.id}
|
||||
onClick={() => setExpandedRegulation(industry.id === expandedRegulation ? null : industry.id)}
|
||||
className={`p-4 rounded-lg border text-left transition-all ${
|
||||
expandedRegulation === industry.id
|
||||
? 'border-teal-500 bg-teal-50 ring-2 ring-teal-200'
|
||||
: 'border-slate-200 hover:border-slate-300 hover:bg-slate-50'
|
||||
}`}
|
||||
>
|
||||
<div className="text-2xl mb-2">{industry.icon}</div>
|
||||
<div className="font-medium text-slate-900 text-sm">{industry.name}</div>
|
||||
<div className="text-xs text-slate-500 mt-1">{regs.length} Regulierungen</div>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Selected Industry Details */}
|
||||
{expandedRegulation && INDUSTRIES.find(i => i.id === expandedRegulation) && (
|
||||
<div className="mt-6 p-4 bg-slate-50 rounded-lg">
|
||||
{(() => {
|
||||
const industry = INDUSTRIES.find(i => i.id === expandedRegulation)!
|
||||
const regCodes = INDUSTRY_REGULATION_MAP[industry.id] || []
|
||||
const regs = REGULATIONS.filter(r => regCodes.includes(r.code))
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="text-3xl">{industry.icon}</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900">{industry.name}</h4>
|
||||
<p className="text-sm text-slate-500">{industry.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{regs.map((reg) => {
|
||||
const regInRag = isInRag(reg.code)
|
||||
return (
|
||||
<div
|
||||
key={reg.code}
|
||||
className={`bg-white p-3 rounded-lg border ${regInRag ? 'border-green-200' : 'border-slate-200'}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className={`px-2 py-0.5 text-xs rounded ${TYPE_COLORS[reg.type]}`}>
|
||||
{reg.code}
|
||||
</span>
|
||||
{regInRag ? (
|
||||
<span className="px-1.5 py-0.5 text-[10px] font-bold bg-green-100 text-green-600 rounded">RAG</span>
|
||||
) : (
|
||||
<span className="px-1.5 py-0.5 text-[10px] font-bold bg-red-50 text-red-400 rounded">✗</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="font-medium text-sm text-slate-900">{reg.name}</div>
|
||||
<div className="text-xs text-slate-500 mt-1 line-clamp-2">{reg.description}</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ThematicGroupsSection({
|
||||
setActiveTab,
|
||||
setExpandedRegulation,
|
||||
}: {
|
||||
setActiveTab: (v: any) => void
|
||||
setExpandedRegulation: (v: string | null) => void
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
||||
<h3 className="font-semibold text-slate-900 mb-4">Thematische Cluster</h3>
|
||||
<p className="text-sm text-slate-500 mb-4">
|
||||
Regulierungen gruppiert nach Themenbereichen - zeigt Ueberschneidungen.
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
{THEMATIC_GROUPS.map((group) => (
|
||||
<div key={group.id} className="border border-slate-200 rounded-lg overflow-hidden">
|
||||
<div className={`${group.color} px-4 py-2 text-white font-medium flex items-center justify-between`}>
|
||||
<span>{group.name}</span>
|
||||
<span className="text-sm opacity-80">{group.regulations.length} Regulierungen</span>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<p className="text-sm text-slate-600 mb-3">{group.description}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{group.regulations.map((code) => {
|
||||
const reg = REGULATIONS.find(r => r.code === code)
|
||||
const codeInRag = isInRag(code)
|
||||
return (
|
||||
<span
|
||||
key={code}
|
||||
className={`px-3 py-1.5 rounded-full text-sm font-medium cursor-pointer ${
|
||||
codeInRag
|
||||
? 'bg-green-100 text-green-700 hover:bg-green-200'
|
||||
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTab('regulations')
|
||||
setExpandedRegulation(code)
|
||||
}}
|
||||
title={`${reg?.fullName || code}${codeInRag ? ' (im RAG)' : ' (nicht im RAG)'}`}
|
||||
>
|
||||
{codeInRag ? '✓ ' : '✗ '}{code}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function KeyIntersectionsSection() {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
||||
<h3 className="font-semibold text-slate-900 mb-4">Wichtige Schnittstellen</h3>
|
||||
<p className="text-sm text-slate-500 mb-4">
|
||||
Bereiche, in denen sich mehrere Regulierungen ueberschneiden und zusammenwirken.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{KEY_INTERSECTIONS.map((intersection, idx) => (
|
||||
<div key={idx} className="bg-gradient-to-br from-slate-50 to-slate-100 rounded-lg p-4 border border-slate-200">
|
||||
<div className="flex flex-wrap gap-1 mb-2">
|
||||
{intersection.regulations.map((code) => (
|
||||
<span
|
||||
key={code}
|
||||
className={`px-2 py-0.5 text-xs font-medium rounded ${
|
||||
isInRag(code)
|
||||
? 'bg-green-100 text-green-700'
|
||||
: 'bg-red-50 text-red-500'
|
||||
}`}
|
||||
>
|
||||
{isInRag(code) ? '✓ ' : '✗ '}{code}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="font-medium text-slate-900 text-sm mb-1">{intersection.topic}</div>
|
||||
<div className="text-xs text-slate-500">{intersection.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function RegulationMatrix({
|
||||
expandedDocTypes,
|
||||
setExpandedDocTypes,
|
||||
expandedMatrixDoc,
|
||||
setExpandedMatrixDoc,
|
||||
}: {
|
||||
expandedDocTypes: string[]
|
||||
setExpandedDocTypes: (fn: (prev: string[]) => string[]) => void
|
||||
expandedMatrixDoc: string | null
|
||||
setExpandedMatrixDoc: (v: string | null) => void
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b bg-slate-50">
|
||||
<h3 className="font-semibold text-slate-900">Branchen-Regulierungs-Matrix</h3>
|
||||
<p className="text-sm text-slate-500">{RAG_DOCUMENTS.length} Dokumente in {DOC_TYPES.length} Kategorien</p>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-xs">
|
||||
<thead className="bg-slate-50 border-b sticky top-0 z-10">
|
||||
<tr>
|
||||
<th className="px-2 py-2 text-left font-medium text-slate-500 sticky left-0 bg-slate-50 min-w-[200px]">Regulierung</th>
|
||||
{INDUSTRIES_LIST.filter((i: any) => i.id !== 'all').map((industry: any) => (
|
||||
<th key={industry.id} className="px-2 py-2 text-center font-medium text-slate-500 min-w-[60px]">
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-lg">{industry.icon}</span>
|
||||
<span className="text-[10px] leading-tight">{industry.name.split('/')[0]}</span>
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{DOC_TYPES.map((docType: any) => {
|
||||
const docsInType = RAG_DOCUMENTS.filter((d: any) => d.doc_type === docType.id)
|
||||
if (docsInType.length === 0) return null
|
||||
|
||||
const isExpanded = expandedDocTypes.includes(docType.id)
|
||||
|
||||
return (
|
||||
<React.Fragment key={docType.id}>
|
||||
<tr
|
||||
className="bg-slate-100 border-t-2 border-slate-300 cursor-pointer hover:bg-slate-200"
|
||||
onClick={() => {
|
||||
setExpandedDocTypes(prev =>
|
||||
prev.includes(docType.id)
|
||||
? prev.filter((id: string) => id !== docType.id)
|
||||
: [...prev, docType.id]
|
||||
)
|
||||
}}
|
||||
>
|
||||
<td colSpan={INDUSTRIES_LIST.length} className="px-3 py-2 font-bold text-slate-700">
|
||||
<span className="mr-2">{isExpanded ? '\u25BC' : '\u25B6'}</span>
|
||||
{docType.icon} {docType.label} ({docsInType.length})
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{isExpanded && docsInType.map((doc: any) => (
|
||||
<React.Fragment key={doc.code}>
|
||||
<tr
|
||||
className={`hover:bg-slate-50 border-b border-slate-100 cursor-pointer ${expandedMatrixDoc === doc.code ? 'bg-teal-50' : ''}`}
|
||||
onClick={() => setExpandedMatrixDoc(expandedMatrixDoc === doc.code ? null : doc.code)}
|
||||
>
|
||||
<td className="px-2 py-1.5 font-medium sticky left-0 bg-white">
|
||||
<span className="flex items-center gap-1">
|
||||
{isInRag(doc.code) ? (
|
||||
<span className="text-green-500 text-[10px]">●</span>
|
||||
) : (
|
||||
<span className="text-red-300 text-[10px]">○</span>
|
||||
)}
|
||||
<span className="text-teal-600 truncate max-w-[180px]" title={doc.full_name || doc.name}>
|
||||
{doc.name}
|
||||
</span>
|
||||
{(doc.applicability_note || doc.description) && (
|
||||
<span className="text-slate-400 text-[10px] ml-1">{expandedMatrixDoc === doc.code ? '▼' : 'ⓘ'}</span>
|
||||
)}
|
||||
</span>
|
||||
</td>
|
||||
{INDUSTRIES_LIST.filter((i: any) => i.id !== 'all').map((industry: any) => {
|
||||
const applies = doc.industries.includes(industry.id) || doc.industries.includes('all')
|
||||
return (
|
||||
<td key={industry.id} className="px-2 py-1.5 text-center">
|
||||
{applies ? (
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 bg-teal-100 text-teal-600 rounded-full">✓</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 text-slate-300">–</span>
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
{expandedMatrixDoc === doc.code && (doc.applicability_note || doc.description) && (
|
||||
<tr className="bg-teal-50 border-b border-teal-200">
|
||||
<td colSpan={INDUSTRIES_LIST.length} className="px-4 py-3">
|
||||
<div className="text-xs space-y-1.5">
|
||||
{doc.full_name && (
|
||||
<p className="font-semibold text-slate-700">{doc.full_name}</p>
|
||||
)}
|
||||
{doc.applicability_note && (
|
||||
<p className="text-teal-700 bg-teal-100 px-2 py-1 rounded inline-block">
|
||||
<span className="font-medium">Branchenrelevanz:</span> {doc.applicability_note}
|
||||
</p>
|
||||
)}
|
||||
{doc.description && (
|
||||
<p className="text-slate-600">{doc.description}</p>
|
||||
)}
|
||||
{doc.effective_date && (
|
||||
<p className="text-slate-400">In Kraft: {doc.effective_date}</p>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// FutureOutlookSection, RagCoverageSection, FutureRegulationsSection,
|
||||
// LegalBasisSection are imported from ./MapTabSections.tsx
|
||||
Reference in New Issue
Block a user