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
3 changes: 3 additions & 0 deletions src/api/auth/loginApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export const login = async (
? responseData.access_token
: `Bearer ${responseData.access_token}`;

// ํ—ค๋”์—์„œ ์ฒซ ๋กœ๊ทธ์ธ๋งŒ ํ† ์ŠคํŠธ๋ฅผ ๋„์šฐ๊ธฐ ์œ„ํ•œ ํ”Œ๋ž˜๊ทธ
sessionStorage.setItem("just_logged_in", "1");

return {
token,
user: responseData.user,
Expand Down
20 changes: 20 additions & 0 deletions src/api/stock/detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,23 @@ export async function getStockChart(
);
return data;
}

// Total analysis (LLM) API
export interface TotalAnalysisResponse {
main_analysis?: string;
volatility_analysis?: string;
volume_analysis?: string;
fin_total_analysis?: string;
company_analysis?: string;
total_analysis?: string;
combined_technical_analysis?: string;
}

export async function getTotalAnalysis(
stockCode: string
): Promise<TotalAnalysisResponse> {
const { data } = await axiosInstance.get(
`/api/info/total_analysis/${encodeURIComponent(stockCode)}`
);
return data;
}
233 changes: 233 additions & 0 deletions src/components/Intro/ABTestResults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { motion } from "framer-motion";

export default function ABTestResults() {
return (
<div className="bg-gradient-to-br from-[#0A5C2B]/10 to-[#0A5C2B]/5 min-h-screen flex items-center relative snap-start">
<div className="container mx-auto px-4 py-8 md:py-16">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="text-center mb-12"
>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.2 }}
className="text-3xl font-bold text-gray-900 mb-4"
>
๐Ÿ“Š LLM ๊ธฐ๋ฐ˜ ๊ธˆ์œต ๋ถ„์„ ๋ชจ๋ธ A/B ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.4 }}
className="text-lg text-gray-600 mb-8"
>
CoT/RAG ๋ฐฉ์‹์˜ ์šฐ์ˆ˜์„ฑ์„ ๊ณผํ•™์ ์œผ๋กœ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค
</motion.p>

{/* ๊ธฐ์ˆ  ์Šคํƒ ๋น„๊ต */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.6 }}
className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl mx-auto mb-12"
>
<div className="bg-white rounded-xl p-6 shadow-lg">
<h4 className="text-lg font-bold text-gray-500 mb-4 text-center">
Baseline
</h4>
<div className="text-center">
<div className="text-2xl font-bold text-gray-500 mb-2">
GPT-4o
</div>
<div className="text-sm text-gray-600">๊ธฐ๋ณธ ๋ชจ๋ธ</div>
</div>
</div>
<div className="bg-white rounded-xl p-6 shadow-lg border-2 border-[#0A5C2B]">
<h4 className="text-lg font-bold text-[#0A5C2B] mb-4 text-center">
Ours
</h4>
<div className="text-center">
<div className="text-2xl font-bold text-[#0A5C2B] mb-2">
GPT-4o
</div>
<div className="text-sm text-gray-600 mb-2">
+ BarbellAI ๊ธฐ์ˆ 
</div>
<div className="text-xs text-[#0A5C2B] font-medium">
Chain of Thought + RAG
</div>
</div>
</div>
</motion.div>
</motion.div>

<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="max-w-6xl mx-auto"
>
{/* ์ข…ํ•ฉ ๋ถ„์„ ํ‰๊ฐ€ */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.1 }}
className="bg-white rounded-xl p-8 shadow-lg mb-8"
>
<h3 className="text-xl font-bold text-[#0A5C2B] mb-6 text-center">
๐ŸŽฏ ์ข…ํ•ฉ ๋ถ„์„ ํ‰๊ฐ€ (๊นŠ์ด+๊ทผ๊ฑฐ+ํ†ต์ฐฐ๋ ฅ ํ‰๊ท )
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="text-center">
<div className="text-3xl font-bold text-[#0A5C2B] mb-2">
6.92
</div>
<div className="text-sm text-gray-600">CoT/RAG ํ‰๊ท  ์ ์ˆ˜</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-gray-500 mb-2">
5.25
</div>
<div className="text-sm text-gray-600">Baseline ํ‰๊ท  ์ ์ˆ˜</div>
</div>
</div>
<div className="mt-6 text-center">
<div className="text-2xl font-bold text-green-600 mb-2">
+31.79%
</div>
<div className="text-sm text-gray-600">์„ฑ๋Šฅ ํ–ฅ์ƒ๋ฅ </div>
<div className="text-xs text-gray-500 mt-2">
P-value: 0.0000 (p &lt; 0.0001)
</div>
</div>
</motion.div>

{/* ๊ฐœ๋ณ„ ํ•ญ๋ชฉ ํ‰๊ฐ€ */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* ๋ถ„์„์˜ ๊นŠ์ด */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.2 }}
className="bg-white rounded-xl p-6 shadow-lg"
>
<h4 className="text-lg font-bold text-[#0A5C2B] mb-4 text-center">
๐Ÿ” ๋ถ„์„์˜ ๊นŠ์ด
</h4>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">CoT/RAG</span>
<span className="font-bold text-[#0A5C2B]">7.53</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">Baseline</span>
<span className="font-bold text-gray-500">5.28</span>
</div>
<div className="pt-2 border-t">
<div className="text-center">
<div className="text-lg font-bold text-green-600">
+42.61%
</div>
<div className="text-xs text-gray-500">ํ–ฅ์ƒ๋ฅ </div>
</div>
</div>
</div>
</motion.div>

