diff --git a/src/entrypoints/popup/App.tsx b/src/entrypoints/popup/App.tsx index 12ff9d8..3b03b01 100644 --- a/src/entrypoints/popup/App.tsx +++ b/src/entrypoints/popup/App.tsx @@ -4,7 +4,7 @@ import { CustomSelect } from "./CustomSelect"; import { CustomDateTime } from "./CustomDateTime"; import "./App.css"; -type Step = "permission" | "denied" | "category" | "mode" | "conversation" | "purpose" | "analyzing" | "result" | "monitoring"; +type Step = "login" | "loginForm" | "permission" | "denied" | "category" | "mode" | "conversation" | "purpose" | "analyzing" | "result" | "monitoring"; interface FormData { hasPermission: boolean; @@ -35,26 +35,29 @@ interface AnalyzeRequest { uuid: string; messages: Message[]; platform: string; + type: string; } interface ReasonItem { - source: string; - note: string; + quote: string; + reason: string; } interface AnalysisResult { riskLevel: string; summary: string; type: string; - reason: ReasonItem[]; - recommendedQuestions: string[]; - recommendations?: string[]; + risk_signals: ReasonItem[]; + recommended_questions: string[]; + additional_recommendations?: string[]; } function App() { - const [step, setStep] = useState("permission"); + const [step, setStep] = useState("login"); + const [loginEmail, setLoginEmail] = useState(""); + const [loginPassword, setLoginPassword] = useState(""); const [formData, setFormData] = useState({ - hasPermission: false, + hasPermission: true, category: "", mode: "", selectionMode: "message", @@ -99,13 +102,15 @@ function App() { // API ๋ถ„์„ ํ•จ์ˆ˜ const analyzeMessages = async ( messages: Message[], - platform: string + platform: string, + type: string ) => { const uuid = crypto.randomUUID(); const payload: AnalyzeRequest = { uuid, messages, platform, + type, }; //console.log('\n========== ๐Ÿ“ค API REQUEST =========='); @@ -132,12 +137,12 @@ function App() { return { ...result, - reason: Array.isArray(result.reason) ? result.reason : [], - recommendedQuestions: Array.isArray(result.recommendedQuestions) - ? result.recommendedQuestions + risk_signals: Array.isArray(result.risk_signals) ? result.risk_signals : [], + recommended_questions: Array.isArray(result.recommended_questions) + ? result.recommended_questions : [], - recommendations: Array.isArray(result.recommendations) - ? result.recommendations.slice(0, 3) + additional_recommendations: Array.isArray(result.additional_recommendations) + ? result.additional_recommendations.slice(0, 3) : [], } as AnalysisResult; @@ -158,6 +163,12 @@ function App() { } }; + const handleLoginKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleLoginSubmit(); + } + }; + const getRiskBadgeClass = (riskLevel: string) => { const normalized = (riskLevel || "").toLowerCase(); if (normalized === "normal") return "safe"; @@ -262,7 +273,7 @@ function App() { return; } - const platform = formData.category === "job" ? "telegram" : "instagram"; + const platform = formData.category === "๊ตฌ์ง" ? "telegram" : "instagram"; const buildMonitoringMessages = (): Message[] => { const content = formData.purpose?.trim() @@ -280,7 +291,7 @@ function App() { }; const fetchMonitoring = () => { - analyzeMessages(buildMonitoringMessages(), platform) + analyzeMessages(buildMonitoringMessages(), platform, formData.category) .then((result) => { setMonitoringResult(result); }) @@ -310,11 +321,11 @@ function App() { rotationTimerRef.current = window.setInterval(() => { setMonitoringIndex((prev) => { - const length = monitoringResult?.recommendedQuestions?.length || 0; + const length = monitoringResult?.recommended_questions?.length || 0; if (length === 0) return 0; return (prev + 1) % length; }); - }, 3000); + }, 5000); return () => { if (rotationTimerRef.current) { @@ -322,11 +333,36 @@ function App() { rotationTimerRef.current = null; } }; - }, [step, monitoringResult?.recommendedQuestions]); + }, [step, monitoringResult?.recommended_questions]); + + // ๋กœ๊ทธ์ธ ๋‹จ๊ณ„ + const handleLoginYes = () => { + setStep("loginForm"); + }; + + const handleLoginNo = () => { + setStep("permission"); + }; + + // ๋กœ๊ทธ์ธ ํผ ์ œ์ถœ + const handleLoginSubmit = () => { + if (loginEmail.trim() && loginPassword.trim()) { + // ์•„๋ฌด ๋ฌธ์ž์—ด์ด๋‚˜ ์ž…๋ ฅํ•ด๋„ ๋ฌด์กฐ๊ฑด ๋กœ๊ทธ์ธ ์„ฑ๊ณต + setLoginEmail(""); + setLoginPassword(""); + void browser.runtime.sendMessage({ type: "PERMISSION_GRANTED" }); + setStep("category"); + } + }; - // 1๋‹จ๊ณ„: ๊ถŒํ•œ ์š”์ฒญ + const handleLoginCancel = () => { + setLoginEmail(""); + setLoginPassword(""); + setStep("login"); + }; + + // ๊ถŒํ•œ ์—ฌ๋ถ€ ๊ฒฐ์ • const handlePermissionYes = () => { - setFormData({ ...formData, hasPermission: true }); void browser.runtime.sendMessage({ type: "PERMISSION_GRANTED" }); setStep("category"); }; @@ -460,10 +496,10 @@ function App() { ]; // ํ”Œ๋žซํผ ์ •๋ณด (์นดํ…Œ๊ณ ๋ฆฌ ๊ธฐ๋ฐ˜) - const platform = formData.category === "job" ? "telegram" : "instagram"; + const platform = formData.category === "๊ตฌ์ง" ? "telegram" : "instagram"; // API ํ˜ธ์ถœ - analyzeMessages(exampleMessages, platform) + analyzeMessages(exampleMessages, platform, formData.category) .then((result) => { setAnalysisResult(result); setStep("result"); @@ -477,7 +513,85 @@ function App() { return (
- {/* 1๋‹จ๊ณ„: ๊ถŒํ•œ ์š”์ฒญ */} + {/* ๋กœ๊ทธ์ธ ํ™”๋ฉด */} + {step === "login" && ( +
+
+

๋กœ๊ทธ์ธ

+

์ด ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

+
+ + +
+
+
+ )} + + {/* ๋กœ๊ทธ์ธ ํผ */} + {step === "loginForm" && ( +
+
+

๋กœ๊ทธ์ธ

+

์ด๋ฉ”์ผ ๋˜๋Š” ์‚ฌ์šฉ์ž๋ช…์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”

+
+ setLoginEmail(e.target.value)} + onKeyPress={handleLoginKeyPress} + placeholder="์ด๋ฉ”์ผ ๋˜๋Š” ์‚ฌ์šฉ์ž๋ช…" + className="text-input" + autoFocus + /> +
+
+ setLoginPassword(e.target.value)} + onKeyPress={handleLoginKeyPress} + placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ" + className="text-input" + /> +
+
+ + +
+
+ +
+
+
+ )} + + {/* ๊ถŒํ•œ ์š”์ฒญ ํ™”๋ฉด */} {step === "permission" && (
@@ -499,8 +613,7 @@ function App() { {step === "denied" && (
-

์•„์‰ฝ์Šต๋‹ˆ๋‹ค

-

์ ‘๊ทผ ๊ถŒํ•œ์„ ํ—ˆ๋ฝํ•˜์…”์•ผ ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

+

์ ‘๊ทผ ๊ถŒํ•œ์„ ํ—ˆ๋ฝํ•˜์…”์•ผ
์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@@ -517,12 +630,12 @@ function App() { setFormData({ ...formData, category: value })} + placeholder="์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ" options={[ - { value: "", label: "์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ" }, - { value: "job", label: "๊ตฌ์ง" }, - { value: "trade", label: "์ค‘๊ณ ๊ฑฐ๋ž˜" }, - { value: "investment", label: "์žฌํƒœํฌ" }, - { value: "sidebusiness", label: "๋ถ€์—…" }, + { value: "๊ตฌ์ง", label: "๊ตฌ์ง" }, + { value: "์ค‘๊ณ ๊ฑฐ๋ž˜", label: "์ค‘๊ณ ๊ฑฐ๋ž˜" }, + { value: "์žฌํ…Œํฌ", label: "์žฌํ…Œํฌ" }, + { value: "๋ถ€์—…", label: "๋ถ€์—…" }, ]} />
@@ -583,27 +696,25 @@ function App() {

๋Œ€ํ™” ์˜์—ญ ์„ค์ •

์‹œ๊ฐ„ ๋‹จ์œ„, ๋‚ ์งœ ๋‹จ์œ„๋กœ ๋Œ€ํ™”๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

- {currentPlatform === "telegram" && ( - { - const mode = value as FormData["selectionMode"]; - setFormData((prev) => ({ - ...prev, - selectionMode: mode, - conversationStart: "์‹œ์ž‘ ๋ฉ”์„ธ์ง€๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”", - conversationEnd: "๋งˆ์ง€๋ง‰ ๋ฉ”์„ธ์ง€๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”", - conversationStartTime: "", - conversationEndTime: "", - })); - void browser.runtime.sendMessage({ type: "RESET_SELECTIONS" }); - }} - options={[ - { value: "message", label: "๋ฉ”์„ธ์ง€๋กœ ์„ ํƒ" }, - { value: "time", label: "์‹œ๊ฐ„์œผ๋กœ ์„ ํƒ" }, - ]} - /> - )} + { + const mode = value as FormData["selectionMode"]; + setFormData((prev) => ({ + ...prev, + selectionMode: mode, + conversationStart: "์‹œ์ž‘ ๋ฉ”์„ธ์ง€๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”", + conversationEnd: "๋งˆ์ง€๋ง‰ ๋ฉ”์„ธ์ง€๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”", + conversationStartTime: "", + conversationEndTime: "", + })); + void browser.runtime.sendMessage({ type: "RESET_SELECTIONS" }); + }} + options={[ + { value: "message", label: "๋ฉ”์„ธ์ง€๋กœ ์„ ํƒ" }, + { value: "time", label: "์‹œ๊ฐ„์œผ๋กœ ์„ ํƒ" }, + ]} + /> {timeError && (
โš ๏ธ {timeError} @@ -756,8 +867,8 @@ function App() {

๋ถ„์„ ์™„๋ฃŒ

-
-
{analysisResult.riskLevel || "SAFE"}
+
+
๋งค์šฐ ๋†’์Œ
@@ -783,18 +894,18 @@ function App() {

โš ๏ธ ์œ„ํ—˜ ์‹ ํ˜ธ

- {analysisResult.reason.length === 0 ? ( + {analysisResult.risk_signals.length === 0 ? (
"์œ„ํ—˜ ์‹ ํ˜ธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"
) : ( - analysisResult.reason.map((item, index) => ( + analysisResult.risk_signals.map((item, index) => (
{index + 1} - {item.source} + {item.quote}
-
"{item.note}"
+
"{item.reason}"
)) )} @@ -802,13 +913,13 @@ function App() {
{/* ์ถ”๊ฐ€ ๊ถŒ๊ณ  ์‚ฌํ•ญ */} - {analysisResult.recommendations && analysisResult.recommendations.length > 0 && ( + {analysisResult.additional_recommendations && analysisResult.additional_recommendations.length > 0 && (

โœจ ์ถ”๊ฐ€ ๊ถŒ๊ณ 

- {analysisResult.recommendations.map((rec, index) => ( + {analysisResult.additional_recommendations.map((rec, index) => (
{ - const text = monitoringResult?.recommendedQuestions?.length - ? monitoringResult.recommendedQuestions[monitoringIndex] + const text = monitoringResult?.recommended_questions?.length + ? monitoringResult.recommended_questions[monitoringIndex] : ""; if (text) { void handleCopyRecommendation(text); @@ -868,8 +979,8 @@ function App() { aria-label="์ถ”์ฒœ ์งˆ๋ฌธ ๋ณต์‚ฌ" >

- {monitoringResult?.recommendedQuestions?.length - ? monitoringResult.recommendedQuestions[monitoringIndex] + {monitoringResult?.recommended_questions?.length + ? monitoringResult.recommended_questions[monitoringIndex] : "์ถ”์ฒœ ์งˆ๋ฌธ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค..."}

@@ -878,9 +989,9 @@ function App() {

์˜์‹ฌ๊ฐ€๋Š” ๋Œ€ํ™”

    - {monitoringResult?.reason?.length ? ( - monitoringResult.reason.map((item, index) => ( -
  • {item.note}
  • + {monitoringResult?.risk_signals?.length ? ( + monitoringResult.risk_signals.map((item, index) => ( +
  • {item.reason}
  • )) ) : (
  • ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค
  • diff --git a/src/entrypoints/popup/CustomSelect.tsx b/src/entrypoints/popup/CustomSelect.tsx index 342d9b1..cec979b 100644 --- a/src/entrypoints/popup/CustomSelect.tsx +++ b/src/entrypoints/popup/CustomSelect.tsx @@ -6,13 +6,15 @@ interface CustomSelectProps { onChange: (value: string) => void; options: { value: string; label: string }[]; className?: string; + placeholder?: string; } -export function CustomSelect({ value, onChange, options, className = "" }: CustomSelectProps) { +export function CustomSelect({ value, onChange, options, className = "", placeholder = "" }: CustomSelectProps) { const [isOpen, setIsOpen] = useState(false); const selectRef = useRef(null); const selectedOption = options.find(opt => opt.value === value); + const displayLabel = selectedOption?.label || placeholder; useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -42,7 +44,7 @@ export function CustomSelect({ value, onChange, options, className = "" }: Custo className={`custom-select-trigger ${isOpen ? "open" : ""}`} onClick={() => setIsOpen(!isOpen)} > - {selectedOption?.label} + {displayLabel}