필터 기본값 수정

This commit is contained in:
2025-12-15 18:47:26 +09:00
parent c2b81c19c5
commit 3d022a1305
6 changed files with 28 additions and 87 deletions

View File

@@ -1,61 +0,0 @@
/**
* 소 용도 분류 설정
*
* @description
* 소의 용도를 결정하는 비즈니스 로직 임계값 정의
* - 도태 (Culling): 낮은 수태율, 번식 능력 부족
* - 인공수정 (Artificial Insemination): 높은 수태율 + 우수 등급
* - 공란우 (Donor): 중간 수태율 + 우수 등급
* - 수란우 (Recipient): 높은 수태율 + 낮은 등급
*/
export const COW_PURPOSE_CONFIG = {
/**
* 수태율 기반 임계값 (%)
*/
CONCEPTION_RATE_THRESHOLDS: {
/**
* 도태 대상 최대 수태율 (30% 미만)
* 수태율이 이 값보다 낮으면 번식 능력이 부족하여 도태 대상
*/
CULLING_MAX: 30,
/**
* 공란우 최대 수태율 (50% 미만)
* 수태율이 낮지만 우수한 유전자 보유 시 수정란 공급
*/
DONOR_MAX: 50,
/**
* 수란우 최소 수태율 (65% 이상)
* 높은 수태율을 가진 소에게 우수 수정란 이식
*/
RECIPIENT_MIN: 65,
/**
* 인공수정 최소 수태율 (65% 이상)
* 높은 수태율 + 우수 등급 → 일반 인공수정 대상
*/
INSEMINATION_MIN: 65,
},
/**
* 나이 기반 임계값 (년)
*/
AGE_THRESHOLDS: {
/**
* 노령우 기준 (10년 이상)
* 이 나이 이상이면 도태 고려 대상
*/
OLD_AGE_YEARS: 10,
/**
* 번식 적정 최소 나이 (2년)
*/
BREEDING_MIN_AGE: 2,
/**
* 번식 적정 최대 나이 (8년)
*/
BREEDING_MAX_AGE: 8,
},
} as const;

View File

@@ -1810,8 +1810,8 @@ export class GenomeService {
]; ];
// inputTraitConditions가 있으면 사용, 없으면 35개 형질 기본값 사용 // inputTraitConditions가 있으면 사용, 없으면 35개 형질 기본값 사용
const traitConditions = inputTraitConditions && inputTraitConditions.length > 0 const traitConditions = inputTraitConditions && inputTraitConditions.length > 0
? inputTraitConditions ? inputTraitConditions // 프론트에서 보낸 형질사용
: ALL_TRAITS.map(traitNm => ({ traitNm, weight: 1 })); : ALL_TRAITS.map(traitNm => ({ traitNm, weight: 1 })); // 기본값 사용
console.log('[getFarmRegionRanking] traitConditions:', traitConditions.length, 'traits'); console.log('[getFarmRegionRanking] traitConditions:', traitConditions.length, 'traits');

View File

