[fix] 디렉토리 구조 변경 및 파일명칭 수정

pull/5/head
minheon 2026-04-02 10:55:09 +09:00
parent c29541a365
commit 45f6a9f6ab
95 changed files with 160 additions and 164 deletions

View File

@ -10,25 +10,36 @@
## 디렉토리 구조 ## 디렉토리 구조
디렉토리 구조는 다음을 따를 예정입니다.
```bash ```bash
src/ src/
├── app/ # 애플리케이션 진입점 및 전역 설정 (Router, Providers, 글로벌 스타일) ├── app/ # 애플리케이션 진입점 및 전역 설정 (Router, Providers, 글로벌 스타일)
├── assets/ # 정적 파일 (이미지, 폰트, 로티 애니메이션 등) │ └── providers/ # React Context Provider 모음 (QueryProvider 등)
├── components/ # 도메인에 종속되지 않는 공통 UI 컴포넌트 (버튼, 모달, 디자인 시스템) ├── assets/ # 정적 파일 (이미지, 폰트, SVG 아이콘 등)
├── components/ # 도메인에 종속되지 않는 공통 UI 컴포넌트 (버튼, 카드, 디자인 시스템)
├── features/ # 핵심 비즈니스 로직 및 도메인 영역 (이 구조의 핵심) ├── features/ # 핵심 비즈니스 로직 및 도메인 영역 (이 구조의 핵심)
│ ├── auth/ # 특정 도메인 (예: 인증) │ ├── home/ # 홈(랜딩) 도메인
│ │ ├── api/ # 해당 도메인 전용 API 통신 함수 │ │ ├── content/ # 해당 도메인 전용 UI 텍스트·카피 (정적 콘텐츠)
│ │ ├── hooks/ # 해당 도메인 전용 커스텀 훅 │ │ ├── hooks/ # 해당 도메인 전용 커스텀 훅
│ │ ├── store/ # 해당 도메인 전용 상태 (Zustand 등) │ │ └── ui/ # 해당 도메인 전용 UI 컴포넌트
│ ├── report/ # 리포트 도메인
│ │ ├── config/ # 섹션 ID·레이블 등 UI 설정값
│ │ ├── hooks/ # 해당 도메인 전용 커스텀 훅
│ │ ├── mocks/ # API 연동 전 임시 목업 데이터
│ │ ├── types/ # 해당 도메인 전용 타입 정의 │ │ ├── types/ # 해당 도메인 전용 타입 정의
│ │ └── ui/ # 해당 도메인 전용 UI 컴포넌트 │ │ └── ui/ # 해당 도메인 전용 UI 컴포넌트
├── hooks/ # 전역에서 사용하는 공통 훅 (useClickOutside 등) │ └── plan/ # 마케팅 플랜 도메인 (report와 동일한 구조)
├── layouts/ # 페이지 레이아웃 (GNB, Sidebar, 풋터 등) │ ├── config/
├── pages/ # 라우팅과 1:1 매칭되는 페이지 진입점 (여기서는 features의 컴포넌트만 조립) │ ├── hooks/
│ ├── mocks/
│ ├── types/
│ └── ui/
├── hooks/ # 전역에서 사용하는 공통 훅 (useInView 등)
├── layouts/ # 페이지 레이아웃 (GNB, SubNav, Footer 등)
├── pages/ # 라우팅과 1:1 매칭되는 페이지 진입점 (features의 컴포넌트만 조립)
├── services/ # 공통 API 클라이언트 설정 (Axios 인스턴스, 인터셉터 등) ├── services/ # 공통 API 클라이언트 설정 (Axios 인스턴스, 인터셉터 등)
├── store/ # 전역 상태 관리 (사용자 세션, 테마 등) ├── store/ # 전역 상태 관리 (사용자 세션, 테마 등)
└── utils/ # 공통 유틸리티 함수 (날짜 포맷팅, 정규식 등) ├── types/ # 여러 도메인에서 공유하는 공통 타입 정의
└── utils/ # 공통 유틸리티 함수 (숫자 포맷팅, URL 처리 등)
``` ```
## 시작하기 ## 시작하기

View File