{/* ๋…ผ๋ฆฌ์  ๊ทผ๊ฑฐ */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.3 }}
className="bg-white rounded-xl p-6 shadow-lg"
>
<h4 className="text-lg font-bold text-[#0A5C2B] mb-4 text-center">
๐Ÿง  ๋…ผ๋ฆฌ์  ๊ทผ๊ฑฐ
</h4>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">CoT/RAG</span>
<span className="font-bold text-[#0A5C2B]">6.61</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">Baseline</span>
<span className="font-bold text-gray-500">5.59</span>
</div>
<div className="pt-2 border-t">
<div className="text-center">
<div className="text-lg font-bold text-green-600">
+18.25%
</div>
<div className="text-xs text-gray-500">ํ–ฅ์ƒ๋ฅ </div>
</div>
</div>
</div>
</motion.div>

{/* ํ†ต์ฐฐ๋ ฅ */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.4 }}
className="bg-white rounded-xl p-6 shadow-lg"
>
<h4 className="text-lg font-bold text-[#0A5C2B] mb-4 text-center">
๐Ÿ’ก ํ†ต์ฐฐ๋ ฅ
</h4>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">CoT/RAG</span>
<span className="font-bold text-[#0A5C2B]">6.63</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">Baseline</span>
<span className="font-bold text-gray-500">4.89</span>
</div>
<div className="pt-2 border-t">
<div className="text-center">
<div className="text-lg font-bold text-green-600">
+35.58%
</div>
<div className="text-xs text-gray-500">ํ–ฅ์ƒ๋ฅ </div>
</div>
</div>
</div>
</motion.div>
</div>

{/* ๊ฒฐ๋ก  */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.5 }}
className="mt-8 text-center"
>
<div className="bg-[#0A5C2B] text-white rounded-xl p-6">
<h4 className="text-xl font-bold mb-2">๐Ÿ† ๊ฒฐ๋ก </h4>
<p className="text-white/90">
CoT/RAG ๋ฐฉ์‹์ด ๋ชจ๋“  ํ‰๊ฐ€ ํ•ญ๋ชฉ์—์„œ ํ†ต๊ณ„์ ์œผ๋กœ ์œ ์˜๋ฏธํ•˜๊ฒŒ ์šฐ์ˆ˜ํ•œ
์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
<br />
<span className="font-semibold">p &lt; 0.001</span> ์ˆ˜์ค€์—์„œ
ํ†ต๊ณ„์  ์œ ์˜์„ฑ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
</p>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
16 changes: 15 additions & 1 deletion src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import BALLFiNLogo from "../../assets/BALLFiN.svg";
import { Link, useLocation } from "react-router-dom";
import { Menu, X, LogOut, User, Settings } from "lucide-react";
Expand Down Expand Up @@ -40,13 +40,25 @@ const Header = () => {
message: string;
type: "success" | "error";
}>({ show: false, message: "", type: "success" });
const wasLoggedInRef = useRef(false);

useEffect(() => {
// ๋กœ๊ทธ์ธ ์ƒํƒœ ๋ฐ ์‚ฌ์šฉ์ž ์ •๋ณด ํ™•์ธ
const checkLoginStatus = async () => {
const token = localStorage.getItem("access_token");

if (token) {
// ์ฒซ ๋กœ๊ทธ์ธ ํ”Œ๋ž˜๊ทธ ํ™•์ธ
const justLoggedIn = sessionStorage.getItem("just_logged_in");
if (justLoggedIn) {
setToast({
show: true,
message: "๋กœ๊ทธ์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
type: "success",
});
sessionStorage.removeItem("just_logged_in");
}
wasLoggedInRef.current = true;
setIsLoggedIn(true);

// ๋จผ์ € localStorage์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ํ™•์ธ
Expand Down Expand Up @@ -81,11 +93,13 @@ const Header = () => {
setIsLoggedIn(false);
setUserName("");
setUserEmail("");
wasLoggedInRef.current = false;
}
} else {
setIsLoggedIn(false);
setUserName("");
setUserEmail("");
wasLoggedInRef.current = false;
}
};

Expand Down
11 changes: 8 additions & 3 deletions src/components/stockDetail/FinancialStatement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface FinancialData {
interface FinancialStatementProps {
data: FinancialData;
analysis?: any; // /info/company/{code} ์‘๋‹ต ๊ฐ์ฒด (company_analysis ํฌํ•จ)
isAnalysisLoading?: boolean; // LLM ๋ถ„์„ ๋กœ๋”ฉ ์ƒํƒœ
}

interface FinancialIndicator {
Expand All @@ -30,10 +31,11 @@ interface FinancialIndicator {
export default function FinancialStatement({
data,
analysis,
isAnalysisLoading: _isAnalysisLoading = false,
}: FinancialStatementProps) {
const [hoveredIndicator, setHoveredIndicator] = useState<string | null>(null);

const company = analysis?.company_analysis;
const company = analysis?.company_data; // ์žฌ๋ฌด ๋ฐ์ดํ„ฐ
const isAnalysisLoading = !company;

// ๊ฐ„๋‹จ ์Šค์ผˆ๋ ˆํ†ค ํ…์ŠคํŠธ
Expand Down Expand Up @@ -296,14 +298,17 @@ export default function FinancialStatement({
<DollarSign className="w-5 h-5 text-blue-600 mr-2" />
<h4 className="font-semibold text-blue-800">์ข…ํ•ฉ ํ•ด์„</h4>
</div>
{isAnalysisLoading ? (
{_isAnalysisLoading ? (
<div className="space-y-2">
<SkeletonText widthClass="w-10/12" />
<SkeletonText widthClass="w-6/12" />
</div>
) : (
<p className="text-sm text-blue-700 leading-relaxed">
{safeText(company?.total_analysis)}
{safeText(
analysis?.company_analysis ??
"์žฌ๋ฌด ๋ถ„์„ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค."
)}
</p>
)}
</div>
Expand Down
Loading