@@ -10,7 +10,7 @@ import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { useToast } from "@/hooks/use-toast" import { useToast } from "@/hooks/use-toast"
import { ComparisonAveragesDto, TraitComparisonAveragesDto, cowApi, genomeApi, geneApi, GeneDetail, GenomeRequestDto } from "@/lib/api" import { ComparisonAveragesDto, TraitComparisonAveragesDto, cowApi, genomeApi, geneApi, GeneDetail, GenomeRequestDto, mptApi, MptDto } from "@/lib/api"
import { CowDetail } from "@/types/cow.types" import { CowDetail } from "@/types/cow.types"
import { GenomeTrait } from "@/types/genome.types" import { GenomeTrait } from "@/types/genome.types"
import { useGlobalFilter } from "@/contexts/GlobalFilterContext" import { useGlobalFilter } from "@/contexts/GlobalFilterContext"
@@ -35,6 +35,7 @@ import { TraitComparison } from "./genome/_components/genome-integrated-comparis
import { CowNumberDisplay } from "@/components/common/cow-number-display" import { CowNumberDisplay } from "@/components/common/cow-number-display"
import { isValidGenomeAnalysis, getInvalidReason, getInvalidMessage } from "@/lib/utils/genome-analysis-config" import { isValidGenomeAnalysis, getInvalidReason, getInvalidMessage } from "@/lib/utils/genome-analysis-config"
import { AuthGuard } from "@/components/auth/auth-guard" import { AuthGuard } from "@/components/auth/auth-guard"
import { MptTable } from "./reproduction/_components/mpt-table"
// 형질명 → 카테고리 매핑 (한우 35개 형질) // 형질명 → 카테고리 매핑 (한우 35개 형질)
const TRAIT_CATEGORY_MAP: Record<string, string> = { const TRAIT_CATEGORY_MAP: Record<string, string> = {
@@ -628,7 +629,7 @@ export default function CowOverviewPage() {
</Card> </Card>
{/* 친자확인 섹션 */} {/* 친자확인 섹션 */}
<h3 className="text-lg lg:text-xl font-bold text-foreground"> </h3> <h3 className="text-lg lg:text-xl font-bold text-foreground"></h3>
<Card className="bg-white border border-border shadow-sm rounded-2xl overflow-hidden"> <Card className="bg-white border border-border shadow-sm rounded-2xl overflow-hidden">
<CardContent className="p-0"> <CardContent className="p-0">
@@ -997,7 +998,7 @@ export default function CowOverviewPage() {
</Card> </Card>
{/* 친자확인 섹션 */} {/* 친자확인 섹션 */}
<h3 className="text-lg lg:text-xl font-bold text-foreground"> </h3> <h3 className="text-lg lg:text-xl font-bold text-foreground"></h3>
<Card className="bg-white border border-border shadow-sm rounded-2xl overflow-hidden"> <Card className="bg-white border border-border shadow-sm rounded-2xl overflow-hidden">
<CardContent className="p-0"> <CardContent className="p-0">
@@ -1258,7 +1259,7 @@ export default function CowOverviewPage() {
</Card> </Card>
{/* 친자확인 결과 섹션 (유전체 탭과 동일) */} {/* 친자확인 결과 섹션 (유전체 탭과 동일) */}
<h3 className="text-lg lg:text-xl font-bold text-foreground"> </h3> <h3 className="text-lg lg:text-xl font-bold text-foreground"></h3>
<Card className="bg-white border border-border shadow-sm rounded-2xl overflow-hidden"> <Card className="bg-white border border-border shadow-sm rounded-2xl overflow-hidden">
<CardContent className="p-0"> <CardContent className="p-0">
@@ -1674,11 +1675,15 @@ export default function CowOverviewPage() {
)} )}
</TabsContent> </TabsContent>
{/* 번식능력 탭 */} {/* 번식능력 탭 */}
<TabsContent value="reproduction" className="mt-6 space-y-6"> <TabsContent value="reproduction" className="mt-6 space-y-6">
{/* 혈액화학검사(MPT) 테이블 */}
<MptTable cowShortNo={cowNo?.slice(-4)} cowNo={cowNo} farmNo={cow?.fkFarmNo} cow={cow} genomeRequest={genomeRequest} />
{/* TODO: 번식능력 분석 결과 (추후 사용)
{hasReproductionData ? ( {hasReproductionData ? (
<div> <div>
{/* TODO: 번식능력 분석 결과 표시 */}
<h3 className="text-lg lg:text-xl font-bold text-foreground">번식능력 분석 결과</h3> <h3 className="text-lg lg:text-xl font-bold text-foreground">번식능력 분석 결과</h3>
</div> </div>
) : ( ) : (
@@ -1692,6 +1697,7 @@ export default function CowOverviewPage() {
</CardContent> </CardContent>
</Card> </Card>
)} )}
*/}
</TabsContent> </TabsContent>
</Tabs> </Tabs>
</div> </div>

View File

