필터 UI 수정 및 대시보드 연동

This commit is contained in:
2025-12-11 11:21:07 +09:00
parent 886aa9abd9
commit a673fd9429
5 changed files with 430 additions and 140 deletions

View File

@@ -16,6 +16,7 @@ import {
YAxis
} from 'recharts'
import { genomeApi, TraitRankDto } from "@/lib/api/genome.api"
import { useGlobalFilter } from "@/contexts/GlobalFilterContext"
// 카테고리 색상 (모던 & 다이나믹 - 생동감 있는 색상)
const CATEGORY_COLORS: Record<string, string> = {
@@ -184,9 +185,23 @@ export function NormalDistributionChart({
chartFilterTrait: externalChartFilterTrait,
onChartFilterTraitChange
}: NormalDistributionChartProps) {
const { filters } = useGlobalFilter()
// 필터에서 고정된 첫 번째 형질 (없으면 첫 번째 선택된 형질, 없으면 '도체중')
const firstPinnedTrait = filters.pinnedTraits?.[0] || selectedTraitData[0]?.name || '도체중'
// 차트 필터 - 선택된 형질 또는 전체 선발지수 (외부 제어 가능)
const [internalChartFilterTrait, setInternalChartFilterTrait] = useState<string>('overall')
// 필터 비활성 시 기본값은 첫 번째 고정 형질
const [internalChartFilterTrait, setInternalChartFilterTrait] = useState<string>(() => {
return filters.isActive ? 'overall' : firstPinnedTrait
})
// 필터 활성 상태 변경 시 기본값 업데이트
useEffect(() => {
if (!filters.isActive && internalChartFilterTrait === 'overall') {
setInternalChartFilterTrait(firstPinnedTrait)
}
}, [filters.isActive, firstPinnedTrait])
// 외부에서 제어하면 외부 값 사용, 아니면 내부 상태 사용
const chartFilterTrait = externalChartFilterTrait ?? internalChartFilterTrait
@@ -366,12 +381,15 @@ export function NormalDistributionChart({
<SelectValue placeholder="전체 선발지수" />
</SelectTrigger>
<SelectContent>
<SelectItem value="overall"> </SelectItem>
{selectedTraitData.length > 0 && (
{filters.isActive && (
<SelectItem value="overall"> </SelectItem>
)}
{/* 모든 형질 표시 (필터 설정과 무관) */}
{allTraits.length > 0 && (
<>
{/* 카테고리별로 그룹핑 */}
{(['성장', '생산', '체형', '무게', '비율'] as const).map((category) => {
const categoryTraits = selectedTraitData.filter(t => t.category === category)
const categoryTraits = allTraits.filter(t => t.category === category)
if (categoryTraits.length === 0) return null
return (
<div key={category}>

View File

@@ -193,8 +193,21 @@ export default function CowOverviewPage() {
const [highlightMode, setHighlightMode] = useState<'farm' | 'region' | null>(null)
const distributionChartRef = useRef<HTMLDivElement>(null)
// 필터에서 고정된 첫 번째 형질 (없으면 '도체중')
const firstPinnedTrait = filters.pinnedTraits?.[0] || '도체중'
// 차트 형질 필터 (전체 선발지수 또는 개별 형질)
const [chartFilterTrait, setChartFilterTrait] = useState<string>('overall')
// 필터 비활성 시 기본값은 첫 번째 고정 형질
const [chartFilterTrait, setChartFilterTrait] = useState<string>(() => {
return filters.isActive ? 'overall' : firstPinnedTrait
})
// 필터 활성 상태 변경 시 기본값 업데이트
useEffect(() => {
if (!filters.isActive && chartFilterTrait === 'overall') {
setChartFilterTrait(firstPinnedTrait)
}
}, [filters.isActive, firstPinnedTrait, chartFilterTrait])
// 유전자 탭 필터 상태
const [geneSearchKeyword, setGeneSearchKeyword] = useState('')

View File

@@ -16,6 +16,7 @@ import {
import { apiClient, farmApi } from "@/lib/api"
import { DashboardStatsDto, FarmRegionRankingDto, YearlyTraitTrendDto, genomeApi } from "@/lib/api/genome.api"
import { useAuthStore } from "@/store/auth-store"
import { useGlobalFilter } from "@/contexts/GlobalFilterContext"
import {
AlertCircle,
CheckCircle2,
@@ -57,23 +58,50 @@ const TRAIT_CATEGORIES: Record<string, string[]> = {
export default function DashboardPage() {
const { user } = useAuthStore()
const { filters } = useGlobalFilter()
const [farmNo, setFarmNo] = useState<number | null>(null)
const [loading, setLoading] = useState(true)
const [stats, setStats] = useState<DashboardStatsDto | null>(null)
const [farmRanking, setFarmRanking] = useState<FarmRegionRankingDto | null>(null)
// 필터에서 고정된 첫 번째 형질 (없으면 '도체중')
const firstPinnedTrait = filters.pinnedTraits?.[0] || '도체중'
// 연도별 육종가 추이 관련 state
const [selectedTrait, setSelectedTrait] = useState<string>(() => {
if (typeof window !== 'undefined') {
return localStorage.getItem('dashboard_trait') || '도체중'
return localStorage.getItem('dashboard_trait') || firstPinnedTrait
}
return '도체중'
return firstPinnedTrait
})
const [traitTrendData, setTraitTrendData] = useState<YearlyTraitTrendDto | null>(null)
const [traitTrendLoading, setTraitTrendLoading] = useState(false)
// 보은군 내 농가 위치 차트 분포기준 (선발지수 or 개별 형질)
const [distributionBasis, setDistributionBasis] = useState<string>('overall')
// 필터 활성 시 'overall', 비활성 시 고정된 첫 번째 형질
const [distributionBasis, setDistributionBasis] = useState<string>(() => {
return filters.isActive ? 'overall' : firstPinnedTrait
})
// 필터 변경 시 기본값 업데이트
useEffect(() => {
if (!filters.isActive && distributionBasis === 'overall') {
setDistributionBasis(firstPinnedTrait)
}
}, [filters.isActive, distributionBasis, firstPinnedTrait])
// 필터에서 고정된 형질이 변경되면 selectedTrait도 업데이트
useEffect(() => {
if (filters.pinnedTraits && filters.pinnedTraits.length > 0) {
const newFirstPinned = filters.pinnedTraits[0]
// 첫 번째 고정 형질로 변경
setSelectedTrait(newFirstPinned)
// distributionBasis가 overall이 아니면 첫 번째 고정 형질로 변경
if (distributionBasis !== 'overall') {
setDistributionBasis(newFirstPinned)
}
}
}, [filters.pinnedTraits])
// 모든 형질 목록 (평탄화)
const allTraits = Object.entries(TRAIT_CATEGORIES).flatMap(([cat, traits]) =>
@@ -431,7 +459,9 @@ export default function DashboardPage() {
<SelectValue placeholder="전체 선발지수" />
</SelectTrigger>
<SelectContent>
<SelectItem value="overall" className="text-sm font-medium"> </SelectItem>
{filters.isActive && (
<SelectItem value="overall" className="text-sm font-medium"> </SelectItem>
)}
{Object.entries(TRAIT_CATEGORIES).map(([category, traits]) => (
<div key={category}>
<div className="px-2 py-1.5 text-sm font-semibold text-slate-500 bg-slate-50">{category}</div>