대시보드

💻 HTML 2025-12-23
수정
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Enzycept Plus Solution - Sales Dashboard</title>

    <script>
        (function() {
            const savedTheme = localStorage.getItem('theme') || 'dark';
            document.documentElement.setAttribute('data-theme', savedTheme);
        })();
    </script>

    <script src="https://cdn.plot.ly/plotly-3.3.0.min.js"></script>

    <style>
        /* ===== CSS 변수 (저명도 다크모드 적용) ===== */
        :root {
            --bg-color: #ffffff;
            --text-color: #212529;
            --header-bg: #f8f9fa;
            --border-color: #dee2e6;
            --card-bg: #ffffff;
            --info-header: #4FC3F7;
        }

        [data-theme="dark"] {
            --bg-color: #0d1117;
            --text-color: #b0b8c1; /* 명도를 낮춘 부드러운 텍스트 */
            --header-bg: #161b22;
            --border-color: #30363d;
            --card-bg: #1c2128;
            --info-header: #4FC3F7;
        }

        /* ===== 레이아웃 최적화 (넘침 방지) ===== */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box; /* 패딩이 너비에 포함되도록 설정 */
        }

        body {
            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            transition: background-color 0.2s ease, color 0.2s ease;
            overflow-x: hidden; /* 가로 스크롤 방지 */
        }

        .header {
            background-color: var(--header-bg);
            padding: 15px 25px;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .theme-toggle {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            color: var(--text-color);
            cursor: pointer;
            padding: 8px 16px;
            border-radius: 20px;
            font-size: 0.85rem;
            transition: all 0.2s;
        }

        .container {
            width: 100%;
            max-width: 100%; /* Toledo 사이드바 환경에 맞게 유연하게 설정 */
            margin: 0 auto;
            padding: 20px;
        }

        .info-box {
            background-color: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 18px;
            margin-bottom: 20px;
        }

        .info-box h3 {
            margin-bottom: 6px;
            color: var(--info-header);
        }

        /* 차트 컨테이너 넘침 방지 설정 */
        #dashboard-plot {
            width: 100%;
            max-width: 100%;
            background-color: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 10px;
            overflow: hidden; /* 내부 요소가 튀어나오지 않게 함 */
        }

        kbd {
            background: var(--header-bg);
            border: 1px solid var(--border-color);
            padding: 2px 5px;
            border-radius: 3px;
            font-size: 0.75rem;
        }
    </style>