@@ -716,7 +716,7 @@ function MyCowContent() {
> >
<span className="flex items-center justify-center gap-1.5 max-sm:gap-1"> <span className="flex items-center justify-center gap-1.5 max-sm:gap-1">
<span className="w-2 h-2 rounded-full bg-slate-400"></span> <span className="w-2 h-2 rounded-full bg-slate-400"></span>
<span className="font-bold">{cows.filter(c => c.genomeScore === undefined || c.genomeScore === null).length}</span> <span className="font-bold">{cows.filter(c => c.genomeScore === undefined || c.genomeScore === null).length}</span>
</span> </span>
</button> </button>
</div> </div>

View File

@@ -504,13 +504,7 @@ export function GlobalFilterDialog({ externalOpen, onExternalOpenChange, geneCou
className="h-8 md:h-9 text-xs md:text-base font-semibold px-2 md:px-3 relative" className="h-8 md:h-9 text-xs md:text-base font-semibold px-2 md:px-3 relative"
> >
<Settings2 className="h-3 w-3 md:h-4 md:w-4 mr-1 md:mr-1.5" /> <Settings2 className="h-3 w-3 md:h-4 md:w-4 mr-1 md:mr-1.5" />
<span></span> <span> ({traitCount})</span>
<Badge
variant={(geneCount > 0 || traitCount > 0) ? "default" : "outline"}
className={`ml-1.5 text-[9px] md:text-xs px-1.5 py-0 ${(geneCount > 0 || traitCount > 0) ? "bg-white text-primary" : "text-muted-foreground"}`}
>
{(geneCount > 0 || traitCount > 0) ? "활성" : "비활성"}
</Badge>
</Button> </Button>
</SheetTrigger> </SheetTrigger>

View File

@@ -112,9 +112,10 @@ export interface GlobalFilterSettings {
} }
/** /**
* ====================================================================================================
* 기본 필터 초기값 설정 * 기본 필터 초기값 설정
* 사용자가 해당 형질 선택하지 않았을때 필터의 초기 값 0 세팅 * 사용자가 해당 형질 선택하지 않았을때 필터의 초기 값 0 세팅
* * ====================================================================================================
* const [filterSettings, setFilterSettings] = useState(DEFAULT_FILTER_SETTINGS); * const [filterSettings, setFilterSettings] = useState(DEFAULT_FILTER_SETTINGS);
* function resetFilter() { * function resetFilter() {
setFilterSettings(DEFAULT_FILTER_SETTINGS); // 초기화 setFilterSettings(DEFAULT_FILTER_SETTINGS); // 초기화
@@ -134,22 +135,22 @@ export const DEFAULT_FILTER_SETTINGS: GlobalFilterSettings = {
"12개월령체중": 0, "12개월령체중": 0,
// 경제형질 (점수: 0 ~ 10) // 경제형질 (점수: 0 ~ 10)
도체중: 0, 도체중: 1,
등심단면적: 0, 등심단면적: 1,
등지방두께: 0, 등지방두께: 1,
근내지방도: 0, 근내지방도: 1,
// 체형형질 (점수: 0 ~ 10) - DB 형질명과 일치 // 체형형질 (점수: 0 ~ 10) - DB 형질명과 일치
체고: 0, 체고: 1,
십자: 0, 십자: 0,
체장: 0, 체장: 1,
흉심: 0, 흉심: 0,
흉폭: 0, 흉폭: 0,
고장: 0, 고장: 0,
요각폭: 0, 요각폭: 0,
좌골폭: 0, 좌골폭: 0,
곤폭: 0, 곤폭: 0,
흉위: 0, 흉위: 1,
// 부위별무게 (점수: 0 ~ 10) - DB 형질명과 일치 // 부위별무게 (점수: 0 ~ 10) - DB 형질명과 일치
안심weight: 0, 안심weight: 0,
@@ -177,6 +178,7 @@ export const DEFAULT_FILTER_SETTINGS: GlobalFilterSettings = {
}, },
inbreedingThreshold: 0, // 근친도 기본값 0 inbreedingThreshold: 0, // 근친도 기본값 0
isActive: true, // 기본 7개 형질이 선택되어 있으므로 활성화 isActive: true, // 기본 7개 형질이 선택되어 있으므로 활성화
// 기본 각각 1점으로 세팅
updtDt: new Date(), updtDt: new Date(),
}; };