파일 정리
This commit is contained in:
@@ -182,9 +182,6 @@ function MyCowContent() {
|
||||
|
||||
setError(null)
|
||||
|
||||
// 마커 타입 정보 (gene.api 제거됨 - 추후 백엔드 구현 시 복구)
|
||||
const currentMarkerTypes = markerTypes
|
||||
|
||||
// 전역 필터 + 랭킹 모드를 기반으로 랭킹 옵션 구성
|
||||
// 타입을 any로 지정하여 백엔드 API와의 호환성 유지
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -279,94 +276,24 @@ function MyCowContent() {
|
||||
ranking?: { traits?: { traitName: string; traitEbv: number | null; traitVal: number | null }[] }; // 랭킹 형질
|
||||
}
|
||||
const cowsWithMockGenes = response.items.map((item: RankingItem) => {
|
||||
// 백엔드에서 genes 객체를 배열로 변환
|
||||
// genes 객체 형식: { "PLAG1": 2, "NCAPG": 1, ... }
|
||||
// 배열 형식으로 변환: [{ name: "PLAG1", genotype: "AA", favorable: true }, ...]
|
||||
let genesArray = []
|
||||
|
||||
if (item.entity.genes && typeof item.entity.genes === 'object') {
|
||||
// 백엔드 genes 객체를 배열로 변환
|
||||
genesArray = Object.entries(item.entity.genes).map(([markerName, count]) => {
|
||||
const favorableCount = count as number
|
||||
let genotype = 'N/A'
|
||||
let favorable = false
|
||||
|
||||
// favorableCount에 따라 유전자형 결정
|
||||
if (favorableCount === 2) {
|
||||
genotype = 'AA' // 동형 접합 (유리)
|
||||
favorable = true
|
||||
} else if (favorableCount === 1) {
|
||||
genotype = 'AG' // 이형 접합 (중간)
|
||||
favorable = true
|
||||
} else {
|
||||
genotype = 'GG' // 동형 접합 (불리)
|
||||
favorable = false
|
||||
}
|
||||
|
||||
return {
|
||||
name: markerName,
|
||||
genotype,
|
||||
favorable,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 백엔드에서 genes 데이터가 없으면 mock 생성
|
||||
genesArray = generateMockGenes()
|
||||
}
|
||||
|
||||
// currentMarkerTypes를 사용하여 동적으로 육량형/육질형 개수 계산
|
||||
// 동형접합(AA)과 이형접합(AG)을 구분하여 계산
|
||||
const isHomozygous = (genotype: string) => genotype.length === 2 && genotype[0] === genotype[1]
|
||||
|
||||
const quantityHomoCount = genesArray.filter(g =>
|
||||
currentMarkerTypes[g.name] === 'QTY' && g.favorable && isHomozygous(g.genotype)
|
||||
).length
|
||||
const quantityHeteroCount = genesArray.filter(g =>
|
||||
currentMarkerTypes[g.name] === 'QTY' && g.favorable && !isHomozygous(g.genotype)
|
||||
).length
|
||||
const qualityHomoCount = genesArray.filter(g =>
|
||||
currentMarkerTypes[g.name] === 'QLT' && g.favorable && isHomozygous(g.genotype)
|
||||
).length
|
||||
const qualityHeteroCount = genesArray.filter(g =>
|
||||
currentMarkerTypes[g.name] === 'QLT' && g.favorable && !isHomozygous(g.genotype)
|
||||
).length
|
||||
|
||||
return {
|
||||
...item.entity, // 실제 cow 데이터
|
||||
rank: item.rank, // 백엔드에서 계산한 랭킹
|
||||
rankScore: item.sortValue, // 백엔드에서 계산한 점수
|
||||
grade: item.grade, // 백엔드에서 계산한 등급 (A~E)
|
||||
genes: genesArray,
|
||||
quantityGeneCount: quantityHomoCount + quantityHeteroCount,
|
||||
qualityGeneCount: qualityHomoCount + qualityHeteroCount,
|
||||
quantityHomoCount,
|
||||
quantityHeteroCount,
|
||||
qualityHomoCount,
|
||||
qualityHeteroCount,
|
||||
// 유전체 점수는 sortValue에서 가져옴 (백엔드 랭킹 엔진이 계산한 값)
|
||||
...item.entity,
|
||||
rank: item.rank,
|
||||
rankScore: item.sortValue,
|
||||
grade: item.grade,
|
||||
genomeScore: item.sortValue,
|
||||
geneScore: item.compositeScores?.geneScore,
|
||||
// 번식 정보 (백엔드에서 가져옴 - 암소만)
|
||||
// 번식 정보
|
||||
calvingCount: item.entity.calvingCount,
|
||||
bcs: item.entity.bcs,
|
||||
inseminationCount: item.entity.inseminationCount,
|
||||
// 근친도 (백엔드에서 계산된 근친계수 백분율)
|
||||
inbreedingPercent: item.entity.inbreedingPercent ?? 0,
|
||||
// 아비 KPN 번호 (genome trait에서 가져옴)
|
||||
sireKpn: item.entity.sireKpn ?? null,
|
||||
// 분석일자
|
||||
anlysDt: item.entity.anlysDt ?? null,
|
||||
// 분석불가 사유
|
||||
unavailableReason: item.entity.unavailableReason ?? null,
|
||||
// 번식능력검사(MPT) 여부
|
||||
hasMpt: item.entity.hasMpt ?? false,
|
||||
// MPT 검사일
|
||||
mptTestDt: item.entity.mptTestDt ?? null,
|
||||
// MPT 월령
|
||||
mptMonthAge: item.entity.mptMonthAge ?? null,
|
||||
//====================================================================================================================
|
||||
// 형질 데이터 (백엔드에서 계산됨, 형질명 → 표준화육종가 매핑)
|
||||
// 백엔드 응답: { traitName: string, traitVal: number, traitEbv: number, traitPercentile: number }
|
||||
// 형질 데이터
|
||||
traits: item.ranking?.traits?.reduce((acc: Record<string,
|
||||
{ breedVal: number | null, traitVal: number | null }>, t: { traitName: string; traitEbv: number | null; traitVal: number | null }) => {
|
||||
acc[t.traitName] = { breedVal: t.traitEbv, traitVal: t.traitVal };
|
||||
@@ -389,98 +316,6 @@ function MyCowContent() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [filters, rankingMode, isFilterSet])
|
||||
|
||||
// Mock 유전자 생성 함수 (실제로는 API에서 가져와야 함)
|
||||
const generateMockGenes = () => {
|
||||
// 모든 소가 다양한 유전자를 가지도록 더 많은 유전자 풀 생성
|
||||
const genePool = [
|
||||
// 육량형 유전자
|
||||
{ name: 'PLAG1', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA', 'AG'] },
|
||||
{ name: 'NCAPG', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA', 'AG'] },
|
||||
{ name: 'LCORL', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA', 'AG'] },
|
||||
{ name: 'MSTN', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA'] },
|
||||
{ name: 'IGF1', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA', 'AG'] },
|
||||
{ name: 'GH1', genotypes: ['CC', 'CT', 'TT'], favorable: ['CC', 'CT'] },
|
||||
{ name: 'LAP3', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA'] },
|
||||
{ name: 'ARRDC3', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA', 'AG'] },
|
||||
// 육질형 유전자
|
||||
{ name: 'CAPN1', genotypes: ['CC', 'CG', 'GG'], favorable: ['CC', 'CG'] },
|
||||
{ name: 'CAST', genotypes: ['AA', 'AG', 'GG'], favorable: ['GG', 'AG'] },
|
||||
{ name: 'FASN', genotypes: ['AA', 'AG', 'GG'], favorable: ['GG', 'AG'] },
|
||||
{ name: 'SCD', genotypes: ['AA', 'AV', 'VV'], favorable: ['VV', 'AV'] },
|
||||
{ name: 'FABP4', genotypes: ['AA', 'AG', 'GG'], favorable: ['AA', 'AG'] },
|
||||
{ name: 'SREBP1', genotypes: ['CC', 'CT', 'TT'], favorable: ['CC', 'CT'] },
|
||||
{ name: 'DGAT1', genotypes: ['AA', 'AK', 'KK'], favorable: ['KK', 'AK'] },
|
||||
{ name: 'LEP', genotypes: ['CC', 'CT', 'TT'], favorable: ['TT', 'CT'] },
|
||||
]
|
||||
|
||||
// 모든 유전자를 포함 (랜덤 유전자형)
|
||||
return genePool.map(gene => {
|
||||
const genotype = gene.genotypes[Math.floor(Math.random() * gene.genotypes.length)]
|
||||
return {
|
||||
name: gene.name,
|
||||
genotype,
|
||||
favorable: gene.favorable.includes(genotype),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 유전자형 판단 및 스타일 정의
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 동형접합 여부 판단
|
||||
* AA, GG, CC, TT 등 → true
|
||||
* AG, CT, AK 등 → false
|
||||
*/
|
||||
const isHomozygous = (genotype: string): boolean => {
|
||||
return genotype.length === 2 && genotype[0] === genotype[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* 유전자 뱃지 스타일 정의
|
||||
* @param genotype 유전자형 (AA, AG, GG 등)
|
||||
* @param favorable 우량 유전자 여부
|
||||
* @param geneCategory 유전자 카테고리 ('QTY': 육량형, 'QLT': 육질형)
|
||||
*/
|
||||
type GeneBadgeStyle = {
|
||||
className: string
|
||||
icon: 'star' | 'circle' | 'double-circle' | 'minus' | 'none'
|
||||
}
|
||||
|
||||
const getGeneBadgeStyle = (
|
||||
genotype: string,
|
||||
favorable: boolean,
|
||||
geneCategory: 'QTY' | 'QLT'
|
||||
): GeneBadgeStyle => {
|
||||
const isHomo = isHomozygous(genotype)
|
||||
|
||||
// 1. 동형접합 우량 (AA형) → 진한 색 (육량: 파랑, 육질: 주황)
|
||||
if (isHomo && favorable) {
|
||||
return {
|
||||
className: geneCategory === 'QTY'
|
||||
? 'bg-blue-600 text-white border-blue-700'
|
||||
: 'bg-orange-600 text-white border-orange-700',
|
||||
icon: 'none',
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 이형접합 우량 (AG형) → 중간 색 (육량: 파랑, 육질: 주황)
|
||||
if (!isHomo && favorable) {
|
||||
return {
|
||||
className: geneCategory === 'QTY'
|
||||
? 'bg-blue-400 text-white border-blue-500'
|
||||
: 'bg-orange-400 text-white border-orange-500',
|
||||
icon: 'none',
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 불량형 (GG형) → 연한 회색
|
||||
return {
|
||||
className: 'bg-gray-300 text-gray-600 border-gray-400',
|
||||
icon: 'none',
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 컬럼 스타일은 globals.css의 CSS 변수로 관리됨
|
||||
@@ -962,10 +797,10 @@ function MyCowContent() {
|
||||
선발지수
|
||||
</th>
|
||||
{selectedDisplayGenes.length > 0 && (
|
||||
<th className="cow-table-header bg-blue-50" style={{ width: '140px' }}>유전자형</th>
|
||||
<th className="cow-table-header" style={{ width: '140px' }}>유전자형</th>
|
||||
)}
|
||||
{selectedDisplayTraits.length > 0 && (
|
||||
<th className="cow-table-header bg-teal-50" style={{ width: '140px' }}>형질</th>
|
||||
<th className="cow-table-header" style={{ width: '140px' }}>형질</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1022,15 +857,20 @@ function MyCowContent() {
|
||||
{(() => {
|
||||
// 번식능력만 있는 개체 판단
|
||||
const hasMptOnly = cow.hasMpt && !cow.genomeScore && !cow.anlysDt
|
||||
// 번식능력 탭이거나 번식능력만 있는 개체: MPT 월령 사용
|
||||
// 번식능력 탭이거나 번식능력만 있는 개체: MPT 검사일 기준 월령
|
||||
if (analysisFilter === 'mptOnly' || hasMptOnly) {
|
||||
return cow.mptMonthAge ? `${cow.mptMonthAge}개월` : '-'
|
||||
if (cow.cowBirthDt && cow.mptTestDt) {
|
||||
const birthDate = new Date(cow.cowBirthDt)
|
||||
const refDate = new Date(cow.mptTestDt)
|
||||
return `${Math.floor((refDate.getTime() - birthDate.getTime()) / (1000 * 60 * 60 * 24 * 30.44))}개월`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
// 유전체 분석일 기준 월령
|
||||
if (cow.cowBirthDt && cow.anlysDt) {
|
||||
const birthDate = new Date(cow.cowBirthDt)
|
||||
const refDate = new Date(cow.anlysDt)
|
||||
const ageInMonths = Math.floor((refDate.getTime() - birthDate.getTime()) / (1000 * 60 * 60 * 24 * 30.44))
|
||||
return `${ageInMonths}개월`
|
||||
return `${Math.floor((refDate.getTime() - birthDate.getTime()) / (1000 * 60 * 60 * 24 * 30.44))}개월`
|
||||
}
|
||||
return '-'
|
||||
})()}
|
||||
@@ -1079,7 +919,7 @@ function MyCowContent() {
|
||||
</td>
|
||||
{selectedDisplayGenes.length > 0 && (
|
||||
<td
|
||||
className="py-2 px-2 text-sm bg-blue-50/30"
|
||||
className="py-2 px-2 text-sm"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{(() => {
|
||||
@@ -1092,15 +932,17 @@ function MyCowContent() {
|
||||
{displayGenes.map((geneName) => {
|
||||
const gene = cow.genes?.find(g => g.name === geneName)
|
||||
const genotype = gene?.genotype || '-'
|
||||
const favorable = gene?.favorable || false
|
||||
const geneCategory = markerTypes[geneName] as 'QTY' | 'QLT'
|
||||
const badgeStyle = gene ? getGeneBadgeStyle(genotype, favorable, geneCategory) : null
|
||||
// 육량형: 파랑, 육질형: 주황
|
||||
const badgeClass = geneCategory === 'QTY'
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-orange-500 text-white'
|
||||
|
||||
return (
|
||||
<div key={geneName} className="flex items-center gap-2">
|
||||
<span className="text-xs text-slate-600 min-w-[60px]">{geneName}</span>
|
||||
{gene ? (
|
||||
<Badge className={`text-xs font-semibold ${badgeStyle?.className}`}>
|
||||
<Badge className={`text-xs font-semibold ${badgeClass}`}>
|
||||
{genotype}
|
||||
</Badge>
|
||||
) : (
|
||||
@@ -1134,7 +976,7 @@ function MyCowContent() {
|
||||
)}
|
||||
{selectedDisplayTraits.length > 0 && (
|
||||
<td
|
||||
className="py-2 px-2 text-sm bg-teal-50/30"
|
||||
className="py-2 px-2 text-sm"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex flex-col items-start gap-1.5">
|
||||
@@ -1264,10 +1106,14 @@ function MyCowContent() {
|
||||
{(() => {
|
||||
// 번식능력만 있는 개체 판단
|
||||
const hasMptOnly = cow.hasMpt && !cow.genomeScore && !cow.anlysDt
|
||||
// 번식능력 탭이거나 번식능력만 있는 개체: MPT 월령 사용
|
||||
// 번식능력 탭이거나 번식능력만 있는 개체: MPT 검사일 기준 월령
|
||||
if (analysisFilter === 'mptOnly' || hasMptOnly) {
|
||||
return cow.mptMonthAge ? `${cow.mptMonthAge}개월` : '-'
|
||||
if (cow.cowBirthDt && cow.mptTestDt) {
|
||||
return `${Math.floor((new Date(cow.mptTestDt).getTime() - new Date(cow.cowBirthDt).getTime()) / (1000 * 60 * 60 * 24 * 30.44))}개월`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
// 유전체 분석일 기준 월령
|
||||
if (cow.cowBirthDt && cow.anlysDt) {
|
||||
return `${Math.floor((new Date(cow.anlysDt).getTime() - new Date(cow.cowBirthDt).getTime()) / (1000 * 60 * 60 * 24 * 30.44))}개월`
|
||||
}
|
||||
@@ -1344,12 +1190,14 @@ function MyCowContent() {
|
||||
{displayGenes.map((geneName) => {
|
||||
const gene = cow.genes?.find(g => g.name === geneName)
|
||||
const geneCategory = markerTypes[geneName] as 'QTY' | 'QLT'
|
||||
const genotype = gene?.genotype || 'GG'
|
||||
const favorable = gene?.favorable || false
|
||||
const badgeStyle = getGeneBadgeStyle(genotype, favorable, geneCategory)
|
||||
const genotype = gene?.genotype || '-'
|
||||
// 육량형: 파랑, 육질형: 주황
|
||||
const badgeClass = geneCategory === 'QTY'
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-orange-500 text-white'
|
||||
|
||||
return (
|
||||
<Badge key={geneName} className={`text-xs px-1.5 py-0.5 font-medium ${badgeStyle.className}`}>
|
||||
<Badge key={geneName} className={`text-xs px-1.5 py-0.5 font-medium ${badgeClass}`}>
|
||||
{geneName} {genotype}
|
||||
</Badge>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user