@ -1,6 +1,6 @@
import type { CSSProperties } from "react"; import type { CSSProperties } from "react";
import EyeIcon from "@/assets/icons/eye.svg?react"; import EyeIcon from "@/assets/icons/eye.svg?react";
import { formatCompactNumber } from "@/lib/formatNumber"; import { formatCompactNumber } from "@/utils/formatNumber";
export type TopVideoCardProps = { export type TopVideoCardProps = {
title: string; title: string;

View File

@ -1,6 +1,4 @@
export { ComparisonRow, type ComparisonRowProps } from "@/components/compare/ComparisonRow";
export { BrandConsistencyMap, type BrandConsistencyMapProps } from "@/components/brand/BrandConsistencyMap"; export { BrandConsistencyMap, type BrandConsistencyMapProps } from "@/components/brand/BrandConsistencyMap";
export { OtherChannelRow, type OtherChannelRowProps } from "@/components/channel/OtherChannelRow";
export { SeverityBadge, type SeverityBadgeProps } from "@/components/badge/SeverityBadge"; export { SeverityBadge, type SeverityBadgeProps } from "@/components/badge/SeverityBadge";
export { ChannelScoreCard, type ChannelScoreCardProps } from "@/components/card/ChannelScoreCard"; export { ChannelScoreCard, type ChannelScoreCardProps } from "@/components/card/ChannelScoreCard";
export { InfoStatCard, type InfoStatCardProps } from "@/components/card/InfoStatCard"; export { InfoStatCard, type InfoStatCardProps } from "@/components/card/InfoStatCard";
@ -8,7 +6,6 @@ export { MetricCard, type MetricCardProps } from "@/components/card/MetricCard";
export { PixelInstallCard, type PixelInstallCardProps } from "@/components/card/PixelInstallCard"; export { PixelInstallCard, type PixelInstallCardProps } from "@/components/card/PixelInstallCard";
export { TopVideoCard, type TopVideoCardProps } from "@/components/card/TopVideoCard"; export { TopVideoCard, type TopVideoCardProps } from "@/components/card/TopVideoCard";
export { TagChipList, type TagChipListProps } from "@/components/chip/TagChipList"; export { TagChipList, type TagChipListProps } from "@/components/chip/TagChipList";
export { DiagnosisRow, type DiagnosisRowProps } from "@/components/diagnosis/DiagnosisRow";
export { ConsolidationCallout, type ConsolidationCalloutProps } from "@/components/panel/ConsolidationCallout"; export { ConsolidationCallout, type ConsolidationCalloutProps } from "@/components/panel/ConsolidationCallout";
export { HighlightPanel, type HighlightPanelProps } from "@/components/panel/HighlightPanel"; export { HighlightPanel, type HighlightPanelProps } from "@/components/panel/HighlightPanel";
export { ScoreRing, type ScoreRingProps } from "@/components/rating/ScoreRing"; export { ScoreRing, type ScoreRingProps } from "@/components/rating/ScoreRing";

View File

@ -1,5 +1,5 @@
import StarIcon from "@/assets/icons/star.svg?react"; import StarIcon from "@/assets/icons/star.svg?react";
import { formatCompactNumber } from "@/lib/formatNumber"; import { formatCompactNumber } from "@/utils/formatNumber";
export type StarRatingDisplayProps = { export type StarRatingDisplayProps = {
rating: number; rating: number;

View File

@ -5,7 +5,7 @@ import {
CTA_FOOTNOTE, CTA_FOOTNOTE,
CTA_HEADLINE, CTA_HEADLINE,
CTA_URL_PLACEHOLDER, CTA_URL_PLACEHOLDER,
} from "@/features/home/constants/cta_contents"; } from "@/features/home/content/cta";
import { useAnalyze } from "@/features/home/hooks/useAnalyze"; import { useAnalyze } from "@/features/home/hooks/useAnalyze";
import { useInView } from "@/hooks/useInView"; import { useInView } from "@/hooks/useInView";

View File

@ -7,7 +7,7 @@ import {
HERO_LEAD_EN, HERO_LEAD_EN,
HERO_LEAD_KO, HERO_LEAD_KO,
HERO_URL_PLACEHOLDER, HERO_URL_PLACEHOLDER,
} from "@/features/home/constants/hero_contents"; } from "@/features/home/content/hero";
import { useAnalyze } from "@/features/home/hooks/useAnalyze"; import { useAnalyze } from "@/features/home/hooks/useAnalyze";
export function HeroSection() { export function HeroSection() {

View File

@ -1,4 +1,4 @@
import { PROBLEM_CARDS, PROBLEM_CARD_STAGGER } from "@/features/home/constants/problem_contents"; import { PROBLEM_CARDS, PROBLEM_CARD_STAGGER } from "@/features/home/content/problem";
import { useInView } from "@/hooks/useInView"; import { useInView } from "@/hooks/useInView";
export function ProblemSection() { export function ProblemSection() {

View File

@ -1,4 +1,4 @@
import { SOLUTION_CARDS } from "@/features/home/constants/solution_contents"; import { SOLUTION_CARDS } from "@/features/home/content/solution";
import { useInView } from "@/hooks/useInView"; import { useInView } from "@/hooks/useInView";
export function SolutionSection() { export function SolutionSection() {

View File

@ -1,4 +1,4 @@
import { CORE_MODULES, MODULE_CARD_STAGGER } from "@/features/home/constants/modules_contents"; import { CORE_MODULES, MODULE_CARD_STAGGER } from "@/features/home/content/modules";
import { useInView } from "@/hooks/useInView"; import { useInView } from "@/hooks/useInView";
import { CoreModuleCard } from "./system/CoreModuleCard"; import { CoreModuleCard } from "./system/CoreModuleCard";
import { CoreModulesCenterHeading } from "./system/CoreModulesCenterHeading"; import { CoreModulesCenterHeading } from "./system/CoreModulesCenterHeading";

View File

@ -1,5 +1,5 @@
import CheckCircleIcon from "@/assets/home/check-circle.svg?react"; import CheckCircleIcon from "@/assets/home/check-circle.svg?react";
import { USE_CASE_CARDS } from "@/features/home/constants/use_cases_contents"; import { USE_CASE_CARDS } from "@/features/home/content/useCases";
import { useInView } from "@/hooks/useInView"; import { useInView } from "@/hooks/useInView";
export function UseCaseSection() { export function UseCaseSection() {

View File

@ -1,4 +1,4 @@
import { AGDP_NODES } from "@/features/home/constants/process_contents"; import { AGDP_NODES } from "@/features/home/content/process";
import { AgdpOrbitNode } from "./AgdpOrbitNode"; import { AgdpOrbitNode } from "./AgdpOrbitNode";
import { AgdpRewardPathLabel } from "./AgdpRewardPathLabel"; import { AgdpRewardPathLabel } from "./AgdpRewardPathLabel";

View File

@ -1,5 +1,5 @@
import type { AgdpNodeDef } from "@/features/home/constants/process_contents"; import type { AgdpNodeDef } from "@/features/home/content/process";
import { AGDP_SLOT_WRAPPER_CLASS } from "@/features/home/constants/process_contents"; import { AGDP_SLOT_WRAPPER_CLASS } from "@/features/home/content/process";
type Props = { node: AgdpNodeDef }; type Props = { node: AgdpNodeDef };

View File

@ -1,4 +1,4 @@
import type { CoreModule } from "@/features/home/constants/modules_contents"; import type { CoreModule } from "@/features/home/content/modules";
type Props = { type Props = {
mod: CoreModule; mod: CoreModule;

View File

@ -1,5 +1,5 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { MOCK_PLAN } from "@/features/plan/constants/mock_plan"; import { MOCK_PLAN } from "@/features/plan/mocks/plan";
import type { MarketingPlan } from "@/features/plan/types/marketingPlan"; import type { MarketingPlan } from "@/features/plan/types/marketingPlan";
type UseMarketingPlanResult = { type UseMarketingPlanResult = {

View File

@ -1,7 +1,7 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useMainSubNav } from "@/layouts/MainSubNavLayout"; import { useMainSubNav } from "@/layouts/MainSubNavLayout";
import type { SubNavItem } from "@/layouts/SubNav"; import type { SubNavItem } from "@/layouts/SubNav";
import { PLAN_SECTIONS } from "@/features/plan/constants/plan_sections"; import { PLAN_SECTIONS } from "@/features/plan/config/planSections";
export function usePlanSubNav() { export function usePlanSubNav() {
const { setSubNav } = useMainSubNav(); const { setSubNav } = useMainSubNav();

View File

@ -1,44 +0,0 @@
import { useCurrentMarketingPlan } from "@/features/plan/hooks/useCurrentMarketingPlan";
import { usePlanSubNav } from "@/features/plan/hooks/usePlanSubNav";
import { PlanAssetCollectionSection } from "@/features/plan/ui/PlanAssetCollectionSection";
import { PlanBrandingGuideSection } from "@/features/plan/ui/PlanBrandingGuideSection";
import { PlanChannelStrategySection } from "@/features/plan/ui/PlanChannelStrategySection";
import { PlanContentCalendarSection } from "@/features/plan/ui/PlanContentCalendarSection";
import { PlanContentStrategySection } from "@/features/plan/ui/PlanContentStrategySection";
import { PlanCtaSection } from "@/features/plan/ui/PlanCtaSection";
import { PlanHeaderSection } from "@/features/plan/ui/PlanHeaderSection";
import { PlanMyAssetUploadSection } from "@/features/plan/ui/PlanMyAssetUploadSection";
export function MarketingPlanPage() {
const { data, error } = useCurrentMarketingPlan();
usePlanSubNav();
if (error || !data) {
return (
<div className="min-h-[50vh] flex items-center justify-center px-6">
<div className="text-center max-w-md">
<p className="title-16 text-[var(--color-status-critical-text)] mb-2 break-keep">
</p>
<p className="body-14 text-neutral-60 break-keep">
{error ?? "마케팅 플랜을 찾을 수 없습니다."}
</p>
</div>
</div>
);
}
return (
<div data-plan-content data-plan-id={data.id}>
<PlanHeaderSection />
<PlanBrandingGuideSection />
<PlanChannelStrategySection />
<PlanContentStrategySection />
<PlanContentCalendarSection />
<PlanAssetCollectionSection />
<PlanMyAssetUploadSection />
<PlanCtaSection />
</div>
);
}

View File

@ -2,7 +2,7 @@ import { useCurrentMarketingPlan } from "@/features/plan/hooks/useCurrentMarketi
import { PlanHeaderDaysBadge } from "@/features/plan/ui/header/PlanHeaderDaysBadge"; import { PlanHeaderDaysBadge } from "@/features/plan/ui/header/PlanHeaderDaysBadge";
import { PlanHeaderHeroBlobs } from "@/features/plan/ui/header/PlanHeaderHeroBlobs"; import { PlanHeaderHeroBlobs } from "@/features/plan/ui/header/PlanHeaderHeroBlobs";
import { PlanHeaderHeroColumn } from "@/features/plan/ui/header/PlanHeaderHeroColumn"; import { PlanHeaderHeroColumn } from "@/features/plan/ui/header/PlanHeaderHeroColumn";
import { PLAN_HEADER_BG_CLASS } from "@/features/plan/ui/header/planHeaderSectionStyles"; import { PLAN_HEADER_BG_CLASS } from "@/features/plan/ui/header/planHeaderSectionClasses";
export function PlanHeaderSection() { export function PlanHeaderSection() {
const { data, error } = useCurrentMarketingPlan(); const { data, error } = useCurrentMarketingPlan();

View File

@ -14,7 +14,7 @@ import {
assetTypeBadgeClass, assetTypeBadgeClass,
assetTypeDisplayLabel, assetTypeDisplayLabel,
formatYoutubeViews, formatYoutubeViews,
} from "@/features/plan/ui/assetCollection/assetCollectionBadgeClass"; } from "@/features/plan/ui/assetCollection/assetCollectionBadgeClasses";
type AssetCollectionPanelProps = { type AssetCollectionPanelProps = {
data: AssetCollectionData; data: AssetCollectionData;

View File

@ -4,7 +4,7 @@ import { BrandingChannelIcon } from "@/features/plan/ui/branding/BrandingChannel
import { import {
brandingChannelStatusBadgeClass, brandingChannelStatusBadgeClass,
brandingChannelStatusLabel, brandingChannelStatusLabel,
} from "@/features/plan/ui/branding/brandingChannelStatusClass"; } from "@/features/plan/ui/branding/brandingChannelStatusClasses";
type BrandingChannelRulesTabProps = { type BrandingChannelRulesTabProps = {
channels: BrandGuide["channelBranding"]; channels: BrandGuide["channelBranding"];

View File

@ -2,7 +2,7 @@ import { useState } from "react";
import { BrandConsistencyMap } from "@/components/brand/BrandConsistencyMap"; import { BrandConsistencyMap } from "@/components/brand/BrandConsistencyMap";
import { SegmentTabButton } from "@/features/plan/ui/SegmentTabButton"; import { SegmentTabButton } from "@/features/plan/ui/SegmentTabButton";
import type { BrandGuide } from "@/features/plan/types/marketingPlan"; import type { BrandGuide } from "@/features/plan/types/marketingPlan";
import { BRANDING_GUIDE_TAB_ITEMS, type BrandingGuideTabKey } from "@/features/plan/ui/branding/brandingTabItems"; import { BRANDING_GUIDE_TAB_ITEMS, type BrandingGuideTabKey } from "@/features/plan/ui/branding/brandingTabs";
import { BrandingChannelRulesTab } from "@/features/plan/ui/branding/BrandingChannelRulesTab"; import { BrandingChannelRulesTab } from "@/features/plan/ui/branding/BrandingChannelRulesTab";
import { BrandingToneVoiceTab } from "@/features/plan/ui/branding/BrandingToneVoiceTab"; import { BrandingToneVoiceTab } from "@/features/plan/ui/branding/BrandingToneVoiceTab";
import { BrandingVisualIdentityTab } from "@/features/plan/ui/branding/BrandingVisualIdentityTab"; import { BrandingVisualIdentityTab } from "@/features/plan/ui/branding/BrandingVisualIdentityTab";

View File

@ -3,7 +3,7 @@ import { Pill } from "@/components/atoms/Pill";
import { Surface } from "@/components/atoms/Surface"; import { Surface } from "@/components/atoms/Surface";
import type { ChannelStrategyCard } from "@/features/plan/types/marketingPlan"; import type { ChannelStrategyCard } from "@/features/plan/types/marketingPlan";
import { BrandingChannelIcon } from "@/features/plan/ui/branding/BrandingChannelIcon"; import { BrandingChannelIcon } from "@/features/plan/ui/branding/BrandingChannelIcon";
import { channelStrategyPriorityPillClass } from "@/features/plan/ui/channelStrategy/channelStrategyPillClass"; import { channelStrategyPriorityPillClass } from "@/features/plan/ui/channelStrategy/channelStrategyPillClasses";
type ChannelStrategyGridProps = { type ChannelStrategyGridProps = {
channels: ChannelStrategyCard[]; channels: ChannelStrategyCard[];

View File

@ -5,7 +5,7 @@ import {
CALENDAR_CONTENT_TYPE_LABELS, CALENDAR_CONTENT_TYPE_LABELS,
CALENDAR_DAY_HEADERS, CALENDAR_DAY_HEADERS,
calendarContentTypeVisual, calendarContentTypeVisual,
} from "@/features/plan/ui/contentCalendar/calendarContentTypeVisual"; } from "@/features/plan/ui/contentCalendar/calendarContentTypeClasses";
import { ContentCalendarTypeIcon } from "@/features/plan/ui/contentCalendar/ContentCalendarTypeIcon"; import { ContentCalendarTypeIcon } from "@/features/plan/ui/contentCalendar/ContentCalendarTypeIcon";
type ContentCalendarPanelProps = { type ContentCalendarPanelProps = {

View File

@ -4,7 +4,7 @@ import type { ContentStrategyData } from "@/features/plan/types/marketingPlan";
import { import {
CONTENT_STRATEGY_TAB_ITEMS, CONTENT_STRATEGY_TAB_ITEMS,
type ContentStrategyTabKey, type ContentStrategyTabKey,
} from "@/features/plan/ui/contentStrategy/contentStrategyTabItems"; } from "@/features/plan/ui/contentStrategy/contentStrategyTabs";
import { ContentStrategyPillarsTab } from "@/features/plan/ui/contentStrategy/ContentStrategyPillarsTab"; import { ContentStrategyPillarsTab } from "@/features/plan/ui/contentStrategy/ContentStrategyPillarsTab";
import { ContentStrategyRepurposingTab } from "@/features/plan/ui/contentStrategy/ContentStrategyRepurposingTab"; import { ContentStrategyRepurposingTab } from "@/features/plan/ui/contentStrategy/ContentStrategyRepurposingTab";
import { ContentStrategyTypesTab } from "@/features/plan/ui/contentStrategy/ContentStrategyTypesTab"; import { ContentStrategyTypesTab } from "@/features/plan/ui/contentStrategy/ContentStrategyTypesTab";

View File

@ -1,6 +1,6 @@
import { Pill } from "@/components/atoms/Pill"; import { Pill } from "@/components/atoms/Pill";
import type { ContentTypeRow } from "@/features/plan/types/marketingPlan"; import type { ContentTypeRow } from "@/features/plan/types/marketingPlan";
import { contentStrategyChannelBadgeClass } from "@/features/plan/ui/contentStrategy/contentStrategyChannelBadgeClass"; import { contentStrategyChannelBadgeClass } from "@/features/plan/ui/contentStrategy/contentStrategyChannelBadgeClasses";
type ContentStrategyTypesTabProps = { type ContentStrategyTypesTabProps = {
rows: ContentTypeRow[]; rows: ContentTypeRow[];

View File

@ -1,6 +1,6 @@
import CalendarIcon from "@/assets/report/calendar.svg?react"; import CalendarIcon from "@/assets/report/calendar.svg?react";
import GlobeIcon from "@/assets/report/globe.svg?react"; import GlobeIcon from "@/assets/report/globe.svg?react";
import { PLAN_HEADER_META_CHIP_CLASS } from "@/features/plan/ui/header/planHeaderSectionStyles"; import { PLAN_HEADER_META_CHIP_CLASS } from "@/features/plan/ui/header/planHeaderSectionClasses";
type PlanHeaderMetaChipsProps = { type PlanHeaderMetaChipsProps = {
date: string; date: string;

View File

@ -1,7 +1,7 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useMainSubNav } from "@/layouts/MainSubNavLayout"; import { useMainSubNav } from "@/layouts/MainSubNavLayout";
import type { SubNavItem } from "@/layouts/SubNav"; import type { SubNavItem } from "@/layouts/SubNav";
import { REPORT_SECTIONS } from "@/features/report/constants/report_sections"; import { REPORT_SECTIONS } from "@/features/report/config/reportSections";
export function useReportSubNav() { export function useReportSubNav() {
const { setSubNav } = useMainSubNav(); const { setSubNav } = useMainSubNav();

View File

@ -1,4 +1,4 @@
import type { OtherChannelsReport } from "@/types/otherChannels"; import type { OtherChannelsReport } from "@/features/report/types/otherChannels";
/** DEMO `mockReport` 기타 채널 + 웹사이트 진단 */ /** DEMO `mockReport` 기타 채널 + 웹사이트 진단 */
export const MOCK_OTHER_CHANNELS_REPORT: OtherChannelsReport = { export const MOCK_OTHER_CHANNELS_REPORT: OtherChannelsReport = {

View File

@ -1,5 +1,5 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_CHANNEL_SCORES } from "@/features/report/constants/mock_channel_scores"; import { MOCK_CHANNEL_SCORES } from "@/features/report/mocks/channelScores";
import type { ChannelScore } from "@/features/report/types/channelScore"; import type { ChannelScore } from "@/features/report/types/channelScore";
import { ChannelScoreGrid } from "@/features/report/ui/channels/ChannelScoreGrid"; import { ChannelScoreGrid } from "@/features/report/ui/channels/ChannelScoreGrid";

View File

@ -1,5 +1,5 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_CLINIC_SNAPSHOT } from "@/features/report/constants/mock_clinic_snapshot"; import { MOCK_CLINIC_SNAPSHOT } from "@/features/report/mocks/clinicSnapshot";
import type { ClinicSnapshot } from "@/features/report/types/clinicSnapshot"; import type { ClinicSnapshot } from "@/features/report/types/clinicSnapshot";
import { ClinicCertificationsBlock } from "@/features/report/ui/clinic/ClinicCertificationsBlock"; import { ClinicCertificationsBlock } from "@/features/report/ui/clinic/ClinicCertificationsBlock";
import { ClinicInfoStatGrid } from "@/features/report/ui/clinic/ClinicInfoStatGrid"; import { ClinicInfoStatGrid } from "@/features/report/ui/clinic/ClinicInfoStatGrid";

View File

@ -1,5 +1,5 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_PROBLEM_DIAGNOSIS } from "@/features/report/constants/mock_problem_diagnosis"; import { MOCK_PROBLEM_DIAGNOSIS } from "@/features/report/mocks/problemDiagnosis";
import type { DiagnosisItem } from "@/features/report/types/diagnosis"; import type { DiagnosisItem } from "@/features/report/types/diagnosis";
import { ProblemDiagnosisCard } from "@/features/report/ui/diagnosis/ProblemDiagnosisCard"; import { ProblemDiagnosisCard } from "@/features/report/ui/diagnosis/ProblemDiagnosisCard";

View File

@ -1,9 +1,9 @@
import GlobeIcon from "@/assets/report/globe.svg?react"; import GlobeIcon from "@/assets/report/globe.svg?react";
import { BrandConsistencyMap } from "@/components/brand/BrandConsistencyMap"; import { BrandConsistencyMap } from "@/components/brand/BrandConsistencyMap";
import { DiagnosisRow } from "@/components/diagnosis/DiagnosisRow"; import { DiagnosisRow } from "@/features/report/ui/diagnosis/DiagnosisRow";
import { ConsolidationCallout } from "@/components/panel/ConsolidationCallout"; import { ConsolidationCallout } from "@/components/panel/ConsolidationCallout";
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_FACEBOOK_AUDIT } from "@/features/report/constants/mock_facebook_audit"; import { MOCK_FACEBOOK_AUDIT } from "@/features/report/mocks/facebookAudit";
import type { FacebookAudit } from "@/features/report/types/facebookAudit"; import type { FacebookAudit } from "@/features/report/types/facebookAudit";
import { FacebookPageCard } from "@/features/report/ui/facebook/FacebookPageCard"; import { FacebookPageCard } from "@/features/report/ui/facebook/FacebookPageCard";

View File

@ -1,6 +1,6 @@
import { DiagnosisRow } from "@/components/diagnosis/DiagnosisRow"; import { DiagnosisRow } from "@/features/report/ui/diagnosis/DiagnosisRow";
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_INSTAGRAM_AUDIT } from "@/features/report/constants/mock_instagram_audit"; import { MOCK_INSTAGRAM_AUDIT } from "@/features/report/mocks/instagramAudit";
import type { InstagramAudit } from "@/features/report/types/instagramAudit"; import type { InstagramAudit } from "@/features/report/types/instagramAudit";
import { InstagramAccountCard } from "@/features/report/ui/instagram/InstagramAccountCard"; import { InstagramAccountCard } from "@/features/report/ui/instagram/InstagramAccountCard";

View File

@ -1,5 +1,5 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_KPI_METRICS } from "@/features/report/constants/mock_kpi"; import { MOCK_KPI_METRICS } from "@/features/report/mocks/kpi";
import type { KpiMetric } from "@/features/report/types/kpiDashboard"; import type { KpiMetric } from "@/features/report/types/kpiDashboard";
import { KpiMetricsTable } from "@/features/report/ui/kpi/KpiMetricsTable"; import { KpiMetricsTable } from "@/features/report/ui/kpi/KpiMetricsTable";
import { KpiTransformationCtaCard } from "@/features/report/ui/kpi/KpiTransformationCtaCard"; import { KpiTransformationCtaCard } from "@/features/report/ui/kpi/KpiTransformationCtaCard";

View File

@ -1,6 +1,6 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_OTHER_CHANNELS_REPORT } from "@/features/report/constants/mock_other_channels"; import { MOCK_OTHER_CHANNELS_REPORT } from "@/features/report/mocks/otherChannels";
import type { OtherChannelsReport } from "@/types/otherChannels"; import type { OtherChannelsReport } from "@/features/report/types/otherChannels";
import { OtherChannelsList } from "@/features/report/ui/otherChannels/OtherChannelsList"; import { OtherChannelsList } from "@/features/report/ui/otherChannels/OtherChannelsList";
import { WebsiteTechAuditBlock } from "@/features/report/ui/otherChannels/WebsiteTechAuditBlock"; import { WebsiteTechAuditBlock } from "@/features/report/ui/otherChannels/WebsiteTechAuditBlock";

View File

@ -1,9 +1,9 @@
import { MOCK_REPORT_OVERVIEW } from "@/features/report/constants/mock_report_overview"; import { MOCK_REPORT_OVERVIEW } from "@/features/report/mocks/reportOverview";
import type { ReportOverviewData } from "@/features/report/types/reportOverview"; import type { ReportOverviewData } from "@/features/report/types/reportOverview";
import { OverviewHeroBlobs } from "@/features/report/ui/overview/OverviewHeroBlobs"; import { OverviewHeroBlobs } from "@/features/report/ui/overview/OverviewHeroBlobs";
import { OverviewHeroColumn } from "@/features/report/ui/overview/OverviewHeroColumn"; import { OverviewHeroColumn } from "@/features/report/ui/overview/OverviewHeroColumn";
import { OverviewScorePanel } from "@/features/report/ui/overview/OverviewScorePanel"; import { OverviewScorePanel } from "@/features/report/ui/overview/OverviewScorePanel";
import { OVERVIEW_SECTION_BG_CLASS } from "@/features/report/ui/overview/overviewSectionStyles"; import { OVERVIEW_SECTION_BG_CLASS } from "@/features/report/ui/overview/overviewSectionClasses";
type ReportOverviewSectionProps = { type ReportOverviewSectionProps = {
data?: ReportOverviewData; data?: ReportOverviewData;

View File

@ -1,40 +0,0 @@
import { useParams } from "react-router-dom";
import { useReportSubNav } from "@/features/report/hooks/useReportSubNav";
import { ReportChannelsSection } from "@/features/report/ui/ReportChannelsSection";
import { ReportClinicSection } from "@/features/report/ui/ReportClinicSection";
import { ReportDiagnosisSection } from "@/features/report/ui/ReportDiagnosisSection";
import { ReportFacebookSection } from "@/features/report/ui/ReportFacebookSection";
import { ReportInstagramSection } from "@/features/report/ui/ReportInstagramSection";
import { ReportKpiSection } from "@/features/report/ui/ReportKpiSection";
import { ReportOtherChannelsSection } from "@/features/report/ui/ReportOtherChannelsSection";
import { ReportOverviewSection } from "@/features/report/ui/ReportOverviewSection";
import { ReportRoadmapSection } from "@/features/report/ui/ReportRoadmapSection";
import { ReportTransformationSection } from "@/features/report/ui/ReportTransformationSection";
import { ReportYouTubeSection } from "@/features/report/ui/ReportYouTubeSection";
export function ReportPage() {
const { id } = useParams<{ id: string }>();
useReportSubNav();
return (
<div data-report-content>
<p className="body-14 text-neutral-60 px-6 max-w-7xl mx-auto py-2">
Report ID: <span className="text-navy-900 font-medium">{id}</span>
</p>
<div className="divide-y divide-neutral-20">
<ReportOverviewSection />
<ReportClinicSection />
<ReportChannelsSection />
<ReportYouTubeSection />
<ReportInstagramSection />
<ReportFacebookSection />
<ReportOtherChannelsSection />
<ReportDiagnosisSection />
<ReportTransformationSection />
<ReportRoadmapSection />
<ReportKpiSection />
</div>
</div>
);
}

View File

@ -1,5 +1,5 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_ROADMAP } from "@/features/report/constants/mock_roadmap"; import { MOCK_ROADMAP } from "@/features/report/mocks/roadmap";
import type { RoadmapMonth } from "@/features/report/types/roadmap"; import type { RoadmapMonth } from "@/features/report/types/roadmap";
import { RoadmapMonthsGrid } from "@/features/report/ui/roadmap/RoadmapMonthsGrid"; import { RoadmapMonthsGrid } from "@/features/report/ui/roadmap/RoadmapMonthsGrid";

View File

@ -1,5 +1,5 @@
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_TRANSFORMATION } from "@/features/report/constants/mock_transformation"; import { MOCK_TRANSFORMATION } from "@/features/report/mocks/transformation";
import type { TransformationProposal } from "@/features/report/types/transformationProposal"; import type { TransformationProposal } from "@/features/report/types/transformationProposal";
import { TransformationTabbedView } from "@/features/report/ui/transformation/TransformationTabbedView"; import { TransformationTabbedView } from "@/features/report/ui/transformation/TransformationTabbedView";

View File

@ -1,7 +1,7 @@
import { TagChipList } from "@/components/chip/TagChipList"; import { TagChipList } from "@/components/chip/TagChipList";
import { DiagnosisRow } from "@/components/diagnosis/DiagnosisRow"; import { DiagnosisRow } from "@/features/report/ui/diagnosis/DiagnosisRow";
import { PageSection } from "@/components/section/PageSection"; import { PageSection } from "@/components/section/PageSection";
import { MOCK_YOUTUBE_AUDIT } from "@/features/report/constants/mock_youtube_audit"; import { MOCK_YOUTUBE_AUDIT } from "@/features/report/mocks/youtubeAudit";
import type { YouTubeAudit } from "@/features/report/types/youtubeAudit"; import type { YouTubeAudit } from "@/features/report/types/youtubeAudit";
import { YouTubeChannelInfoCard } from "@/features/report/ui/youtube/YouTubeChannelInfoCard"; import { YouTubeChannelInfoCard } from "@/features/report/ui/youtube/YouTubeChannelInfoCard";
import { YouTubeMetricsGrid } from "@/features/report/ui/youtube/YouTubeMetricsGrid"; import { YouTubeMetricsGrid } from "@/features/report/ui/youtube/YouTubeMetricsGrid";

View File

@ -6,7 +6,7 @@ import CalendarIcon from "@/assets/report/calendar.svg?react";
import GlobeIcon from "@/assets/report/globe.svg?react"; import GlobeIcon from "@/assets/report/globe.svg?react";
import MapPinIcon from "@/assets/report/map-pin.svg?react"; import MapPinIcon from "@/assets/report/map-pin.svg?react";
import type { ClinicSnapshot } from "@/features/report/types/clinicSnapshot"; import type { ClinicSnapshot } from "@/features/report/types/clinicSnapshot";
import { formatCompactNumber } from "@/lib/formatNumber"; import { formatCompactNumber } from "@/utils/formatNumber";
export type ClinicStatRow = { export type ClinicStatRow = {
label: string; label: string;

View File

@ -1,6 +1,6 @@
import AlertCircleIcon from "@/assets/icons/alert-circle.svg?react"; import AlertCircleIcon from "@/assets/icons/alert-circle.svg?react";
import type { DiagnosisItem } from "@/features/report/types/diagnosis"; import type { DiagnosisItem } from "@/features/report/types/diagnosis";
import { problemDiagnosisSeverityDotClass } from "@/features/report/ui/diagnosis/severityDotClass"; import { problemDiagnosisSeverityDotClass } from "@/features/report/ui/diagnosis/severityDotClasses";
export type ProblemDiagnosisCardProps = { export type ProblemDiagnosisCardProps = {
item: DiagnosisItem; item: DiagnosisItem;

View File

@ -8,9 +8,9 @@ import ImageIcon from "@/assets/report/image.svg?react";
import Link2Icon from "@/assets/report/link-2.svg?react"; import Link2Icon from "@/assets/report/link-2.svg?react";
import MessageCircleIcon from "@/assets/report/message-circle.svg?react"; import MessageCircleIcon from "@/assets/report/message-circle.svg?react";
import type { FacebookPage } from "@/features/report/types/facebookAudit"; import type { FacebookPage } from "@/features/report/types/facebookAudit";
import { facebookLangBadgeClass } from "@/features/report/ui/facebook/langBadgeClass"; import { facebookLangBadgeClass } from "@/features/report/ui/facebook/langBadgeClasses";
import { formatCompactNumber } from "@/lib/formatNumber"; import { formatCompactNumber } from "@/utils/formatNumber";
import { safeUrl } from "@/lib/safeUrl"; import { safeUrl } from "@/utils/safeUrl";
export type FacebookPageCardProps = { export type FacebookPageCardProps = {
page: FacebookPage; page: FacebookPage;

View File

@ -3,9 +3,9 @@ import ChannelInstagramIcon from "@/assets/icons/channel-instagram.svg?react";
import ExternalLinkIcon from "@/assets/icons/external-link.svg?react"; import ExternalLinkIcon from "@/assets/icons/external-link.svg?react";
import { TagChipList } from "@/components/chip/TagChipList"; import { TagChipList } from "@/components/chip/TagChipList";
import type { InstagramAccount } from "@/features/report/types/instagramAudit"; import type { InstagramAccount } from "@/features/report/types/instagramAudit";
import { instagramLangBadgeClass } from "@/features/report/ui/instagram/langBadgeClass"; import { instagramLangBadgeClass } from "@/features/report/ui/instagram/langBadgeClasses";
import { formatCompactNumber } from "@/lib/formatNumber"; import { formatCompactNumber } from "@/utils/formatNumber";
import { safeUrl } from "@/lib/safeUrl"; import { safeUrl } from "@/utils/safeUrl";
export type InstagramAccountCardProps = { export type InstagramAccountCardProps = {
account: InstagramAccount; account: InstagramAccount;

View File

@ -2,8 +2,8 @@ import ExternalLinkIcon from "@/assets/icons/external-link.svg?react";
import CheckCircleIcon from "@/assets/report/check-circle.svg?react"; import CheckCircleIcon from "@/assets/report/check-circle.svg?react";
import HelpCircleIcon from "@/assets/report/help-circle.svg?react"; import HelpCircleIcon from "@/assets/report/help-circle.svg?react";
import XCircleIcon from "@/assets/report/x-circle.svg?react"; import XCircleIcon from "@/assets/report/x-circle.svg?react";
import type { OtherChannelStatus } from "@/types/otherChannels"; import type { OtherChannelStatus } from "@/features/report/types/otherChannels";
import { safeUrl } from "@/lib/safeUrl"; import { safeUrl } from "@/utils/safeUrl";
export type OtherChannelRowProps = { export type OtherChannelRowProps = {
name: string; name: string;

View File

@ -1,5 +1,5 @@
import { OtherChannelRow } from "@/components/channel/OtherChannelRow"; import { OtherChannelRow } from "@/features/report/ui/otherChannels/OtherChannelRow";
import type { OtherChannel } from "@/types/otherChannels"; import type { OtherChannel } from "@/features/report/types/otherChannels";
export type OtherChannelsListProps = { export type OtherChannelsListProps = {
channels: OtherChannel[]; channels: OtherChannel[];

View File

@ -2,7 +2,7 @@ import AlertCircleIcon from "@/assets/icons/alert-circle.svg?react";
import CheckCircleIcon from "@/assets/report/check-circle.svg?react"; import CheckCircleIcon from "@/assets/report/check-circle.svg?react";
import GlobeIcon from "@/assets/report/globe.svg?react"; import GlobeIcon from "@/assets/report/globe.svg?react";
import { PixelInstallCard } from "@/components/card/PixelInstallCard"; import { PixelInstallCard } from "@/components/card/PixelInstallCard";
import type { WebsiteAudit } from "@/types/otherChannels"; import type { WebsiteAudit } from "@/features/report/types/otherChannels";
export type WebsiteTechAuditBlockProps = { export type WebsiteTechAuditBlockProps = {
website: WebsiteAudit; website: WebsiteAudit;

View File

@ -1,7 +1,7 @@
import CalendarIcon from "@/assets/report/calendar.svg?react"; import CalendarIcon from "@/assets/report/calendar.svg?react";
import GlobeIcon from "@/assets/report/globe.svg?react"; import GlobeIcon from "@/assets/report/globe.svg?react";
import MapPinIcon from "@/assets/report/map-pin.svg?react"; import MapPinIcon from "@/assets/report/map-pin.svg?react";
import { OVERVIEW_META_CHIP_CLASS } from "@/features/report/ui/overview/overviewSectionStyles"; import { OVERVIEW_META_CHIP_CLASS } from "@/features/report/ui/overview/overviewSectionClasses";
export type OverviewMetaChipsProps = { export type OverviewMetaChipsProps = {
date: string; date: string;

View File

@ -1,5 +1,5 @@
import type { NewChannelProposal } from "@/features/report/types/transformationProposal"; import type { NewChannelProposal } from "@/features/report/types/transformationProposal";
import { newChannelPriorityClass } from "@/features/report/ui/transformation/newChannelPriorityClass"; import { newChannelPriorityClass } from "@/features/report/ui/transformation/newChannelPriorityClasses";
export type NewChannelProposalsTableProps = { export type NewChannelProposalsTableProps = {
rows: NewChannelProposal[]; rows: NewChannelProposal[];

View File

@ -1,5 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { ComparisonRow } from "@/components/compare/ComparisonRow"; import { ComparisonRow } from "@/features/report/ui/transformation/ComparisonRow";
import type { TransformationProposal } from "@/features/report/types/transformationProposal"; import type { TransformationProposal } from "@/features/report/types/transformationProposal";
import { NewChannelProposalsTable } from "@/features/report/ui/transformation/NewChannelProposalsTable"; import { NewChannelProposalsTable } from "@/features/report/ui/transformation/NewChannelProposalsTable";
import { PlatformStrategyCard } from "@/features/report/ui/transformation/PlatformStrategyCard"; import { PlatformStrategyCard } from "@/features/report/ui/transformation/PlatformStrategyCard";

View File

@ -1,7 +1,7 @@
import ChannelYoutubeIcon from "@/assets/icons/channel-youtube.svg?react"; import ChannelYoutubeIcon from "@/assets/icons/channel-youtube.svg?react";
import ExternalLinkIcon from "@/assets/icons/external-link.svg?react"; import ExternalLinkIcon from "@/assets/icons/external-link.svg?react";
import type { YouTubeAudit } from "@/features/report/types/youtubeAudit"; import type { YouTubeAudit } from "@/features/report/types/youtubeAudit";
import { safeUrl } from "@/lib/safeUrl"; import { safeUrl } from "@/utils/safeUrl";
export type YouTubeChannelInfoCardProps = { export type YouTubeChannelInfoCardProps = {
data: YouTubeAudit; data: YouTubeAudit;

View File

@ -4,7 +4,7 @@ import VideoIcon from "@/assets/icons/video.svg?react";
import UsersIcon from "@/assets/icons/users.svg?react"; import UsersIcon from "@/assets/icons/users.svg?react";
import { MetricCard } from "@/components/card/MetricCard"; import { MetricCard } from "@/components/card/MetricCard";
import type { YouTubeAudit } from "@/features/report/types/youtubeAudit"; import type { YouTubeAudit } from "@/features/report/types/youtubeAudit";
import { formatCompactNumber } from "@/lib/formatNumber"; import { formatCompactNumber } from "@/utils/formatNumber";
export type YouTubeMetricsGridProps = { export type YouTubeMetricsGridProps = {
data: YouTubeAudit; data: YouTubeAudit;

View File

@ -68,7 +68,7 @@ export function PageNavigator() {
{/* 이전 페이지 */} {/* 이전 페이지 */}
<button <button
type="button" type="button"
onClick={() => prev && navigate(prev.navigatePath)} onClick={() => { if (prev) { navigate(prev.navigatePath); window.scrollTo({ top: 0, behavior: "smooth" }); } }}
disabled={!prev} disabled={!prev}
aria-label={prev ? `이전: ${prev.label}` : "이전 페이지 없음"} aria-label={prev ? `이전: ${prev.label}` : "이전 페이지 없음"}
className="body-14-medium flex items-center gap-2 px-3 py-2 rounded-full text-neutral-70 transition-all hover:bg-neutral-20 hover:text-navy-900 disabled:opacity-30 disabled:cursor-not-allowed cursor-pointer" className="body-14-medium flex items-center gap-2 px-3 py-2 rounded-full text-neutral-70 transition-all hover:bg-neutral-20 hover:text-navy-900 disabled:opacity-30 disabled:cursor-not-allowed cursor-pointer"
@ -83,7 +83,7 @@ export function PageNavigator() {
<button <button
key={page.id} key={page.id}
type="button" type="button"
onClick={() => navigate(page.navigatePath)} onClick={() => { navigate(page.navigatePath); window.scrollTo({ top: 0, behavior: "smooth" }); }}
title={page.label} title={page.label}
aria-label={page.label} aria-label={page.label}
className={`rounded-full transition-all cursor-pointer ${ className={`rounded-full transition-all cursor-pointer ${
@ -98,7 +98,7 @@ export function PageNavigator() {
{/* 다음 페이지 */} {/* 다음 페이지 */}
<button <button
type="button" type="button"
onClick={() => next && navigate(next.navigatePath)} onClick={() => { if (next) { navigate(next.navigatePath); window.scrollTo({ top: 0, behavior: "smooth" }); } }}
disabled={!next} disabled={!next}
aria-label={next ? `다음: ${next.label}` : "다음 페이지 없음"} aria-label={next ? `다음: ${next.label}` : "다음 페이지 없음"}
className="body-14-medium flex items-center gap-2 px-3 py-2 rounded-full text-neutral-70 transition-all hover:bg-neutral-20 hover:text-navy-900 disabled:opacity-30 disabled:cursor-not-allowed cursor-pointer" className="body-14-medium flex items-center gap-2 px-3 py-2 rounded-full text-neutral-70 transition-all hover:bg-neutral-20 hover:text-navy-900 disabled:opacity-30 disabled:cursor-not-allowed cursor-pointer"

View File

@ -1,5 +1,44 @@
import { MarketingPlanPage } from "@/features/plan/ui/MarketingPlanPage"; import { useCurrentMarketingPlan } from "@/features/plan/hooks/useCurrentMarketingPlan";
import { usePlanSubNav } from "@/features/plan/hooks/usePlanSubNav";
import { PlanHeaderSection } from "@/features/plan/ui/PlanHeaderSection";
import { PlanBrandingGuideSection } from "@/features/plan/ui/PlanBrandingGuideSection";
import { PlanChannelStrategySection } from "@/features/plan/ui/PlanChannelStrategySection";
import { PlanContentStrategySection } from "@/features/plan/ui/PlanContentStrategySection";
import { PlanContentCalendarSection } from "@/features/plan/ui/PlanContentCalendarSection";
import { PlanAssetCollectionSection } from "@/features/plan/ui/PlanAssetCollectionSection";
import { PlanMyAssetUploadSection } from "@/features/plan/ui/PlanMyAssetUploadSection";
import { PlanCtaSection } from "@/features/plan/ui/PlanCtaSection";
export function PlanPage() { export function PlanPage() {
return <MarketingPlanPage />; const { data, error } = useCurrentMarketingPlan();
usePlanSubNav();
if (error || !data) {
return (
<div className="min-h-[50vh] flex items-center justify-center px-6">
<div className="text-center max-w-md">
<p className="title-16 text-[var(--color-status-critical-text)] mb-2 break-keep">
</p>
<p className="body-14 text-neutral-60 break-keep">
{error ?? "마케팅 플랜을 찾을 수 없습니다."}
</p>
</div>
</div>
);
}
return (
<div data-plan-content data-plan-id={data.id}>
<PlanHeaderSection />
<PlanBrandingGuideSection />
<PlanChannelStrategySection />
<PlanContentStrategySection />
<PlanContentCalendarSection />
<PlanAssetCollectionSection />
<PlanMyAssetUploadSection />
<PlanCtaSection />
</div>
);
} }

View File

@ -1,5 +1,34 @@
import { ReportPage as ReportFeaturePage } from "@/features/report/ui/ReportPage"; import { useReportSubNav } from "@/features/report/hooks/useReportSubNav";
import { ReportOverviewSection } from "@/features/report/ui/ReportOverviewSection";
import { ReportClinicSection } from "@/features/report/ui/ReportClinicSection";
import { ReportChannelsSection } from "@/features/report/ui/ReportChannelsSection";
import { ReportYouTubeSection } from "@/features/report/ui/ReportYouTubeSection";
import { ReportInstagramSection } from "@/features/report/ui/ReportInstagramSection";
import { ReportFacebookSection } from "@/features/report/ui/ReportFacebookSection";
import { ReportOtherChannelsSection } from "@/features/report/ui/ReportOtherChannelsSection";
import { ReportDiagnosisSection } from "@/features/report/ui/ReportDiagnosisSection";
import { ReportTransformationSection } from "@/features/report/ui/ReportTransformationSection";
import { ReportRoadmapSection } from "@/features/report/ui/ReportRoadmapSection";
import { ReportKpiSection } from "@/features/report/ui/ReportKpiSection";
export function ReportPage() { export function ReportPage() {
return <ReportFeaturePage />; useReportSubNav();
return (
<div data-report-content>
<div className="divide-y divide-neutral-20">
<ReportOverviewSection />
<ReportClinicSection />
<ReportChannelsSection />
<ReportYouTubeSection />
<ReportInstagramSection />
<ReportFacebookSection />
<ReportOtherChannelsSection />
<ReportDiagnosisSection />
<ReportTransformationSection />
<ReportRoadmapSection />
<ReportKpiSection />
</div>
</div>
);
} }

2
src/services/index.ts Normal file
View File

@ -0,0 +1,2 @@
// 공통 API 클라이언트 설정 (Axios 인스턴스, 인터셉터 등)
// TODO: Phase 1 백엔드 연동 시 구현

2
src/store/index.ts Normal file
View File

@ -0,0 +1,2 @@
// 전역 상태 관리 (사용자 세션, 테마 등)
// TODO: Phase 1 백엔드 연동 시 구현