From 09d3e77c3ec25184ed33192d51a1f49ccd98afde Mon Sep 17 00:00:00 2001 From: Zero-1016 Date: Tue, 17 Feb 2026 18:52:15 +0900 Subject: [PATCH 01/11] feat: add paste-in external problem feedback card --- app/(main)/page.tsx | 2 + components/external-problem-feedback.tsx | 178 +++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 components/external-problem-feedback.tsx diff --git a/app/(main)/page.tsx b/app/(main)/page.tsx index 378f213..43136a9 100644 --- a/app/(main)/page.tsx +++ b/app/(main)/page.tsx @@ -6,6 +6,7 @@ import { HeroSection } from "@/components/hero-section" import { VirtualizedProblemList } from "@/components/virtualized-problem-list" import { StreakWidget } from "@/components/streak-widget" import { StatsWidget } from "@/components/stats-widget" +import { ExternalProblemFeedback } from "@/components/external-problem-feedback" import { PageTransition } from "@/components/page-transition" import { problems } from "@/lib/problems" import { localizeCategory } from "@/lib/problems" @@ -130,6 +131,7 @@ export default function HomePage() {
{/* Main Content Area */}
+

{selectedCategory === "ALL" diff --git a/components/external-problem-feedback.tsx b/components/external-problem-feedback.tsx new file mode 100644 index 0000000..afe2040 --- /dev/null +++ b/components/external-problem-feedback.tsx @@ -0,0 +1,178 @@ +"use client" + +import { useState } from "react" +import { Loader2 } from "lucide-react" +import { Textarea } from "@/components/ui/textarea" +import { getApiSettings } from "@/lib/local-progress" +import { useAppLanguage } from "@/lib/use-app-language" + +interface ChatResponse { + message?: string +} + +export function ExternalProblemFeedback() { + const REQUEST_TIMEOUT_MS = 20000 + const { language } = useAppLanguage() + const [problemTitle, setProblemTitle] = useState("") + const [problemText, setProblemText] = useState("") + const [code, setCode] = useState("") + const [feedback, setFeedback] = useState("") + const [error, setError] = useState("") + const [isLoading, setIsLoading] = useState(false) + + const text = + language === "ko" + ? { + title: "외부 문제 붙여넣기 피드백", + description: + "다른 사이트 문제를 그대로 붙여넣고, 접근 방향과 개선 포인트를 바로 받아보세요.", + titleLabel: "문제 제목 (선택)", + titlePlaceholder: "예: Longest Substring Without Repeating Characters", + problemLabel: "문제 설명", + problemPlaceholder: + "문제 본문, 입력/출력 형식, 제약사항을 그대로 붙여넣어 주세요.", + codeLabel: "내 코드 (선택)", + codePlaceholder: "작성한 코드가 있다면 함께 붙여넣어 주세요.", + submit: "피드백 받기", + loading: "분석 중...", + resultTitle: "AI 피드백", + errorRequired: "문제 설명을 먼저 붙여넣어 주세요.", + errorGeneric: "피드백 요청에 실패했습니다. 잠시 후 다시 시도해 주세요.", + } + : { + title: "Paste External Problem", + description: + "Paste a problem from another site and get quick feedback on approach and improvements.", + titleLabel: "Problem title (optional)", + titlePlaceholder: "e.g. Longest Substring Without Repeating Characters", + problemLabel: "Problem statement", + problemPlaceholder: "Paste full statement, input/output format, and constraints.", + codeLabel: "Your code (optional)", + codePlaceholder: "Paste your current solution if you want code-level feedback.", + submit: "Get Feedback", + loading: "Analyzing...", + resultTitle: "AI Feedback", + errorRequired: "Paste the problem statement first.", + errorGeneric: "Failed to request feedback. Please try again.", + } + + const handleSubmit = async () => { + if (!problemText.trim()) { + setError(text.errorRequired) + return + } + + setError("") + setFeedback("") + setIsLoading(true) + + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS) + + try { + const response = await fetch("/api/chat", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + signal: controller.signal, + body: JSON.stringify({ + messages: [ + { + role: "user", + content: + language === "ko" + ? "붙여넣은 문제를 기준으로 접근 방향, 자주 틀리는 포인트, 다음 액션 1개를 짧게 피드백해줘." + : "Based on the pasted problem, give a short review with approach direction, common pitfalls, and one next action.", + }, + ], + code, + problemTitle: problemTitle.trim() || "External Problem", + problemDescription: problemText, + language, + aiConfig: getApiSettings(), + }), + }) + + if (!response.ok) { + throw new Error("REQUEST_FAILED") + } + + const data = (await response.json()) as ChatResponse + setFeedback(data.message?.trim() || text.errorGeneric) + } catch (requestError) { + if (requestError instanceof DOMException && requestError.name === "AbortError") { + setError(language === "ko" ? "요청 시간이 초과되었습니다. 다시 시도해 주세요." : "The request timed out. Please try again.") + } else { + setError(text.errorGeneric) + } + } finally { + clearTimeout(timeoutId) + setIsLoading(false) + } + } + + return ( +
+
+

{text.title}

+

{text.description}

+
+ +
+
+ +