diff --git a/src/app/globals.css b/src/app/globals.css index 31afb53..1a2c522 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,6 +2,11 @@ @tailwind components; @tailwind utilities; +html, +body { + width: 100%; + height: 100%; +} .loadingSpinner { width: 40px; height: 40px; @@ -42,3 +47,10 @@ src: url("https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff") format("woff"); } + +.list-style { + list-style-type: square; + padding-inline-start: 1.5em; + list-style-position: inside; + line-height: 24px; +} diff --git a/src/app/login/layout.tsx b/src/app/login/layout.tsx index f6d04f4..de9f8b3 100644 --- a/src/app/login/layout.tsx +++ b/src/app/login/layout.tsx @@ -11,7 +11,7 @@ export default function Layout({ children: React.ReactNode; }>) { return ( -
+
{children}
); diff --git a/src/app/main/layout.tsx b/src/app/main/layout.tsx index 22733aa..cd44eaa 100644 --- a/src/app/main/layout.tsx +++ b/src/app/main/layout.tsx @@ -11,7 +11,7 @@ export default function Layout({ children: React.ReactNode; }>) { return ( -
+
{children}
); diff --git a/src/app/members/layout.tsx b/src/app/members/layout.tsx index ea32f90..9d85e69 100644 --- a/src/app/members/layout.tsx +++ b/src/app/members/layout.tsx @@ -11,7 +11,7 @@ export default function Layout({ children: React.ReactNode; }>) { return ( -
+
{children}
); diff --git a/src/app/my-account/layout.tsx b/src/app/my-account/layout.tsx index bc71184..7170077 100644 --- a/src/app/my-account/layout.tsx +++ b/src/app/my-account/layout.tsx @@ -10,9 +10,5 @@ export default function Layout({ }: Readonly<{ children: React.ReactNode; }>) { - return ( -
- {children} -
- ); + return
{children}
; } diff --git a/src/app/portfolio/layout.tsx b/src/app/portfolio/layout.tsx index d131995..d6104e4 100644 --- a/src/app/portfolio/layout.tsx +++ b/src/app/portfolio/layout.tsx @@ -10,9 +10,5 @@ export default function Layout({ }: Readonly<{ children: React.ReactNode; }>) { - return ( -
- {children} -
- ); + return
{children}
; } diff --git a/src/app/search/[id]/_components/detail-info/company-overview/consensus.tsx b/src/app/search/[id]/_components/detail-info/company-overview/consensus.tsx new file mode 100644 index 0000000..a9a7fb0 --- /dev/null +++ b/src/app/search/[id]/_components/detail-info/company-overview/consensus.tsx @@ -0,0 +1,29 @@ +export default function Consensus() { + return ( +
+
컨센서스
+
+
+
+ 적극 매도 +
+
+
+ 매도 +
+
+
+ 중립 +
+
+
+ 매수 +
+
+
+ 적극 매수 +
+
+
+ ); +} diff --git a/src/app/search/[id]/_components/detail-info/company-overview/financial-statements.tsx b/src/app/search/[id]/_components/detail-info/company-overview/financial-statements.tsx new file mode 100644 index 0000000..430a0ba --- /dev/null +++ b/src/app/search/[id]/_components/detail-info/company-overview/financial-statements.tsx @@ -0,0 +1,138 @@ +"use client"; + +type FinancialRatio = { + stockAccountingYearMonth: string; + grossMarginRatio: number; + businessProfitRate: number; + netInterestRate: number; + roeValue: number; + earningsPerShare: number; + salesPerShare: number; + bookValuePerShare: number; + reserveRate: number; + liabilityRate: number; +}; + +type NumberKeys = { + [K in keyof FinancialRatio]: FinancialRatio[K] extends number ? K : never; +}[keyof FinancialRatio]; + +type FinancialMetric = { + label: string; + key: NumberKeys; + isPercentage?: boolean; +}; + +const FINANCIAL_METRICS: FinancialMetric[] = [ + { label: "총마진율", key: "grossMarginRatio", isPercentage: true }, + { label: "사업 수익률", key: "businessProfitRate", isPercentage: true }, + { label: "순이자율", key: "netInterestRate", isPercentage: true }, + { label: "ROE", key: "roeValue", isPercentage: true }, + { label: "EPS(주당순이익)", key: "earningsPerShare" }, + { label: "SPS(주당매출액)", key: "salesPerShare" }, + { label: "BPS(주당순자산)", key: "bookValuePerShare" }, + { label: "주식유보율", key: "reserveRate", isPercentage: true }, + { label: "부채율", key: "liabilityRate", isPercentage: true }, +]; + +const formatValue = (value: number | null, isPercentage?: boolean) => { + if (value === null) return "-"; + const formattedNumber = value.toLocaleString(); + return isPercentage ? `${formattedNumber}%` : formattedNumber; +}; + +function TableHeader({ years }: { years: string[] }) { + return ( + + + + 주요재무정보 + + + 연간 + + + + {years.map((year) => ( + + {year} + + ))} + + + ); +} + +function TableRow({ + label, + values, + isPercentage, +}: { + label: string; + values: (number | null)[]; + isPercentage?: boolean; +}) { + return ( + + + {label} + + {values.map((value, index) => ( + + {formatValue(value, isPercentage)} + + ))} + + ); +} + +function NoDataMessage() { + return ( +
+ 데이터가 없습니다. +
+ ); +} + +interface FinancialStatementsProps { + data?: FinancialRatio[]; +} + +export default function FinancialStatements({ + data, +}: FinancialStatementsProps) { + if (!data || data.length === 0) { + return ; + } + + const years = data.map((item) => item.stockAccountingYearMonth); + + return ( + + + + + {FINANCIAL_METRICS.map((metric) => ( + + item[metric.key] !== undefined ? Number(item[metric.key]) : null, + )} + isPercentage={metric.isPercentage} + /> + ))} + +
재무비율
+ ); +} diff --git a/src/app/search/[id]/_components/detail-info/company-overview/index.tsx b/src/app/search/[id]/_components/detail-info/company-overview/index.tsx new file mode 100644 index 0000000..99dac62 --- /dev/null +++ b/src/app/search/[id]/_components/detail-info/company-overview/index.tsx @@ -0,0 +1,29 @@ +"use client"; + +import Consensus from "./consensus"; +import FinancialStatements from "./financial-statements"; +import TargetPrice from "./target-price"; + +export default function CompanyOverview() { + return ( +
+
+
기업정보
+ [기준: 2024. 08. 16] +
+
    +
  • 한국 및 DX 부문 해외 9개
  • +
  • 한국 및 DX 부문 해외 9개
  • +
  • 한국 및 DX 부문 해외 9개
  • +
+ + + + +
+ 전 세계 1200개 리서치 회사의 정보를 종합적으로 분석합니다.
+ 기준 2024.08.29 . 레피니티브 제공 +
+
+ ); +} diff --git a/src/app/search/[id]/_components/detail-info/company-overview/target-price.tsx b/src/app/search/[id]/_components/detail-info/company-overview/target-price.tsx new file mode 100644 index 0000000..1c51564 --- /dev/null +++ b/src/app/search/[id]/_components/detail-info/company-overview/target-price.tsx @@ -0,0 +1,17 @@ +export default function TargetPrice() { + return ( +
+
목표주가
+
+
43.90
+
+
목표가범위
+
26.00~60.00
+
+ +
+
+
+
+ ); +} diff --git a/src/app/search/[id]/_components/detail-info/index.tsx b/src/app/search/[id]/_components/detail-info/index.tsx new file mode 100644 index 0000000..43ab2a8 --- /dev/null +++ b/src/app/search/[id]/_components/detail-info/index.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@/components/common/tabs"; + +import CompanyOverview from "./company-overview"; +import RelativeNews from "./relative-news"; + +export default function DetailInfo() { + return ( +
+

상세 정보

+ + + 기업 개요 + 관련 뉴스 + + + + + + + + +
+ ); +} diff --git a/src/app/search/[id]/_components/detail-info/relative-news/index.tsx b/src/app/search/[id]/_components/detail-info/relative-news/index.tsx new file mode 100644 index 0000000..9fd8132 --- /dev/null +++ b/src/app/search/[id]/_components/detail-info/relative-news/index.tsx @@ -0,0 +1,26 @@ +"use client"; + +export default function RelativeNews() { + return ( + + + + + + + + + + + + + + + + +
관련뉴스
제목정보 제공날짜
+ 코스피, 외국인 "바이 코리아"에 2680 선 마감.. LG 에너지 + 솔루 + 머니 투데이2024.09.02
+ ); +} diff --git a/src/app/search/[id]/layout.tsx b/src/app/search/[id]/layout.tsx index a3df806..13ac983 100644 --- a/src/app/search/[id]/layout.tsx +++ b/src/app/search/[id]/layout.tsx @@ -10,9 +10,5 @@ export default function Layout({ }: Readonly<{ children: React.ReactNode; }>) { - return ( -
- {children} -
- ); + return
{children}
; } diff --git a/src/app/search/[id]/page.tsx b/src/app/search/[id]/page.tsx index ea14aa3..a0c79f6 100644 --- a/src/app/search/[id]/page.tsx +++ b/src/app/search/[id]/page.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import CandlestickChartContainer from "./_components/candle-chart-container"; +import DetailInfo from "./_components/detail-info"; import OrderStock from "./_components/order-stock"; import StockHeader from "./_components/stock-header"; import TutorialContainer from "./_components/tutorial/tutorial-container"; @@ -74,6 +75,7 @@ export default async function StockPage({
+
); } catch (error) { diff --git a/src/app/search/layout.tsx b/src/app/search/layout.tsx index 71daa12..76d7443 100644 --- a/src/app/search/layout.tsx +++ b/src/app/search/layout.tsx @@ -10,5 +10,5 @@ export default function Layout({ }: Readonly<{ children: React.ReactNode; }>) { - return
{children}
; + return
{children}
; } diff --git a/src/components/nav-bar/_components/nav-menu.tsx b/src/components/nav-bar/_components/nav-menu.tsx index 282703a..ccb85fe 100644 --- a/src/components/nav-bar/_components/nav-menu.tsx +++ b/src/components/nav-bar/_components/nav-menu.tsx @@ -105,7 +105,7 @@ export default function NavMenu() { }; return ( - +
{NAV_ITEMS.map((item) => ( diff --git a/tailwind.config.ts b/tailwind.config.ts index b3c4e7d..53a3678 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -40,6 +40,7 @@ const config: Config = { 100: "#E9FFF0", 200: "#E8F5E9", 500: "#1DA65A", + 900: "#324950", }, blue: { 100: "#EDF1FC", @@ -50,6 +51,7 @@ const config: Config = { 900: "#1A00DF", }, gray: { + 80: "#D9D9D9", 100: "#B6B6B6", 200: "#B7B7B7", 300: "#A1A1A1",