</head>
<body>

    <div class="header">
        <h1>📊 Sales Analysis Dashboard</h1>
        <button class="theme-toggle" onclick="toggleTheme()" id="themeBtn">🌙 Dark Mode</button>
    </div>

    <div class="container">
        <div class="info-box">
            <h3>Dashboard Overview</h3>
            <p>Enzycept Plus Solution (2023-2025). <kbd>Ctrl+Shift+D</kbd>로 테마 전환 가능.</p>
        </div>

        <div id="dashboard-plot"></div>
    </div>

    <script type="text/javascript">
        // 데이터 정의
        const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
        const sales2023 = [3.90, 3.37, 4.37, 2.14, 2.62, 3.29, 6.65, 1.56, 5.23, 3.85, 3.29, 5.12];
        const sales2024 = [3.56, 6.10, 1.97, 5.03, 5.48, 3.43, 6.66, 4.08, 4.21, 2.96, 6.96, 9.89];
        const sales2025 = [13.37, 6.42, 9.16, 12.96, 5.08, 10.75, 6.66, 21.22, 8.83, 16.98, 7.93, 3.69];

        const qty2023 = [294, 234, 307, 140, 188, 225, 485, 99, 362, 315, 240, 357];
        const qty2024 = [264, 440, 134, 366, 384, 260, 419, 287, 257, 144, 464, 1019];
        const qty2025 = [1169, 456, 831, 1120, 398, 970, 499, 2143, 617, 1670, 567, 216];

        function getTraces(isDark) {
            const textColor = isDark ? '#b0b8c1' : '#212529';
            const tableBg = isDark ? '#1c2128' : '#ffffff';
            const tableLine = isDark ? '#30363d' : '#dee2e6';

            return [
                { x: months, y: sales2023, name: '23 Sales', type: 'bar', marker: {color: '#4FC3F7'}, xaxis: 'x', yaxis: 'y' },
                { x: months, y: sales2024, name: '24 Sales', type: 'bar', marker: {color: '#66BB6A'}, xaxis: 'x', yaxis: 'y' },
                { x: months, y: sales2025, name: '25 Sales', type: 'bar', marker: {color: '#FF7043'}, xaxis: 'x', yaxis: 'y' },

                { x: months, y: qty2023, name: '23 Qty', type: 'bar', marker: {color: '#4FC3F7', opacity: 0.6}, xaxis: 'x2', yaxis: 'y2', showlegend: false },
                { x: months, y: qty2024, name: '24 Qty', type: 'bar', marker: {color: '#66BB6A', opacity: 0.6}, xaxis: 'x2', yaxis: 'y2', showlegend: false },
                { x: months, y: qty2025, name: '25 Qty', type: 'bar', marker: {color: '#FF7043', opacity: 0.6}, xaxis: 'x2', yaxis: 'y2', showlegend: false },

                { x: ['2023', '2024', '2025'], y: [45.4, 60.3, 123.0], type: 'bar', marker: {color: ['#4FC3F7', '#66BB6A', '#FF7043']}, xaxis: 'x3', yaxis: 'y3', showlegend: false },

                {
                    type: 'table',
                    domain: { x: [0.55, 1.0], y: [0, 0.44] },
                    header: {
                        values: [["<b>Metric</b>"], ["<b>2023</b>"], ["<b>2024</b>"], ["<b>2025</b>"]],
                        align: "center", fill: {color: isDark ? '#30363d' : '#4FC3F7'},
                        font: {color: "white", size: 12}, line: {color: tableLine}
                    },
                    cells: {
                        values: [["Sales", "Qty", "Growth"], ["45.4M", "3,246", "-"], ["60.3M", "4,438", "+33%"], ["123.0M", "10,656", "+104%"]],
                        align: ["left", "right"], fill: {color: tableBg},
                        font: {color: textColor, size: 11}, line: {color: tableLine}, height: 28
                    }
                }
            ];
        }

        function applyTheme(theme) {
            const isDark = (theme === 'dark');
            document.documentElement.setAttribute('data-theme', theme);
            localStorage.setItem('theme', theme);
            document.getElementById('themeBtn').textContent = isDark ? '☀️ Light Mode' : '🌙 Dark Mode';

            const layout = {
                height: 900,
                autosize: true, // 컨테이너 크기에 맞춤
                paper_bgcolor: isDark ? '#1c2128' : '#ffffff',
                plot_bgcolor: isDark ? '#1c2128' : '#f8f9fa',
                font: { color: isDark ? '#b0b8c1' : '#212529', family: 'Segoe UI' },
                grid: { rows: 2, columns: 2, pattern: 'independent' },
                xaxis: { title: 'Sales Trend', gridcolor: isDark ? '#30363d' : '#dee2e6' },
                yaxis: { title: 'M KRW', gridcolor: isDark ? '#30363d' : '#dee2e6' },
                xaxis2: { title: 'Quantity Trend', gridcolor: isDark ? '#30363d' : '#dee2e6' },
                yaxis2: { title: 'ea', gridcolor: isDark ? '#30363d' : '#dee2e6' },
                xaxis3: { title: 'Annual Comparison', gridcolor: isDark ? '#30363d' : '#dee2e6' },
                yaxis3: { title: 'M KRW', gridcolor: isDark ? '#30363d' : '#dee2e6' },
                margin: { t: 50, b: 80, l: 50, r: 20 },
                legend: { orientation: 'h', x: 0.5, xanchor: 'center', y: -0.1 }
            };

            Plotly.react('dashboard-plot', getTraces(isDark), layout, {responsive: true, displayModeBar: false});
        }

        function toggleTheme() {
            const current = document.documentElement.getAttribute('data-theme');
            applyTheme(current === 'light' ? 'dark' : 'light');
        }

        document.addEventListener('DOMContentLoaded', () => {
            applyTheme(localStorage.getItem('theme') || 'dark');
        });

        // 창 크기가 변경될 때 차트 크기 재조정
        window.addEventListener('resize', () => {
            Plotly.Plots.resize('dashboard-plot');
        });

        document.addEventListener('keydown', (e) => {
            if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'D') {
                e.preventDefault();
                toggleTheme();
            }
        });
    </script>
</body>
</html>
상세 정보
생성일: 2025-12-23
수정일: 2025-12-28
이 아이템이 링크하는 문서

링크된 문서가 없습니다.

액션
수정
공유 & 관리
복제
목록으로 메인으로
마지막 수정: 2025-12-28 11:32
이미지 URL 수정됨