design: 기업용 UI 리디자인 및 로컬 실행 스크립트 추가

- 로그인/접근거부 페이지: 좌우 분할 레이아웃(다크 네이비 브랜드 패널 + 흰색 폼),
  실제 Google G 컬러 버튼, 자물쇠·안내 아이콘 적용
- 메인 페이지: 다크 네이비 헤더, 웰컴 히어로 배너(오늘 날짜 JS 자동 표시),
  SVG 아이콘이 있는 업무 카드 그리드, 호버 시 상단 블루 액센트 라인
- styles.css: 코퍼레이트 네이비/블루 색상 토큰 체계로 전면 재작성,
  Pretendard 폰트 유지, 모바일 반응형 포함
- run-local.bat / run-local.ps1: 더블클릭 또는 인수(stop/logs/ps/restart)로
  로컬 Docker 컨테이너를 간편하게 관리하는 스크립트 추가

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 23:38:32 +09:00
parent c93e2be0a7
commit 60a1426a23
6 changed files with 918 additions and 176 deletions
+73 -10
View File
@@ -3,17 +3,80 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>접속 불가</title>
<title>접속 불가 — DBX 업무 포털</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body class="auth-page">
<main class="auth-shell">
<section class="auth-panel">
<p class="eyebrow">Access denied</p>
<h1>접속할 수 없습니다</h1>
<p class="lead">{{ reason }}</p>
<a class="primary-button" href="/login">다른 Google 계정으로 로그인</a>
</section>
</main>
<body>
<div class="auth-layout">
<!-- 좌측: 브랜드 패널 -->
<aside class="brand-panel">
<div class="brand-logo">
<div class="brand-logo-mark">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
</svg>
</div>
<span class="brand-name">DBX Corp</span>
</div>
<div class="brand-body">
<p class="brand-tag">Internal Portal</p>
<h1 class="brand-headline">업무 포털<br>시스템</h1>
<p class="brand-desc">
DBX Corp 임직원 전용 업무 통합 포털입니다.
</p>
<div class="brand-divider"></div>
</div>
<p class="brand-footer">© 2026 DBX Corp. All rights reserved.</p>
</aside>
<!-- 우측: 오류 패널 -->
<main class="form-panel">
<div class="form-box">
<div class="form-error-badge">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
접속 불가
</div>
<h2 class="form-title">접속할 수 없습니다</h2>
<p class="form-subtitle" style="margin-bottom: 32px;">
{{ reason }}
</p>
<a class="btn-primary" href="/login">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/>
<polyline points="10 17 15 12 10 7"/>
<line x1="15" y1="12" x2="3" y2="12"/>
</svg>
다른 Google 계정으로 로그인
</a>
<div class="form-note" style="margin-top: 20px;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
</svg>
<span>
접속 권한이 필요하시면 시스템 관리자에게 문의하세요.
</span>
</div>
</div>
</main>
</div>
</body>
</html>
+69 -11
View File
@@ -3,18 +3,76 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>DBX 메인 페이지 로그인</title>
<title>로그인 — DBX 업무 포털</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body class="auth-page">
<main class="auth-shell">
<section class="auth-panel">
<p class="eyebrow">DBX Workspace</p>
<h1>메인 페이지</h1>
<p class="lead">회사 Google Workspace 계정으로 로그인하세요.</p>
<a class="primary-button" href="/login">Google 계정으로 로그인</a>
<p class="note">허용된 계정만 접속할 수 있습니다.</p>
</section>
</main>
<body>
<div class="auth-layout">
<!-- 좌측: 브랜드 패널 -->
<aside class="brand-panel">
<div class="brand-logo">
<div class="brand-logo-mark">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
</svg>
</div>
<span class="brand-name">DBX Corp</span>
</div>
<div class="brand-body">
<p class="brand-tag">Internal Portal</p>
<h1 class="brand-headline">업무 포털<br>시스템</h1>
<p class="brand-desc">
DBX Corp 임직원 전용 업무 통합 포털입니다.<br>
회사 Google Workspace 계정으로 인증 후 이용하실 수 있습니다.
</p>
<div class="brand-divider"></div>
</div>
<p class="brand-footer">© 2026 DBX Corp. All rights reserved.</p>
</aside>
<!-- 우측: 로그인 폼 -->
<main class="form-panel">
<div class="form-box">
<p class="form-eyebrow">DBX Corp</p>
<h2 class="form-title">업무 포털 로그인</h2>
<p class="form-subtitle">
회사 Google Workspace 계정으로 로그인하세요.<br>
허용된 계정만 접속할 수 있습니다.
</p>
<a class="btn-google" href="/login">
<!-- Google G 아이콘 -->
<svg width="20" height="20" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.18 1.48-4.97 2.31-8.16 2.31-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
</svg>
Google 계정으로 로그인
</a>
<div class="form-note">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
<span>
<strong style="color: var(--text);">dbxcorp.co.kr</strong> 도메인 계정이며
접속 허용 목록에 등록된 임직원만 이용할 수 있습니다.
</span>
</div>
</div>
</main>
</div>
</body>
</html>
+124 -21
View File
@@ -3,34 +3,137 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>DBX 메인 페이지</title>
<title>업무 포털 — DBX Corp</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<header class="topbar">
<div>
<p class="eyebrow">DBX Workspace</p>
<h1>업무 메뉴</h1>
</div>
<div class="account">
{% if user.picture %}
<img src="{{ user.picture }}" alt="" />
{% endif %}
<div>
<strong>{{ user.name }}</strong>
<span>{{ user.email }}</span>
<!-- ── 헤더 ── -->
<header class="header">
<div class="header-brand">
<div class="header-logo-mark">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
</svg>
</div>
<a class="ghost-button" href="/logout">로그아웃</a>
<div class="header-brand-text">
<span class="header-company">DBX Corp</span>
<span class="header-portal">업무 포털</span>
</div>
</div>
<div class="header-user">
<div class="header-user-info">
<span class="header-user-name">{{ user.name }}</span>
<span class="header-user-email">{{ user.email }}</span>
</div>
{% if user.picture %}
<img class="header-avatar" src="{{ user.picture }}" alt="{{ user.name }}" />
{% else %}
<div class="header-avatar-placeholder">
{{ user.name[0] | upper }}
</div>
{% endif %}
<a class="btn-logout" href="/logout">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
<polyline points="16 17 21 12 16 7"/>
<line x1="21" y1="12" x2="9" y2="12"/>
</svg>
<span>로그아웃</span>
</a>
</div>
</header>
<main class="menu-grid">
{% for item in menu_items %}
<a class="menu-card" href="{{ item.url }}">
<span>{{ item.title }}</span>
<small>{{ item.description }}</small>
</a>
{% endfor %}
<!-- ── 히어로 배너 ── -->
<section class="hero">
<p class="hero-greeting">안녕하세요, {{ user.name }}님 👋</p>
<p class="hero-sub">오늘도 좋은 하루 되세요.</p>
<p class="hero-date" id="today-date"></p>
</section>
<!-- ── 메뉴 콘텐츠 ── -->
<main class="content">
<div class="section-header">
<h2 class="section-title">업무 바로가기</h2>
<span class="section-count">{{ menu_items | length }}</span>
</div>
<div class="card-grid">
{% for item in menu_items %}
<a class="menu-card" href="{{ item.url }}">
<div class="card-icon">
{% if loop.index == 1 %}
<!-- 클립보드 / 발주 아이콘 -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
<line x1="8" y1="13" x2="16" y2="13"/>
<line x1="8" y1="17" x2="14" y2="17"/>
</svg>
{% elif loop.index == 2 %}
<!-- 리스트 / 주문 아이콘 -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="8" y1="6" x2="21" y2="6"/>
<line x1="8" y1="12" x2="21" y2="12"/>
<line x1="8" y1="18" x2="21" y2="18"/>
<line x1="3" y1="6" x2="3.01" y2="6"/>
<line x1="3" y1="12" x2="3.01" y2="12"/>
<line x1="3" y1="18" x2="3.01" y2="18"/>
</svg>
{% elif loop.index == 3 %}
<!-- 차트 아이콘 -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="20" x2="18" y2="10"/>
<line x1="12" y1="20" x2="12" y2="4"/>
<line x1="6" y1="20" x2="6" y2="14"/>
<line x1="2" y1="20" x2="22" y2="20"/>
</svg>
{% else %}
<!-- 기본 링크 아이콘 -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="16"/>
<line x1="8" y1="12" x2="16" y2="12"/>
</svg>
{% endif %}
</div>
<div class="card-body">
<p class="card-title">{{ item.title }}</p>
<p class="card-desc">{{ item.description }}</p>
</div>
<div class="card-footer">
<span class="card-link">
바로가기
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/>
<polyline points="12 5 19 12 12 19"/>
</svg>
</span>
</div>
</a>
{% endfor %}
</div>
</main>
<script>
// 오늘 날짜 표시
const d = new Date();
const days = ["일요일","월요일","화요일","수요일","목요일","금요일","토요일"];
document.getElementById("today-date").textContent =
d.getFullYear() + "년 " + (d.getMonth()+1) + "월 " + d.getDate() + "일 " + days[d.getDay()];
</script>
</body>
</html>