필터 및 화면 수정사항 반영
This commit is contained in:
@@ -542,10 +542,10 @@ export function CategoryEvaluationCard({
|
||||
const selectedTrait = traitChartData.find(t => t.name === selectedTraitName)
|
||||
if (!selectedTrait) return null
|
||||
return (
|
||||
<div className="mx-4 mb-4 p-4 bg-slate-50 rounded-xl border border-slate-200 animate-fade-in">
|
||||
<div className="mx-2 mb-4 p-3 bg-slate-50 rounded-xl border border-slate-200 animate-fade-in">
|
||||
{/* 헤더: 형질명 + 닫기 */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="px-4 py-1.5 bg-slate-200 text-slate-700 text-base font-bold rounded-full">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="px-4 py-1.5 bg-slate-200 text-slate-700 text-sm font-bold rounded-full">
|
||||
{selectedTrait.shortName} 조회 기준
|
||||
</span>
|
||||
<button
|
||||
@@ -556,25 +556,25 @@ export function CategoryEvaluationCard({
|
||||
</button>
|
||||
</div>
|
||||
{/* 3개 카드 그리드 */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{/* 보은군 카드 */}
|
||||
<div className="flex flex-col items-center justify-center p-3 bg-white rounded-xl border-2 border-emerald-300 shadow-sm">
|
||||
<span className="text-xs text-muted-foreground mb-1 font-medium">보은군 평균</span>
|
||||
<span className="text-lg font-bold text-emerald-600">
|
||||
<div className="flex flex-col items-center justify-center px-3 py-4 bg-white rounded-xl border-2 border-emerald-300 shadow-sm">
|
||||
<span className="text-xs text-slate-600 mb-1 font-semibold whitespace-nowrap">보은군 평균</span>
|
||||
<span className="text-xl font-bold text-emerald-600">
|
||||
{selectedTrait.regionEpd?.toFixed(2) ?? '-'}
|
||||
</span>
|
||||
</div>
|
||||
{/* 농가 카드 */}
|
||||
<div className="flex flex-col items-center justify-center p-3 bg-white rounded-xl border-2 border-[#1F3A8F]/30 shadow-sm">
|
||||
<span className="text-xs text-muted-foreground mb-1 font-medium">농가 평균</span>
|
||||
<span className="text-lg font-bold text-[#1F3A8F]">
|
||||
<div className="flex flex-col items-center justify-center px-3 py-4 bg-white rounded-xl border-2 border-[#1F3A8F]/30 shadow-sm">
|
||||
<span className="text-xs text-slate-600 mb-1 font-semibold whitespace-nowrap">농가 평균</span>
|
||||
<span className="text-xl font-bold text-[#1F3A8F]">
|
||||
{selectedTrait.farmEpd?.toFixed(2) ?? '-'}
|
||||
</span>
|
||||
</div>
|
||||
{/* 개체 카드 */}
|
||||
<div className="flex flex-col items-center justify-center p-3 bg-white rounded-xl border-2 border-[#1482B0]/30 shadow-sm">
|
||||
<span className="text-xs text-muted-foreground mb-1 font-medium">내 개체</span>
|
||||
<span className="text-lg font-bold text-[#1482B0]">
|
||||
<div className="flex flex-col items-center justify-center px-3 py-4 bg-white rounded-xl border-2 border-[#1482B0]/30 shadow-sm">
|
||||
<span className="text-xs text-slate-600 mb-1 font-semibold whitespace-nowrap">내 개체</span>
|
||||
<span className="text-xl font-bold text-[#1482B0]">
|
||||
{selectedTrait.epd?.toFixed(2) ?? '-'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -811,10 +811,10 @@ export function NormalDistributionChart({
|
||||
return Math.max(chartX + halfWidth + 5, Math.min(x, chartX + chartWidth - halfWidth - 5))
|
||||
}
|
||||
|
||||
// 배지 크기 (더 크게)
|
||||
// 배지 크기 (더 크게) - 모바일에서 텍스트가 한 줄로 나오도록 너비 확보
|
||||
const cowBadgeW = isMobile ? 105 : 135
|
||||
const avgBadgeW = isMobile ? 100 : 135
|
||||
const regionBadgeW = isMobile ? 105 : 145
|
||||
const avgBadgeW = isMobile ? 118 : 135
|
||||
const regionBadgeW = isMobile ? 125 : 145
|
||||
const badgeH = isMobile ? 42 : 48
|
||||
|
||||
// Y 위치 계산 - 겹치지 않게 배치
|
||||
|
||||
@@ -17,6 +17,7 @@ import { GenomeTrait } from "@/types/genome.types"
|
||||
import { useGlobalFilter } from "@/contexts/GlobalFilterContext"
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowUp,
|
||||
BarChart3,
|
||||
CheckCircle2,
|
||||
Download,
|
||||
@@ -156,6 +157,7 @@ export default function CowOverviewPage() {
|
||||
const [geneDataLoading, setGeneDataLoading] = useState(false) // 유전자 데이터 로딩 중
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [activeTab, setActiveTab] = useState<string>('genome')
|
||||
const [showScrollTop, setShowScrollTop] = useState(false)
|
||||
|
||||
// 검사 상태
|
||||
const [hasGenomeData, setHasGenomeData] = useState(false)
|
||||
@@ -220,6 +222,20 @@ export default function CowOverviewPage() {
|
||||
}
|
||||
}, [filters.isActive, firstPinnedTrait, chartFilterTrait])
|
||||
|
||||
// 스크롤 투 탑 버튼 표시 여부
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setShowScrollTop(window.scrollY > 400)
|
||||
}
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
}, [])
|
||||
|
||||
// 맨 위로 스크롤
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
// 유전자 탭 필터 상태
|
||||
const [geneSearchInput, setGeneSearchInput] = useState('') // 실시간 입력값
|
||||
const [geneSearchKeyword, setGeneSearchKeyword] = useState('') // 디바운스된 검색값
|
||||
@@ -1938,6 +1954,17 @@ export default function CowOverviewPage() {
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* 플로팅 맨 위로 버튼 */}
|
||||
{showScrollTop && (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className="fixed bottom-6 right-6 z-50 w-12 h-12 bg-primary text-white rounded-full shadow-lg hover:bg-primary/90 transition-all duration-300 flex items-center justify-center hover:scale-110 active:scale-95"
|
||||
aria-label="맨 위로"
|
||||
>
|
||||
<ArrowUp className="w-6 h-6" />
|
||||
</button>
|
||||
)}
|
||||
</SidebarProvider>
|
||||
</AuthGuard>
|
||||
)
|
||||
|
||||
@@ -973,7 +973,7 @@ function MyCowContent() {
|
||||
})() : '-'}
|
||||
</td>
|
||||
<td className="cow-table-cell">
|
||||
{cow.cowSex === "암" ? "암소" : "수소"}
|
||||
{cow.cowSex === "수" ? "수소" : "암소"}
|
||||
</td>
|
||||
<td className="cow-table-cell">
|
||||
{cow.damCowId && cow.damCowId !== '0' ? cow.damCowId : '-'}
|
||||
@@ -1132,7 +1132,7 @@ function MyCowContent() {
|
||||
<div className="md:hidden space-y-2.5">
|
||||
{paginatedCows.map((cow) => {
|
||||
const rank = getRank(cow)
|
||||
const isFemale = cow.cowSex === '암'
|
||||
const isFemale = cow.cowSex !== '수'
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
162
frontend/src/app/demo/floating-button/page.tsx
Normal file
162
frontend/src/app/demo/floating-button/page.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ArrowUp, ChevronUp, ChevronsUp, MoveUp } from 'lucide-react'
|
||||
|
||||
export default function FloatingButtonDemo() {
|
||||
const [selectedStyle, setSelectedStyle] = useState<number>(1)
|
||||
|
||||
const styles = [
|
||||
{
|
||||
id: 1,
|
||||
name: '기본 프라이머리',
|
||||
className: 'bg-primary text-white shadow-lg hover:bg-primary/90',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '그라데이션 블루',
|
||||
className: 'bg-gradient-to-r from-blue-500 to-indigo-600 text-white shadow-xl shadow-blue-500/30 hover:shadow-blue-500/50',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '글래스모피즘',
|
||||
className: 'bg-white/80 backdrop-blur-md border border-white/50 text-slate-700 shadow-lg hover:bg-white/90',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '아웃라인',
|
||||
className: 'bg-white border-2 border-primary text-primary hover:bg-primary hover:text-white',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '미니멀 다크',
|
||||
className: 'bg-slate-800 text-white shadow-lg hover:bg-slate-700',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '소프트 그레이',
|
||||
className: 'bg-slate-100 text-slate-600 shadow-md hover:bg-slate-200 hover:text-slate-800',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '그린 그라데이션',
|
||||
className: 'bg-gradient-to-r from-emerald-500 to-teal-600 text-white shadow-xl shadow-emerald-500/30',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '네온 퍼플',
|
||||
className: 'bg-violet-600 text-white shadow-xl shadow-violet-500/40 hover:shadow-violet-500/60',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: '오렌지 웜',
|
||||
className: 'bg-gradient-to-r from-orange-400 to-rose-500 text-white shadow-xl shadow-orange-500/30',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: '심플 화이트',
|
||||
className: 'bg-white text-slate-500 shadow-xl border border-slate-200 hover:text-primary hover:border-primary',
|
||||
},
|
||||
]
|
||||
|
||||
const icons = [
|
||||
{ id: 'arrow', icon: ArrowUp, name: 'ArrowUp' },
|
||||
{ id: 'chevron', icon: ChevronUp, name: 'ChevronUp' },
|
||||
{ id: 'chevrons', icon: ChevronsUp, name: 'ChevronsUp' },
|
||||
{ id: 'move', icon: MoveUp, name: 'MoveUp' },
|
||||
]
|
||||
|
||||
const [selectedIcon, setSelectedIcon] = useState('arrow')
|
||||
const SelectedIconComponent = icons.find(i => i.id === selectedIcon)?.icon || ArrowUp
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 p-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl font-bold mb-2">플로팅 버튼 스타일 데모</h1>
|
||||
<p className="text-slate-600 mb-8">원하는 스타일을 선택해보세요. 우하단에 실제 버튼이 표시됩니다.</p>
|
||||
|
||||
{/* 아이콘 선택 */}
|
||||
<div className="mb-8">
|
||||
<h2 className="text-lg font-semibold mb-4">아이콘 선택</h2>
|
||||
<div className="flex gap-3">
|
||||
{icons.map((icon) => {
|
||||
const IconComp = icon.icon
|
||||
return (
|
||||
<button
|
||||
key={icon.id}
|
||||
onClick={() => setSelectedIcon(icon.id)}
|
||||
className={`flex flex-col items-center gap-2 p-4 rounded-xl border-2 transition-all ${
|
||||
selectedIcon === icon.id
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-slate-200 bg-white hover:border-slate-300'
|
||||
}`}
|
||||
>
|
||||
<IconComp className="w-6 h-6" />
|
||||
<span className="text-xs text-slate-600">{icon.name}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 스타일 선택 그리드 */}
|
||||
<h2 className="text-lg font-semibold mb-4">스타일 선택</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-12">
|
||||
{styles.map((style) => (
|
||||
<button
|
||||
key={style.id}
|
||||
onClick={() => setSelectedStyle(style.id)}
|
||||
className={`p-4 rounded-xl border-2 transition-all ${
|
||||
selectedStyle === style.id
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-slate-200 bg-white hover:border-slate-300'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-center mb-3">
|
||||
<div
|
||||
className={`w-12 h-12 rounded-full flex items-center justify-center transition-all ${style.className}`}
|
||||
>
|
||||
<SelectedIconComponent className="w-6 h-6" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm font-medium text-center">{style.name}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 코드 표시 */}
|
||||
<div className="bg-slate-900 rounded-xl p-6 mb-8">
|
||||
<p className="text-slate-400 text-sm mb-2">선택한 스타일 코드:</p>
|
||||
<code className="text-green-400 text-sm break-all">
|
||||
{`className="${styles.find(s => s.id === selectedStyle)?.className}"`}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
{/* 스크롤 테스트용 더미 콘텐츠 */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">스크롤 테스트</h2>
|
||||
<p className="text-slate-600">아래로 스크롤해서 버튼 동작을 확인하세요.</p>
|
||||
{Array.from({ length: 20 }).map((_, i) => (
|
||||
<div key={i} className="p-6 bg-white rounded-xl border border-slate-200">
|
||||
<h3 className="font-semibold mb-2">더미 콘텐츠 #{i + 1}</h3>
|
||||
<p className="text-slate-600">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 실제 플로팅 버튼 */}
|
||||
<button
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||
className={`fixed bottom-6 right-6 z-50 w-14 h-14 rounded-full flex items-center justify-center transition-all duration-300 hover:scale-110 active:scale-95 ${
|
||||
styles.find(s => s.id === selectedStyle)?.className
|
||||
}`}
|
||||
aria-label="맨 위로"
|
||||
>
|
||||
<SelectedIconComponent className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -134,7 +134,7 @@ function SortableTraitItem({
|
||||
<GripVertical className="w-4 h-4 text-slate-400" />
|
||||
</button>
|
||||
{isPinned && <Pin className="w-3 h-3 text-amber-500" fill="currentColor" />}
|
||||
<span className="font-medium text-sm min-w-0 truncate">{id}</span>
|
||||
<span className="font-medium text-sm min-w-0 truncate">{TRAIT_DISPLAY_NAMES[id] || id}</span>
|
||||
<div className="flex items-center gap-1.5 ml-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -221,6 +221,30 @@ const TRAIT_DESCRIPTIONS: Record<string, string> = {
|
||||
'갈비rate': '전체 대비 갈비 비율',
|
||||
}
|
||||
|
||||
// 형질 표시 이름 (DB 키 -> 화면 표시용)
|
||||
const TRAIT_DISPLAY_NAMES: Record<string, string> = {
|
||||
'안심weight': '안심중량',
|
||||
'등심weight': '등심중량',
|
||||
'채끝weight': '채끝중량',
|
||||
'목심weight': '목심중량',
|
||||
'앞다리weight': '앞다리중량',
|
||||
'우둔weight': '우둔중량',
|
||||
'설도weight': '설도중량',
|
||||
'사태weight': '사태중량',
|
||||
'양지weight': '양지중량',
|
||||
'갈비weight': '갈비중량',
|
||||
'안심rate': '안심비율',
|
||||
'등심rate': '등심비율',
|
||||
'채끝rate': '채끝비율',
|
||||
'목심rate': '목심비율',
|
||||
'앞다리rate': '앞다리비율',
|
||||
'우둔rate': '우둔비율',
|
||||
'설도rate': '설도비율',
|
||||
'사태rate': '사태비율',
|
||||
'양지rate': '양지비율',
|
||||
'갈비rate': '갈비비율',
|
||||
}
|
||||
|
||||
type TraitName = keyof typeof DEFAULT_FILTER_SETTINGS.traitWeights
|
||||
|
||||
interface GlobalFilterDialogProps {
|
||||
@@ -973,7 +997,7 @@ export function GlobalFilterDialog({ externalOpen, onExternalOpenChange, geneCou
|
||||
>
|
||||
<Checkbox checked={isSelected} />
|
||||
<div className="flex-1">
|
||||
<span className="font-medium text-sm">{trait}</span>
|
||||
<span className="font-medium text-sm">{TRAIT_DISPLAY_NAMES[trait] || trait}</span>
|
||||
{TRAIT_DESCRIPTIONS[trait] && (
|
||||
<span className="text-xs text-muted-foreground ml-2">{TRAIT_DESCRIPTIONS[trait]}</span>
|
||||
)}
|
||||
|
||||
@@ -266,7 +266,7 @@ function SidebarTrigger({
|
||||
data-slot="sidebar-trigger"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn("size-7 md:size-7", isMobile && "size-9", className)}
|
||||
className={cn("size-7 md:size-7", isMobile && "size-11", className)}
|
||||
onClick={(event) => {
|
||||
onClick?.(event)
|
||||
toggleSidebar()
|
||||
@@ -274,7 +274,7 @@ function SidebarTrigger({
|
||||
{...props}
|
||||
>
|
||||
{isMobile ? (
|
||||
<Menu className="h-6 w-6" />
|
||||
<Menu className="h-7 w-7" />
|
||||
) : (
|
||||
<PanelLeftIcon />
|
||||
)}
|
||||
|
||||
@@ -16,25 +16,25 @@ export const MPT_REFERENCE_RANGES: Record<string, MptReferenceRange> = {
|
||||
// 에너지 카테고리
|
||||
glucose: {
|
||||
name: '혈당',
|
||||
upperLimit: 72,
|
||||
lowerLimit: 46.9,
|
||||
upperLimit: 84,
|
||||
lowerLimit: 40,
|
||||
unit: 'mg/dL',
|
||||
category: '에너지',
|
||||
description: '에너지 대사 상태 지표',
|
||||
},
|
||||
cholesterol: {
|
||||
name: '콜레스테롤',
|
||||
upperLimit: 169,
|
||||
lowerLimit: 117,
|
||||
upperLimit: 252,
|
||||
lowerLimit: 74,
|
||||
unit: 'mg/dL',
|
||||
category: '에너지',
|
||||
description: '혈액 내 콜레스테롤 수치',
|
||||
},
|
||||
nefa: {
|
||||
name: '유리지방산(NEFA)',
|
||||
upperLimit: 382,
|
||||
lowerLimit: 118,
|
||||
unit: 'uEq/L',
|
||||
upperLimit: 660,
|
||||
lowerLimit: 115,
|
||||
unit: 'μEq/L',
|
||||
category: '에너지',
|
||||
description: '혈액 내 유리지방산 수치',
|
||||
},
|
||||
@@ -50,8 +50,8 @@ export const MPT_REFERENCE_RANGES: Record<string, MptReferenceRange> = {
|
||||
// 단백질 카테고리
|
||||
totalProtein: {
|
||||
name: '총단백질',
|
||||
upperLimit: 8.5,
|
||||
lowerLimit: 6.5,
|
||||
upperLimit: 7.7,
|
||||
lowerLimit: 6.2,
|
||||
unit: 'g/dL',
|
||||
category: '단백질',
|
||||
description: '혈액 내 총단백질 수치',
|
||||
@@ -68,7 +68,7 @@ export const MPT_REFERENCE_RANGES: Record<string, MptReferenceRange> = {
|
||||
name: '총글로불린',
|
||||
upperLimit: 36.1,
|
||||
lowerLimit: 9.1,
|
||||
unit: 'g/L',
|
||||
unit: 'g/dL',
|
||||
category: '단백질',
|
||||
description: '혈액 내 총글로불린 수치',
|
||||
},
|
||||
@@ -152,7 +152,7 @@ export const MPT_REFERENCE_RANGES: Record<string, MptReferenceRange> = {
|
||||
// 기타 카테고리
|
||||
creatinine: {
|
||||
name: '크레아티닌',
|
||||
upperLimit: 2.0,
|
||||
upperLimit: 1.3,
|
||||
lowerLimit: 1.0,
|
||||
unit: 'mg/dL',
|
||||
category: '기타',
|
||||
|
||||
@@ -39,6 +39,19 @@ export interface GeneSummary {
|
||||
heterozygousCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 유전자 마커 정보 타입
|
||||
*/
|
||||
export interface MarkerModel {
|
||||
markerNo: number;
|
||||
markerNm: string;
|
||||
markerType: string; // 'QTY' | 'QLT'
|
||||
markerTypeCd?: string; // 'QTY' | 'QLT' (별칭)
|
||||
markerDesc?: string;
|
||||
description?: string;
|
||||
relatedTrait?: string;
|
||||
}
|
||||
|
||||
export const geneApi = {
|
||||
/**
|
||||
* 개체식별번호로 유전자 상세 정보 조회
|
||||
@@ -87,4 +100,34 @@ export const geneApi = {
|
||||
createBulk: async (dataList: Partial<GeneDetail>[]): Promise<GeneDetail[]> => {
|
||||
return await apiClient.post('/gene/bulk', dataList);
|
||||
},
|
||||
|
||||
/**
|
||||
* 유전자 타입별 마커 목록 조회
|
||||
* GET /gene/markers/:type
|
||||
* TODO: 백엔드 API 구현 후 연동 필요
|
||||
*/
|
||||
getGenesByType: async (type: 'QTY' | 'QLT'): Promise<MarkerModel[]> => {
|
||||
try {
|
||||
return await apiClient.get(`/gene/markers/${type}`);
|
||||
} catch {
|
||||
// API 미구현 시 빈 배열 반환
|
||||
console.warn(`[Gene API] getGenesByType(${type}) - API 미구현, 빈 배열 반환`);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 전체 마커 목록 조회
|
||||
* GET /gene/markers
|
||||
* TODO: 백엔드 API 구현 후 연동 필요
|
||||
*/
|
||||
getAllMarkers: async (): Promise<MarkerModel[]> => {
|
||||
try {
|
||||
return await apiClient.get('/gene/markers');
|
||||
} catch {
|
||||
// API 미구현 시 빈 배열 반환
|
||||
console.warn('[Gene API] getAllMarkers() - API 미구현, 빈 배열 반환');
|
||||
return [];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -54,13 +54,7 @@ export function isValidGenomeAnalysis(
|
||||
chipDamName: string | null | undefined,
|
||||
cowId?: string | null,
|
||||
): boolean {
|
||||
// 1. 아비 일치 확인
|
||||
if (chipSireName !== VALID_CHIP_SIRE_NAME) return false;
|
||||
|
||||
// 2. 어미 제외 조건 확인
|
||||
if (chipDamName && INVALID_CHIP_DAM_NAMES.includes(chipDamName)) return false;
|
||||
|
||||
// 3. 개별 제외 개체 확인
|
||||
// 개별 제외 개체만 확인 (부/모 불일치여도 유전자 데이터 있으면 표시)
|
||||
if (cowId && EXCLUDED_COW_IDS.includes(cowId)) return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -128,7 +128,7 @@ export const DEFAULT_FILTER_SETTINGS: GlobalFilterSettings = {
|
||||
analysisIndex: "GENE",
|
||||
selectedGenes: [],
|
||||
pinnedGenes: [],
|
||||
selectedTraits: ["도체중", "등심단면적", "등지방두께", "근내지방도", "체장", "체고", "흉위"],
|
||||
selectedTraits: ["도체중", "등심단면적", "등지방두께", "근내지방도", "등심weight", "체장", "체고"],
|
||||
pinnedTraits: [],
|
||||
traitWeights: {
|
||||
// 성장형질 (점수: 1 ~ 10, 미선택 시 0)
|
||||
@@ -150,11 +150,11 @@ export const DEFAULT_FILTER_SETTINGS: GlobalFilterSettings = {
|
||||
요각폭: 0,
|
||||
좌골폭: 0,
|
||||
곤폭: 0,
|
||||
흉위: 1,
|
||||
흉위: 0,
|
||||
|
||||
// 부위별무게 (점수: 1 ~ 10, 미선택 시 0) - DB 형질명과 일치
|
||||
안심weight: 0,
|
||||
등심weight: 0,
|
||||
등심weight: 1,
|
||||
채끝weight: 0,
|
||||
목심weight: 0,
|
||||
앞다리weight: 0,
|
||||
|
||||
Reference in New Issue
Block a user