207 lines
7.6 KiB
TypeScript
207 lines
7.6 KiB
TypeScript
'use client'
|
|
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { useEffect, useState } from "react"
|
|
import { useAnalysisYear } from "@/contexts/AnalysisYearContext"
|
|
import { useFilterStore } from "@/store/filter-store"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Filter, ChevronDown, ChevronUp } from "lucide-react"
|
|
|
|
interface GeneData {
|
|
geneName: string
|
|
geneType: '육량' | '육질' // 유전자 분류
|
|
farmRate: number // 우리 농장 우량형(AA) 보유율
|
|
regionAvgRate: number // 지역 평균
|
|
}
|
|
|
|
interface GenePossessionStatusProps {
|
|
farmNo: number | null
|
|
}
|
|
|
|
export function GenePossessionStatus({ farmNo }: GenePossessionStatusProps) {
|
|
const { selectedYear } = useAnalysisYear()
|
|
const { filters } = useFilterStore()
|
|
const [allGenes, setAllGenes] = useState<GeneData[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [isExpanded, setIsExpanded] = useState(false)
|
|
|
|
// 선택된 유전자 확인
|
|
const selectedGenes = filters.selectedGenes || []
|
|
const hasFilter = selectedGenes.length > 0
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
setLoading(true)
|
|
|
|
// TODO: 백엔드 API 연동 시 실제 데이터 fetch
|
|
// 현재는 목업 데이터 사용 (전체 유전자 리스트)
|
|
const mockAllGenes: GeneData[] = [
|
|
// 육량 관련
|
|
{ geneName: 'PLAG1', geneType: '육량', farmRate: 85, regionAvgRate: 72 },
|
|
{ geneName: 'NCAPG', geneType: '육량', farmRate: 82, regionAvgRate: 75 },
|
|
{ geneName: 'LCORL', geneType: '육량', farmRate: 78, regionAvgRate: 68 },
|
|
{ geneName: 'LAP3', geneType: '육량', farmRate: 65, regionAvgRate: 58 },
|
|
|
|
// 육질 관련
|
|
{ geneName: 'FABP4', geneType: '육질', farmRate: 88, regionAvgRate: 70 },
|
|
{ geneName: 'SCD', geneType: '육질', farmRate: 80, regionAvgRate: 72 },
|
|
{ geneName: 'DGAT1', geneType: '육질', farmRate: 75, regionAvgRate: 65 },
|
|
{ geneName: 'FASN', geneType: '육질', farmRate: 70, regionAvgRate: 62 },
|
|
{ geneName: 'CAPN1', geneType: '육질', farmRate: 82, regionAvgRate: 68 },
|
|
{ geneName: 'CAST', geneType: '육질', farmRate: 77, regionAvgRate: 64 },
|
|
]
|
|
|
|
// 선택된 유전자 중 목업 데이터에 없는 유전자가 있다면 추가
|
|
if (selectedGenes.length > 0) {
|
|
selectedGenes.forEach(geneName => {
|
|
if (!mockAllGenes.find(g => g.geneName === geneName)) {
|
|
// 선택된 유전자가 목업 데이터에 없으면 기본값으로 추가
|
|
mockAllGenes.push({
|
|
geneName: geneName,
|
|
geneType: geneName.includes('PLAG') || geneName.includes('NCAPG') || geneName.includes('LCORL') || geneName.includes('LAP') ? '육량' : '육질',
|
|
farmRate: Math.floor(Math.random() * 30) + 60, // 60-90 사이 랜덤값
|
|
regionAvgRate: Math.floor(Math.random() * 20) + 55, // 55-75 사이 랜덤값
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
setAllGenes(mockAllGenes)
|
|
setLoading(false)
|
|
}
|
|
|
|
fetchData()
|
|
}, [selectedYear, farmNo, selectedGenes])
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="h-[300px] flex items-center justify-center">
|
|
<p className="text-muted-foreground">데이터 로딩 중...</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!farmNo) {
|
|
return (
|
|
<div className="h-[300px] flex items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-muted-foreground mb-2">농장 정보가 없습니다</p>
|
|
<p className="text-sm text-muted-foreground">로그인 후 다시 시도해주세요</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// 필터에 따라 표시할 유전자 선택
|
|
const allDisplayGenes = hasFilter
|
|
? allGenes.filter(g => selectedGenes.includes(g.geneName))
|
|
: allGenes.slice(0, 6) // TOP 6 (보유율 높은 순으로 이미 정렬됨)
|
|
|
|
// 접기/펼치기 적용 (4개 기준)
|
|
// 단, 선택된 유전자가 있을 때는 모두 표시
|
|
const DISPLAY_LIMIT = 4
|
|
const displayGenes = hasFilter || isExpanded ? allDisplayGenes : allDisplayGenes.slice(0, DISPLAY_LIMIT)
|
|
const hasMore = !hasFilter && allDisplayGenes.length > DISPLAY_LIMIT
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* 필터 배지 표시 */}
|
|
{hasFilter && (
|
|
<div className="flex items-center gap-2 flex-wrap">
|
|
<div className="flex items-center gap-1.5 text-xs text-gray-600">
|
|
<Filter className="h-3.5 w-3.5" />
|
|
<span className="font-medium">타겟 유전자:</span>
|
|
</div>
|
|
{selectedGenes.map(gene => (
|
|
<Badge
|
|
key={gene}
|
|
variant="secondary"
|
|
className="text-xs font-medium bg-blue-50 text-blue-700 border-blue-200"
|
|
>
|
|
{gene}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* 유전자별 바 차트 */}
|
|
<div className="space-y-2.5">
|
|
{displayGenes.map((gene, index) => (
|
|
<div key={gene.geneName} className="space-y-1">
|
|
{/* 유전자명 + 타입 배지 */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm font-semibold text-gray-800 min-w-[60px]">
|
|
{gene.geneName}
|
|
</span>
|
|
<Badge
|
|
variant="outline"
|
|
className={`text-xs px-2 py-0 ${
|
|
gene.geneType === '육량'
|
|
? 'bg-blue-50 text-blue-700 border-blue-200'
|
|
: 'bg-purple-50 text-purple-700 border-purple-200'
|
|
}`}
|
|
>
|
|
{gene.geneType}
|
|
</Badge>
|
|
</div>
|
|
<span className="text-sm font-bold text-gray-900">
|
|
{gene.farmRate}%
|
|
</span>
|
|
</div>
|
|
|
|
{/* 프로그레스 바 */}
|
|
<div className="relative h-7 bg-gray-100 rounded-full overflow-hidden">
|
|
{/* 우리 농장 */}
|
|
<div
|
|
className={`absolute h-full transition-all duration-800 ${
|
|
gene.geneType === '육량' ? 'bg-blue-500' : 'bg-purple-500'
|
|
}`}
|
|
style={{ width: `${gene.farmRate}%` }}
|
|
/>
|
|
{/* 지역 평균 표시 (점선) */}
|
|
<div
|
|
className="absolute h-full border-l-2 border-dashed border-gray-400"
|
|
style={{ left: `${gene.regionAvgRate}%` }}
|
|
title={`지역 평균: ${gene.regionAvgRate}%`}
|
|
/>
|
|
</div>
|
|
|
|
{/* 지역 평균 레이블 */}
|
|
<div className="flex justify-end">
|
|
<span className="text-xs text-gray-500">
|
|
지역 평균: {gene.regionAvgRate}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* 더보기/접기 버튼 */}
|
|
{hasMore && (
|
|
<div className="flex justify-center">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
className="text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-100"
|
|
>
|
|
{isExpanded ? (
|
|
<>
|
|
<ChevronUp className="h-4 w-4 mr-1" />
|
|
접기
|
|
</>
|
|
) : (
|
|
<>
|
|
<ChevronDown className="h-4 w-4 mr-1" />
|
|
나머지 {allDisplayGenes.length - DISPLAY_LIMIT}개 더보기
|
|
</>
|
|
)}
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|