[split-required] Split remaining 500-680 LOC files (final batch)
website (17 pages + 3 components): - multiplayer/wizard, middleware/wizard+test-wizard, communication - builds/wizard, staff-search, voice, sbom/wizard - foerderantrag, mail/tasks, tools/communication, sbom - compliance/evidence, uni-crawler, brandbook (already done) - CollectionsTab, IngestionTab, RiskHeatmap backend-lehrer (5 files): - letters_api (641 → 2), certificates_api (636 → 2) - alerts_agent/db/models (636 → 3) - llm_gateway/communication_service (614 → 2) - game/database already done in prior batch klausur-service (2 files): - hybrid_vocab_extractor (664 → 2) - klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2) voice-service (3 files): - bqas/rag_judge (618 → 3), runner (529 → 2) - enhanced_task_orchestrator (519 → 2) studio-v2 (6 files): - korrektur/[klausurId] (578 → 4), fairness (569 → 2) - AlertsWizard (552 → 2), OnboardingWizard (513 → 2) - korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
120
website/app/admin/staff-search/_components/useStaffSearch.ts
Normal file
120
website/app/admin/staff-search/_components/useStaffSearch.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { StaffMember, Publication, StaffStats, University, EDU_SEARCH_API } from './types'
|
||||
|
||||
export function useStaffSearch() {
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [staff, setStaff] = useState<StaffMember[]>([])
|
||||
const [selectedStaff, setSelectedStaff] = useState<StaffMember | null>(null)
|
||||
const [publications, setPublications] = useState<Publication[]>([])
|
||||
const [stats, setStats] = useState<StaffStats | null>(null)
|
||||
const [universities, setUniversities] = useState<University[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
// Filters
|
||||
const [filterState, setFilterState] = useState('')
|
||||
const [filterUniType, setFilterUniType] = useState('')
|
||||
const [filterPositionType, setFilterPositionType] = useState('')
|
||||
const [filterProfessorsOnly, setFilterProfessorsOnly] = useState(false)
|
||||
|
||||
// Pagination
|
||||
const [total, setTotal] = useState(0)
|
||||
const [offset, setOffset] = useState(0)
|
||||
const limit = 20
|
||||
|
||||
useEffect(() => {
|
||||
fetchStats()
|
||||
fetchUniversities()
|
||||
}, [])
|
||||
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
const res = await fetch(`${EDU_SEARCH_API}/api/v1/staff/stats`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setStats(data)
|
||||
}
|
||||
} catch {
|
||||
// Stats not critical
|
||||
}
|
||||
}
|
||||
|
||||
const fetchUniversities = async () => {
|
||||
try {
|
||||
const res = await fetch(`${EDU_SEARCH_API}/api/v1/universities`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setUniversities(data.universities || [])
|
||||
}
|
||||
} catch {
|
||||
// Universities not critical
|
||||
}
|
||||
}
|
||||
|
||||
const searchStaff = useCallback(async (newOffset = 0) => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams()
|
||||
if (searchQuery) params.append('q', searchQuery)
|
||||
if (filterState) params.append('state', filterState)
|
||||
if (filterUniType) params.append('uni_type', filterUniType)
|
||||
if (filterPositionType) params.append('position_type', filterPositionType)
|
||||
if (filterProfessorsOnly) params.append('is_professor', 'true')
|
||||
params.append('limit', limit.toString())
|
||||
params.append('offset', newOffset.toString())
|
||||
|
||||
const res = await fetch(`${EDU_SEARCH_API}/api/v1/staff/search?${params}`)
|
||||
if (!res.ok) throw new Error('Search failed')
|
||||
|
||||
const data = await res.json()
|
||||
setStaff(data.staff || [])
|
||||
setTotal(data.total || 0)
|
||||
setOffset(newOffset)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Search failed')
|
||||
setStaff([])
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [searchQuery, filterState, filterUniType, filterPositionType, filterProfessorsOnly])
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
searchStaff(0)
|
||||
}, 300)
|
||||
return () => clearTimeout(timer)
|
||||
}, [searchStaff])
|
||||
|
||||
const fetchPublications = async (staffId: string) => {
|
||||
try {
|
||||
const res = await fetch(`${EDU_SEARCH_API}/api/v1/staff/${staffId}/publications`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setPublications(data.publications || [])
|
||||
}
|
||||
} catch {
|
||||
setPublications([])
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelectStaff = (member: StaffMember) => {
|
||||
setSelectedStaff(member)
|
||||
fetchPublications(member.id)
|
||||
}
|
||||
|
||||
return {
|
||||
searchQuery, setSearchQuery,
|
||||
staff, selectedStaff, publications, stats,
|
||||
loading, error,
|
||||
filterState, setFilterState,
|
||||
filterUniType, setFilterUniType,
|
||||
filterPositionType, setFilterPositionType,
|
||||
filterProfessorsOnly, setFilterProfessorsOnly,
|
||||
total, offset, limit,
|
||||
searchStaff, handleSelectStaff,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user