fix: Verifikation — Suchfeld statt 654 Mini-Kacheln + Lazy-Load
- SuggestEvidenceModal: Suchfeld + max 20 Ergebnisse statt alle Kacheln - Verification page: Mitigations nur on-demand laden (nicht beim Seitenstart) - Deutlich schnellerer Seitenaufbau Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-7
@@ -14,6 +14,12 @@ export function SuggestEvidenceModal({
|
|||||||
const [selectedMitigation, setSelectedMitigation] = useState<string>('')
|
const [selectedMitigation, setSelectedMitigation] = useState<string>('')
|
||||||
const [suggested, setSuggested] = useState<SuggestedEvidence[]>([])
|
const [suggested, setSuggested] = useState<SuggestedEvidence[]>([])
|
||||||
const [loadingSuggestions, setLoadingSuggestions] = useState(false)
|
const [loadingSuggestions, setLoadingSuggestions] = useState(false)
|
||||||
|
const [search, setSearch] = useState('')
|
||||||
|
|
||||||
|
const filtered = search.trim()
|
||||||
|
? mitigations.filter(m => (m.title || '').toLowerCase().includes(search.toLowerCase()))
|
||||||
|
: mitigations
|
||||||
|
const displayed = filtered.slice(0, 20) // Show max 20 at a time
|
||||||
|
|
||||||
async function handleSelectMitigation(mitigationId: string) {
|
async function handleSelectMitigation(mitigationId: string) {
|
||||||
setSelectedMitigation(mitigationId)
|
setSelectedMitigation(mitigationId)
|
||||||
@@ -41,22 +47,35 @@ export function SuggestEvidenceModal({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-500 mb-3">
|
<p className="text-sm text-gray-500 mb-2">
|
||||||
Waehlen Sie eine Massnahme, um passende Nachweismethoden vorgeschlagen zu bekommen.
|
Waehlen Sie eine Massnahme ({mitigations.length} gesamt). Suchen Sie nach Name:
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-wrap gap-2">
|
<input
|
||||||
{mitigations.map(m => (
|
type="text" value={search} onChange={e => setSearch(e.target.value)}
|
||||||
|
placeholder="Massnahme suchen..."
|
||||||
|
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg mb-3 focus:ring-2 focus:ring-purple-400 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||||
|
/>
|
||||||
|
<div className="max-h-[200px] overflow-auto space-y-1">
|
||||||
|
{displayed.map(m => (
|
||||||
<button
|
<button
|
||||||
key={m.id} onClick={() => handleSelectMitigation(m.id)}
|
key={m.id} onClick={() => handleSelectMitigation(m.id)}
|
||||||
className={`px-3 py-1.5 text-xs rounded-lg border transition-colors ${
|
className={`w-full text-left px-3 py-2 text-xs rounded-lg border transition-colors ${
|
||||||
selectedMitigation === m.id
|
selectedMitigation === m.id
|
||||||
? 'border-purple-400 bg-purple-50 text-purple-700 font-medium'
|
? 'border-purple-400 bg-purple-50 text-purple-700 font-medium'
|
||||||
: 'border-gray-200 bg-white text-gray-700 hover:border-purple-300'
|
: 'border-gray-100 bg-white text-gray-700 hover:border-purple-300 hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{m.title}
|
{m.title || '(Ohne Titel)'}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
{filtered.length > 20 && (
|
||||||
|
<p className="text-xs text-gray-400 text-center py-1">
|
||||||
|
{filtered.length - 20} weitere — Suchbegriff eingeben um zu filtern
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{filtered.length === 0 && (
|
||||||
|
<p className="text-xs text-gray-400 text-center py-2">Keine Massnahmen gefunden</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-auto p-6">
|
<div className="flex-1 overflow-auto p-6">
|
||||||
|
|||||||
@@ -23,18 +23,25 @@ export default function VerificationPage() {
|
|||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const [verRes, hazRes, mitRes] = await Promise.all([
|
// Only load verifications initially — hazards/mitigations loaded on demand
|
||||||
fetch(`/api/sdk/v1/iace/projects/${projectId}/verifications`),
|
const verRes = await fetch(`/api/sdk/v1/iace/projects/${projectId}/verifications`)
|
||||||
fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards`),
|
|
||||||
fetch(`/api/sdk/v1/iace/projects/${projectId}/mitigations`),
|
|
||||||
])
|
|
||||||
if (verRes.ok) { const j = await verRes.json(); setItems(j.verifications || j || []) }
|
if (verRes.ok) { const j = await verRes.json(); setItems(j.verifications || j || []) }
|
||||||
if (hazRes.ok) { const j = await hazRes.json(); setHazards((j.hazards || j || []).map((h: { id: string; name: string }) => ({ id: h.id, name: h.name }))) }
|
|
||||||
if (mitRes.ok) { const j = await mitRes.json(); setMitigations((j.mitigations || j || []).map((m: { id: string; title: string }) => ({ id: m.id, title: m.title }))) }
|
|
||||||
} catch (err) { console.error('Failed to fetch data:', err) }
|
} catch (err) { console.error('Failed to fetch data:', err) }
|
||||||
finally { setLoading(false) }
|
finally { setLoading(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadMitigationsIfNeeded() {
|
||||||
|
if (mitigations.length > 0) return
|
||||||
|
try {
|
||||||
|
const mitRes = await fetch(`/api/sdk/v1/iace/projects/${projectId}/mitigations`)
|
||||||
|
if (mitRes.ok) {
|
||||||
|
const j = await mitRes.json()
|
||||||
|
const mits = (j.mitigations || j || []).map((m: Record<string, string>) => ({ id: m.id, title: m.title || m.name || '' }))
|
||||||
|
setMitigations(mits)
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSubmit(data: VerificationFormData) {
|
async function handleSubmit(data: VerificationFormData) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/verifications`, {
|
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/verifications`, {
|
||||||
@@ -89,8 +96,8 @@ export default function VerificationPage() {
|
|||||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">Nachweisfuehrung fuer alle Schutzmassnahmen und Sicherheitsanforderungen.</p>
|
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">Nachweisfuehrung fuer alle Schutzmassnahmen und Sicherheitsanforderungen.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{mitigations.length > 0 && (
|
{true && (
|
||||||
<button onClick={() => setShowSuggest(true)} className="flex items-center gap-2 px-3 py-2 border border-green-300 text-green-700 rounded-lg hover:bg-green-50 transition-colors text-sm">
|
<button onClick={async () => { await loadMitigationsIfNeeded(); setShowSuggest(true) }} className="flex items-center gap-2 px-3 py-2 border border-green-300 text-green-700 rounded-lg hover:bg-green-50 transition-colors text-sm">
|
||||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -147,7 +154,7 @@ export default function VerificationPage() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="mt-6 flex items-center justify-center gap-3">
|
<div className="mt-6 flex items-center justify-center gap-3">
|
||||||
{mitigations.length > 0 && (
|
{mitigations.length > 0 && (
|
||||||
<button onClick={() => setShowSuggest(true)} className="px-6 py-3 border border-green-300 text-green-700 rounded-lg hover:bg-green-50 transition-colors">
|
<button onClick={async () => { await loadMitigationsIfNeeded(); setShowSuggest(true) }} className="px-6 py-3 border border-green-300 text-green-700 rounded-lg hover:bg-green-50 transition-colors">
|
||||||
Nachweise vorschlagen
|
Nachweise vorschlagen
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user