Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 63 additions & 5 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
import { demoGenome } from "@/lib/mocks/demoGenome";
import type { GenomeData, PrimerDesignResponseUI } from "@/types";
import { useViewStore } from "@/store/useViewStore";
import {
getInvalidStep1TemplateSequenceChar,
normalizeStep1TemplateSequence,
} from "../src/lib/parsers/step1TemplateSequence";
import Step1TemplateEssential from "@/components/steps/Step1TemplateEssential";
import Step2PrimerProperties from "@/components/steps/Step2PrimerProperties";
import Step3BindingLocation from "@/components/steps/Step3BindingLocation";
Expand Down Expand Up @@ -56,13 +60,61 @@ export default function Home() {
const [isLoading, setIsLoading] = useState(false);
const [apiResult, setApiResult] = useState<PrimerDesignResponseUI | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [step1WarningMessage, setStep1WarningMessage] = useState<string | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const totalSteps = steps.length;
const handleStepChange = (next: number) => {
const clamped = Math.min(Math.max(next, 1), totalSteps) as 1 | 2 | 3 | 4;
setStep(clamped);
};
const handleNext = () => handleStepChange(step + 1);
const clearStep1Warning = () => {
if (step1WarningMessage) {
setStep1WarningMessage(null);
}
};

const reportStep1ValidationMessage = (
mode: "step-transition" | "generate",
message: string | null,
) => {
// Step 전환 검증은 Step1 인라인 경고를 사용하고,
// Generate 직전 검증은 하단 error banner를 사용해 중복 노출을 피한다.
if (mode === "step-transition") {
setStep1WarningMessage(message);
return;
}
setErrorMessage(message);
};

const validateStep1Sequence = (mode: "step-transition" | "generate") => {
const rawInputSequence = sequenceInputRef.current;
const normalizedSequence = normalizeStep1TemplateSequence(rawInputSequence);
const invalidChar = getInvalidStep1TemplateSequenceChar(rawInputSequence);
if (!invalidChar) {
if (mode === "generate" && rawInputSequence.trim().length > 0 && !normalizedSequence) {
// Generate 직전 검증: 입력이 있었는데 정규화 후 빈 문자열이면 요청 중단.
const warningMessage =
"전송할 수 있는 유효한 염기서열이 없습니다. A, T, G, C 문자만 입력해 주세요.";
reportStep1ValidationMessage(mode, warningMessage);
return { isValid: false, normalizedSequence };
}

// 단계 이동 검증: Step1 경고를 초기화하고 다음 동작 진행.
reportStep1ValidationMessage(mode, null);
return { isValid: true, normalizedSequence };
}

const warningMessage = `대소문자 구분 없이 A, T, G, C만 입력 가능합니다. 잘못된 문자를 제거해 주세요.`;
reportStep1ValidationMessage(mode, warningMessage);
return { isValid: false, normalizedSequence };
};
const handleNext = () => {
if (step === 1 && !validateStep1Sequence("step-transition").isValid) {
return;
}

handleStepChange(step + 1);
};
const handleBack = () => handleStepChange(step - 1);
const isLastStep = step === totalSteps;

Expand All @@ -74,11 +126,16 @@ export default function Home() {
);

const handleGenerate = async () => {
const inputSequence = sequenceInputRef.current;
const validation = validateStep1Sequence("generate");
if (!validation.isValid) {
return;
Comment on lines 111 to +131
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자가 Step 1에서 다음 단계로 이동할 때와 Generate 버튼을 클릭할 때 중복된 검증 로직이 실행됩니다. handleNext (88번 줄)와 handleGenerate (105-113번 줄) 모두에서 유효성 검증을 수행하는데, 이는 일관성이 부족하고 유지보수를 어렵게 만듭니다. 검증 로직을 한 곳에서 수행하거나, 각각의 검증 목적이 다르다면 주석으로 명확히 구분해 주세요.

Copilot uses AI. Check for mistakes.
}

const targetSeq =
inputSequence && inputSequence.trim().length > 0
? inputSequence.trim()
validation.normalizedSequence && validation.normalizedSequence.trim().length > 0
? validation.normalizedSequence.trim()
: "ATGCGTACGTAGCTAGCTAGCTAGCTAATGCGTACGTAGCTAGCTAGCTAGCTA";

const payload: AnalyzeRequestInput = {
target_sequence: targetSeq,
species: "Homo sapiens",
Expand Down Expand Up @@ -122,7 +179,6 @@ export default function Home() {
setIsModalOpen(false);
// Surface the error for visibility during development.
console.error("Generate Primers failed", error);
alert(message);
} finally {
setIsLoading(false);
}
Expand All @@ -149,6 +205,8 @@ export default function Home() {
{step === 1 && (
<Step1TemplateEssential
sequenceRef={sequenceInputRef}
validationMessage={step1WarningMessage}
onSequenceChange={clearStep1Warning}
/>
)}

Expand Down
Loading