Files
dbx-main/app/static/erp.css
T
king 316e3fe8d1 feat(erp): ERP 대시보드 + 관리자 권한 시스템 도입
- 로그인: dbxcorp.co.kr 도메인 화이트리스트만 사용. ALLOWED_EMAILS 게이트 제거
- king@dbxcorp.co.kr 슈퍼관리자 자동 부여 (강등/삭제 불가)
- 신규 로그인 사용자는 JSON 저장소에 자동 등록 (기본 모듈 권한 0)
- 관리자 페이지(/admin): 역할/모듈별 토글로 직원 권한 수정
- 메인 대시보드를 ERP 시스템(monochromatic shadcn)으로 재설계
  · 모듈: CORM, Order, 개인경비(준비중), 휴가(준비중)
  · 권한 없는 모듈은 '권한 없음' 배지 + 클릭 불가
- DESIGN.md 토큰 기반 styles → app/static/erp.css
- docker-compose: dbx-main-data 볼륨 마운트 (DATA_DIR=/data)
  으로 권한 JSON 영구 보관

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 23:36:51 +09:00

596 lines
13 KiB
CSS

/* ════════════════════════════════════════════════════
ERP 대시보드 — DESIGN.md 기반 (Monochromatic / shadcn)
════════════════════════════════════════════════════ */
/* Geist 폰트 — CDN. 실패시 Inter / 시스템 폰트 fallback */
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600&family=Geist+Mono&family=Inter:wght@400;500;600&display=swap");
:root {
/* Colors */
--color-canvas-white: #ffffff;
--color-ghost-gray: #f2f2f2;
--color-subtle-ash: #e5e5e5;
--color-midtone-gray: #737373;
--color-rich-black: #0a0a0a;
--color-deep-black: #000000;
--color-callout-red: #c22b10;
--color-success-green:#10c22b;
/* Typography */
--font-geist: 'Geist', 'Inter', ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-geist-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo,
Monaco, Consolas, monospace;
--text-caption: 12px;
--text-body: 14px;
--text-heading: 18px;
--text-display: 48px;
--leading-body: 1.43;
--tracking-heading: -0.45px;
--tracking-display: -2.4px;
/* Spacing */
--sp-4: 4px; --sp-6: 6px; --sp-8: 8px; --sp-10: 10px;
--sp-12: 12px; --sp-16: 16px; --sp-20: 20px; --sp-24: 24px;
--sp-32: 32px; --sp-40: 40px; --sp-80: 80px;
/* Radii */
--r-pill: 9999px;
--r-badge: 26px;
--r-card: 14px;
--r-input: 10px;
--r-btn: 10px;
/* Shadows */
--shadow-card: oklab(0.145 -0.00000143796 0.00000340492 / 0.1) 0px 0px 0px 1px;
--shadow-focus: lab(100 0 0) 0px 0px 0px 2px;
}
/* ── 리셋 ── (auth pages 와 충돌 방지 위해 erp-shell 안에서만 적용) */
.erp-shell, .erp-shell * { box-sizing: border-box; }
.erp-shell { margin: 0; padding: 0; }
body.erp-body {
margin: 0;
font-family: var(--font-geist);
font-size: var(--text-body);
line-height: var(--leading-body);
color: var(--color-rich-black);
background: var(--color-canvas-white);
-webkit-font-smoothing: antialiased;
letter-spacing: 0;
}
.erp-body a { color: inherit; text-decoration: none; }
/* ── 상단 네비게이션 ── */
.erp-nav {
position: sticky;
top: 0;
z-index: 50;
background: var(--color-canvas-white);
border-bottom: 1px solid var(--color-subtle-ash);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--sp-24);
height: 56px;
}
.erp-nav-left {
display: flex;
align-items: center;
gap: var(--sp-16);
}
.erp-brand {
display: flex;
align-items: center;
gap: var(--sp-8);
font-weight: 600;
color: var(--color-deep-black);
font-size: var(--text-body);
letter-spacing: -0.2px;
}
.erp-brand-logo {
height: 36px;
width: auto;
object-fit: contain;
}
.erp-brand-divider {
width: 1px;
height: 20px;
background: var(--color-subtle-ash);
}
.erp-brand-system {
font-size: var(--text-body);
font-weight: 500;
color: var(--color-rich-black);
}
.erp-nav-right {
display: flex;
align-items: center;
gap: var(--sp-8);
}
.erp-user-chip {
display: inline-flex;
align-items: center;
gap: var(--sp-8);
padding: 4px var(--sp-12) 4px 4px;
border: 1px solid var(--color-subtle-ash);
border-radius: var(--r-pill);
background: var(--color-canvas-white);
font-size: var(--text-caption);
color: var(--color-rich-black);
line-height: 1;
}
.erp-user-avatar {
width: 28px;
height: 28px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.erp-user-avatar-fallback {
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--color-deep-black);
color: var(--color-canvas-white);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: var(--text-caption);
font-weight: 600;
}
.erp-user-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.erp-user-name {
font-weight: 500;
color: var(--color-rich-black);
font-size: var(--text-caption);
}
.erp-user-email {
color: var(--color-midtone-gray);
font-size: 11px;
}
/* ── 버튼 ── */
.erp-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--sp-6);
font-family: inherit;
font-size: var(--text-caption);
font-weight: 500;
line-height: 1;
border: 1px solid transparent;
border-radius: var(--r-btn);
cursor: pointer;
white-space: nowrap;
transition: background 120ms, border-color 120ms, color 120ms;
}
.erp-btn-primary {
background: var(--color-deep-black);
color: var(--color-canvas-white);
padding: 8px 16px;
}
.erp-btn-primary:hover {
background: var(--color-rich-black);
}
.erp-btn-ghost {
background: transparent;
color: var(--color-rich-black);
padding: 6px 12px;
border-radius: var(--r-pill);
}
.erp-btn-ghost:hover {
background: var(--color-ghost-gray);
}
.erp-btn-outline {
background: var(--color-canvas-white);
color: var(--color-rich-black);
border-color: var(--color-subtle-ash);
padding: 6px 12px;
}
.erp-btn-outline:hover {
background: var(--color-ghost-gray);
}
.erp-btn-danger {
background: transparent;
color: var(--color-callout-red);
border-color: var(--color-subtle-ash);
padding: 6px 12px;
}
.erp-btn-danger:hover {
border-color: var(--color-callout-red);
background: rgba(194, 43, 16, 0.04);
}
/* ── 본문 컨테이너 ── */
.erp-main {
max-width: 1200px;
margin: 0 auto;
padding: var(--sp-40) var(--sp-24);
}
.erp-greeting {
margin: 0 0 var(--sp-8);
font-size: 28px;
font-weight: 600;
color: var(--color-deep-black);
letter-spacing: -0.6px;
line-height: 1.2;
}
.erp-greeting-sub {
margin: 0 0 var(--sp-32);
color: var(--color-midtone-gray);
font-size: var(--text-body);
}
.erp-section-head {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: var(--sp-16);
}
.erp-section-title {
font-size: var(--text-heading);
font-weight: 500;
color: var(--color-deep-black);
letter-spacing: var(--tracking-heading);
line-height: 1.33;
}
.erp-section-meta {
font-size: var(--text-caption);
color: var(--color-midtone-gray);
}
/* ── 카드 그리드 ── */
.erp-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--sp-16);
}
.erp-card {
position: relative;
display: flex;
flex-direction: column;
gap: var(--sp-12);
background: var(--color-canvas-white);
border-radius: var(--r-card);
box-shadow: var(--shadow-card);
padding: var(--sp-20);
min-height: 180px;
transition: box-shadow 120ms, transform 120ms;
}
.erp-card[data-clickable="true"]:hover {
box-shadow: oklab(0.145 -0.00000143796 0.00000340492 / 0.18) 0 0 0 1px,
oklab(0.145 -0.00000143796 0.00000340492 / 0.06) 0 8px 24px;
}
.erp-card[data-clickable="false"] {
background: var(--color-ghost-gray);
}
.erp-card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--sp-8);
}
.erp-card-title {
font-size: var(--text-heading);
font-weight: 600;
color: var(--color-deep-black);
letter-spacing: var(--tracking-heading);
line-height: 1.2;
}
.erp-card-subtitle {
font-family: var(--font-geist-mono);
font-size: var(--text-caption);
color: var(--color-midtone-gray);
letter-spacing: 0;
}
.erp-card-desc {
font-size: var(--text-body);
color: var(--color-midtone-gray);
flex: 1;
}
.erp-card-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--sp-8);
margin-top: auto;
padding-top: var(--sp-12);
border-top: 1px solid var(--color-subtle-ash);
}
.erp-card-link {
font-size: var(--text-caption);
font-weight: 500;
color: var(--color-deep-black);
display: inline-flex;
align-items: center;
gap: var(--sp-4);
}
.erp-card-link svg {
transition: transform 120ms;
}
.erp-card[data-clickable="true"]:hover .erp-card-link svg {
transform: translateX(2px);
}
/* ── 배지 ── */
.erp-badge {
display: inline-flex;
align-items: center;
gap: var(--sp-6);
padding: 2px 8px;
font-size: 11px;
font-weight: 500;
border-radius: var(--r-badge);
line-height: 1.4;
white-space: nowrap;
}
.erp-badge-inverse {
background: var(--color-deep-black);
color: var(--color-canvas-white);
}
.erp-badge-neutral {
background: var(--color-ghost-gray);
color: var(--color-rich-black);
}
.erp-badge-outline {
background: transparent;
color: var(--color-rich-black);
border: 1px solid #a1a1a1;
}
.erp-badge-success { background: #e6f8e9; color: var(--color-success-green); border: 1px solid #c2efc8; }
.erp-badge-danger { background: #fbe9e6; color: var(--color-callout-red); border: 1px solid #efc6bf; }
.erp-badge-dot::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
/* ── 잠긴 카드 표시 ── */
.erp-locked-overlay {
position: absolute;
top: var(--sp-12);
right: var(--sp-12);
font-size: 11px;
color: var(--color-midtone-gray);
display: inline-flex;
align-items: center;
gap: 4px;
}
/* ── 관리자 페이지 ── */
.erp-admin-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--sp-12);
margin-bottom: var(--sp-16);
flex-wrap: wrap;
}
.erp-input {
font-family: inherit;
font-size: var(--text-body);
color: var(--color-rich-black);
background: var(--color-canvas-white);
border: 1px solid var(--color-subtle-ash);
border-radius: var(--r-input);
padding: 6px var(--sp-10);
min-width: 240px;
}
.erp-input:focus {
outline: none;
box-shadow: var(--shadow-focus);
border-color: var(--color-deep-black);
}
.erp-table-wrap {
border-radius: var(--r-card);
box-shadow: var(--shadow-card);
background: var(--color-canvas-white);
overflow: hidden;
}
.erp-table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-body);
}
.erp-table thead th {
text-align: left;
font-size: var(--text-caption);
font-weight: 500;
color: var(--color-midtone-gray);
background: var(--color-ghost-gray);
padding: var(--sp-10) var(--sp-12);
border-bottom: 1px solid var(--color-subtle-ash);
white-space: nowrap;
}
.erp-table tbody td {
padding: var(--sp-12);
border-bottom: 1px solid var(--color-subtle-ash);
vertical-align: middle;
}
.erp-table tbody tr:last-child td {
border-bottom: none;
}
.erp-table tbody tr[data-dirty="true"] {
background: #fffbeb;
}
.erp-user-cell {
display: flex;
align-items: center;
gap: var(--sp-10);
}
.erp-user-cell .erp-user-avatar,
.erp-user-cell .erp-user-avatar-fallback {
width: 32px;
height: 32px;
font-size: 13px;
}
.erp-user-cell-name {
font-weight: 500;
color: var(--color-rich-black);
line-height: 1.2;
}
.erp-user-cell-email {
font-size: 12px;
color: var(--color-midtone-gray);
line-height: 1.2;
margin-top: 2px;
}
/* shadcn-style 토글 (checkbox 기반) */
.erp-switch {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
}
.erp-switch input { opacity: 0; width: 0; height: 0; }
.erp-switch-slider {
position: absolute;
inset: 0;
background: var(--color-subtle-ash);
border-radius: var(--r-pill);
cursor: pointer;
transition: background 120ms;
}
.erp-switch-slider::before {
content: "";
position: absolute;
width: 16px; height: 16px;
left: 2px; top: 2px;
background: var(--color-canvas-white);
border-radius: 50%;
transition: transform 120ms;
box-shadow: 0 1px 2px rgba(0,0,0,.2);
}
.erp-switch input:checked + .erp-switch-slider { background: var(--color-deep-black); }
.erp-switch input:checked + .erp-switch-slider::before { transform: translateX(16px); }
.erp-switch input:disabled + .erp-switch-slider {
opacity: 0.5;
cursor: not-allowed;
}
/* shadcn-style select */
.erp-select {
font-family: inherit;
font-size: var(--text-body);
color: var(--color-rich-black);
background: var(--color-canvas-white);
border: 1px solid var(--color-subtle-ash);
border-radius: var(--r-input);
padding: 4px var(--sp-10);
cursor: pointer;
}
.erp-select:focus {
outline: none;
box-shadow: var(--shadow-focus);
border-color: var(--color-deep-black);
}
.erp-select:disabled {
background: var(--color-ghost-gray);
cursor: not-allowed;
color: var(--color-midtone-gray);
}
/* 토스트 */
.erp-toast {
position: fixed;
bottom: 24px;
right: 24px;
background: var(--color-deep-black);
color: var(--color-canvas-white);
padding: var(--sp-12) var(--sp-16);
border-radius: var(--r-input);
font-size: var(--text-body);
z-index: 1000;
opacity: 0;
transform: translateY(8px);
transition: opacity 200ms, transform 200ms;
pointer-events: none;
max-width: 360px;
}
.erp-toast[data-visible="true"] {
opacity: 1;
transform: translateY(0);
}
.erp-toast[data-type="error"] {
background: var(--color-callout-red);
}
/* 반응형 */
@media (max-width: 640px) {
.erp-nav { padding: 0 var(--sp-16); height: 52px; }
.erp-brand-system { display: none; }
.erp-user-info { display: none; }
.erp-main { padding: var(--sp-24) var(--sp-16); }
.erp-greeting { font-size: 22px; }
.erp-table thead { display: none; }
.erp-table tbody td { display: block; border: none; padding: 4px var(--sp-12); }
.erp-table tbody tr { display: block; padding: var(--sp-12) 0; border-bottom: 1px solid var(--color-subtle-ash); }
}