UI수정 및 블러처리 조건 변경
parent
440a6c8b72
commit
cab67c711a
19
index.css
19
index.css
|
|
@ -5597,14 +5597,14 @@
|
|||
}
|
||||
|
||||
.dashboard-description {
|
||||
font-size: 10px;
|
||||
font-size: 15px;
|
||||
color: var(--color-text-gray-500);
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.dashboard-description {
|
||||
font-size: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5898,7 +5898,6 @@
|
|||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
|
@ -6096,16 +6095,19 @@
|
|||
/* Mode Toggle */
|
||||
.mode-toggle {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--color-border-gray-700);
|
||||
border-radius: var(--radius-full);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
min-width: 48px;
|
||||
padding: 0.375rem 0.875rem;
|
||||
background-color: transparent;
|
||||
color: var(--color-text-gray-400);
|
||||
font-size: var(--text-sm);
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
|
|
@ -6503,23 +6505,16 @@
|
|||
/* Stats Grid 8 Columns */
|
||||
.stats-grid-8 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.stats-grid-8 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.stats-grid-8 {
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Platform Metrics Grid 8 Columns */
|
||||
.platform-metrics-grid-8 {
|
||||
display: grid;
|
||||
|
|
@ -6758,7 +6753,7 @@
|
|||
.audience-bar-label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--color-text-gray-400);
|
||||
width: 40px;
|
||||
width: 60px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,5 +11,7 @@ if (!rootElement) {
|
|||
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authenticatedFetch, API_URL } from '../../utils/api';
|
||||
|
|
@ -9,6 +8,7 @@ import {
|
|||
|
||||
// 환경변수에서 테스트 모드 확인
|
||||
const isTestPage = import.meta.env.VITE_IS_TESTPAGE === 'true';
|
||||
|
||||
// =====================================================
|
||||
// Types
|
||||
// =====================================================
|
||||
|
|
@ -132,7 +132,7 @@ const MOCK_TOP_CONTENT: TopContent[] = [
|
|||
|
||||
const MOCK_AUDIENCE_DATA: AudienceData = {
|
||||
ageGroups: [
|
||||
{ label: '18-24', percentage: 12 },
|
||||
{ label: '13-24', percentage: 12 },
|
||||
{ label: '25-34', percentage: 35 },
|
||||
{ label: '35-44', percentage: 28 },
|
||||
{ label: '45-54', percentage: 18 },
|
||||
|
|
@ -140,11 +140,11 @@ const MOCK_AUDIENCE_DATA: AudienceData = {
|
|||
],
|
||||
gender: { male: 42, female: 58 },
|
||||
topRegions: [
|
||||
{ region: '서울', percentage: 32 },
|
||||
{ region: '경기', percentage: 24 },
|
||||
{ region: '부산', percentage: 12 },
|
||||
{ region: '인천', percentage: 8 },
|
||||
{ region: '대구', percentage: 6 },
|
||||
{ region: '한국', percentage: 77 },
|
||||
{ region: '일본', percentage: 5 },
|
||||
{ region: '중국', percentage: 4 },
|
||||
{ region: '미국', percentage: 2 },
|
||||
{ region: '인도', percentage: 1 },
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -338,7 +338,7 @@ const YearOverYearChart: React.FC<{
|
|||
tick={{ fill: 'rgba(255,255,255,0.4)', fontSize: 12 }}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
interval={mode === 'day' ? 1 : 0}
|
||||
interval={mode === 'day' ? 2 : 0}
|
||||
/>
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Area
|
||||
|
|
@ -347,8 +347,8 @@ const YearOverYearChart: React.FC<{
|
|||
stroke="#a6ffea"
|
||||
strokeWidth={3}
|
||||
fill="url(#thisYearGradient)"
|
||||
dot={{ fill: '#a6ffea', r: 4, filter: 'drop-shadow(0 0 6px rgba(166,255,234,0.6))' }}
|
||||
activeDot={{ r: 6, fill: '#a6ffea' }}
|
||||
dot={{ fill: '#a6ffea', r: 2, filter: 'drop-shadow(0 0 6px rgba(166,255,234,0.6))' }}
|
||||
activeDot={{ r: 4, fill: '#a6ffea' }}
|
||||
isAnimationActive={true}
|
||||
/>
|
||||
<Line
|
||||
|
|
@ -621,7 +621,10 @@ const DashboardContent: React.FC<DashboardContentProps> = ({ onNavigate }) => {
|
|||
// API 데이터 우선 사용, 없거나 영상 없음(isEmptyState) 시 Mock 데이터로 폴백
|
||||
const contentMetrics = (!isEmptyState && dashboardData?.contentMetrics?.length) ? dashboardData.contentMetrics : MOCK_CONTENT_METRICS;
|
||||
const topContent = (!isEmptyState && dashboardData?.topContent?.length) ? dashboardData.topContent : MOCK_TOP_CONTENT;
|
||||
const hasRealAudienceData = !isEmptyState && !!dashboardData?.audienceData?.ageGroups?.length;
|
||||
const hasRealAgeGroups = !isEmptyState && !!dashboardData?.audienceData?.ageGroups?.some(g => g.percentage > 0);
|
||||
const hasRealGender = !isEmptyState && ((dashboardData?.audienceData?.gender?.male ?? 0) + (dashboardData?.audienceData?.gender?.female ?? 0)) > 0;
|
||||
const hasRealTopRegions = !isEmptyState && !!dashboardData?.audienceData?.topRegions?.some(r => r.percentage > 0);
|
||||
const hasRealAudienceData = hasRealAgeGroups && hasRealGender && hasRealTopRegions;
|
||||
const audienceData = hasRealAudienceData ? dashboardData!.audienceData : MOCK_AUDIENCE_DATA;
|
||||
|
||||
// mode별 차트 데이터를 ChartDataPoint 통합 형식으로 변환
|
||||
|
|
@ -783,7 +786,7 @@ const DashboardContent: React.FC<DashboardContentProps> = ({ onNavigate }) => {
|
|||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h2 className="dashboard-section-title" style={{ marginBottom: '2px' }}>{t('dashboard.contentPerformance')}</h2>
|
||||
<p style={{ fontSize: '12px', color: 'rgba(255,255,255,0.4)', margin: 0 }}>ADO2에서 업로드한 영상의 통계가 표시됩니다.</p>
|
||||
<p style={{ fontSize: '12px', color: 'rgba(255,255,255,0.4)', margin: 0 }}>ADO2에서 업로드한 최근 30개의 영상 통계가 표시됩니다.</p>
|
||||
</div>
|
||||
<div className="mode-toggle">
|
||||
<button
|
||||
|
|
@ -859,49 +862,43 @@ const DashboardContent: React.FC<DashboardContentProps> = ({ onNavigate }) => {
|
|||
<h2 className="dashboard-section-title" style={{ marginBottom: '2px' }}>{t('dashboard.audienceInsights')}</h2>
|
||||
<p style={{ fontSize: '12px', color: 'rgba(255,255,255,0.4)', margin: 0 }}>선택한 채널의 통계가 표시됩니다.</p>
|
||||
</div>
|
||||
{audienceData ? (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div className="audience-cards" style={!hasRealAudienceData && !showMockData ? { filter: 'blur(4px)', pointerEvents: 'none', userSelect: 'none' } : {}}>
|
||||
<AnimatedItem index={0} baseDelay={1100}>
|
||||
<div className="audience-card">
|
||||
<h3 className="audience-card-title">{t('dashboard.ageDistribution')}</h3>
|
||||
<AudienceBarChart data={audienceData.ageGroups} delay={1200} />
|
||||
</div>
|
||||
</AnimatedItem>
|
||||
<AnimatedItem index={1} baseDelay={1100}>
|
||||
<div className="audience-card">
|
||||
<h3 className="audience-card-title">{t('dashboard.genderDistribution')}</h3>
|
||||
<GenderChart male={audienceData.gender.male} female={audienceData.gender.female} delay={1300} />
|
||||
</div>
|
||||
</AnimatedItem>
|
||||
<AnimatedItem index={2} baseDelay={1100}>
|
||||
<div className="audience-card">
|
||||
<h3 className="audience-card-title">{t('dashboard.topRegions')}</h3>
|
||||
<AudienceBarChart data={audienceData.topRegions.map(r => ({ label: r.region, percentage: r.percentage }))} delay={1400} />
|
||||
</div>
|
||||
</AnimatedItem>
|
||||
</div>
|
||||
{dashboardData && !isEmptyState && !hasRealAudienceData && (
|
||||
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}>
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px',
|
||||
background: 'rgba(0,0,0,0.45)',
|
||||
backdropFilter: 'blur(2px)',
|
||||
borderRadius: '12px',
|
||||
padding: '20px 28px',
|
||||
}}>
|
||||
<p style={{ color: 'rgba(255,255,255,0.85)', fontSize: '14px', fontWeight: 500, margin: 0, textAlign: 'center' }}>
|
||||
누적 데이터가 부족하여 실제 시청자 데이터가 없습니다.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div className="audience-cards" style={!hasRealAudienceData && !showMockData ? { filter: 'blur(4px)', pointerEvents: 'none', userSelect: 'none' } : {}}>
|
||||
<AnimatedItem index={0} baseDelay={1100}>
|
||||
<div className="audience-card">
|
||||
<h3 className="audience-card-title">{t('dashboard.ageDistribution')}</h3>
|
||||
<AudienceBarChart data={audienceData.ageGroups} delay={1200} />
|
||||
</div>
|
||||
)}
|
||||
</AnimatedItem>
|
||||
<AnimatedItem index={1} baseDelay={1100}>
|
||||
<div className="audience-card">
|
||||
<h3 className="audience-card-title">{t('dashboard.genderDistribution')}</h3>
|
||||
<GenderChart male={audienceData.gender.male} female={audienceData.gender.female} delay={1300} />
|
||||
</div>
|
||||
</AnimatedItem>
|
||||
<AnimatedItem index={2} baseDelay={1100}>
|
||||
<div className="audience-card">
|
||||
<h3 className="audience-card-title">{t('dashboard.topRegions')}</h3>
|
||||
<AudienceBarChart data={audienceData.topRegions.map(r => ({ label: r.region, percentage: r.percentage }))} delay={1400} />
|
||||
</div>
|
||||
</AnimatedItem>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ textAlign: 'center', padding: '32px', opacity: 0.4 }}>
|
||||
<span>{t('dashboard.noData') || '이 기간에 데이터가 없습니다.'}</span>
|
||||
</div>
|
||||
)}
|
||||
{dashboardData && !isEmptyState && !hasRealAudienceData && (
|
||||
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}>
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px',
|
||||
background: 'rgba(0,0,0,0.45)',
|
||||
backdropFilter: 'blur(2px)',
|
||||
borderRadius: '12px',
|
||||
padding: '20px 28px',
|
||||
}}>
|
||||
<p style={{ color: 'rgba(255,255,255,0.85)', fontSize: '14px', fontWeight: 500, margin: 0, textAlign: 'center' }}>
|
||||
누적 데이터가 부족하여 실제 시청자 정보가 없습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
|
||||
{/* 개발자 모드 전용: mock 데이터 블러 해제 버튼 */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue