회사 관리 기능 확장 + 테넌트/비번 보안 하드닝
- 첫 로그인 비번 강제 변경 (RUN_082): FORCE_PASSWORD_CHANGE 컬럼, ForcePasswordChangeGuardFilter, /auth/change-password API + 페이지 - 테넌트 일관성 가드: TenantConsistencyGuardFilter 로 JWT.company_code ↔ 서브도메인 company_code 대조, CompanyResolver 가 (db_name, company_code) 동시 반환 - 회사 관리 확장 (RUN_083 audit log, RUN_084 lifecycle 컬럼): CompanyAdmin/Members/Templates/Lifecycle/AuditLog 서비스 + CompanyMgmtController + SuperAdminGuard - 회사 관리 UI: CompanyAccordionRow 탭화 + 모달 4종 (AdminInfo/Deactivate/Delete/RecopyTemplates) + AuditLogDrawer + csvExport - 프로비저닝 마법사: force_password_change 토글 반영 - 프론트 인증: storage 이벤트 멀티탭 동기화, 403 errorCode (PASSWORD_CHANGE_REQUIRED / CROSS_TENANT_REJECTED / TENANT_NOT_RESOLVED) 전역 리다이렉트 - 기타: StartupSchemaMigrator, OS별 도커 기동 스크립트, CLAUDE.md 트래킹 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -256,10 +256,9 @@ html:not(.dark) .v5-hdr{
|
||||
box-shadow:0 0 6px rgba(var(--v5-pink-rgb),.8);
|
||||
animation:v5-pdot 2s infinite;
|
||||
}
|
||||
/* Admin mode tint: when .v5-admin-mode, the mode-toggle glows cyan. */
|
||||
.v5-admin-mode .v5-hdr-icon.v5-mode-toggle{
|
||||
color:var(--v5-cyan);
|
||||
background:rgba(var(--v5-cyan-rgb),.10);
|
||||
color:var(--v5-primary);
|
||||
background:rgba(var(--v5-primary-rgb),.10);
|
||||
}
|
||||
|
||||
/* 대시보드 생성 버튼 (헤더, Light/Dark 토글 왼쪽) */
|
||||
@@ -302,9 +301,9 @@ html:not(.dark) .v5-hdr{
|
||||
.v5-admin-btn .ic-home{display:none;}
|
||||
.v5-admin-mode .v5-admin-btn .ic-gear{display:none;}
|
||||
.v5-admin-mode .v5-admin-btn .ic-home{display:block;}
|
||||
.v5-admin-mode .v5-admin-btn{color:var(--v5-cyan);background:rgba(var(--v5-cyan-rgb),.10);}
|
||||
.v5-admin-mode .v5-admin-btn:hover{color:var(--v5-cyan);background:rgba(var(--v5-cyan-rgb),.16);}
|
||||
.v5-admin-mode .v5-admin-btn .v5-admin-label{color:var(--v5-cyan);}
|
||||
.v5-admin-mode .v5-admin-btn{color:var(--v5-primary);background:rgba(var(--v5-primary-rgb),.10);}
|
||||
.v5-admin-mode .v5-admin-btn:hover{color:var(--v5-primary);background:rgba(var(--v5-primary-rgb),.16);}
|
||||
.v5-admin-mode .v5-admin-btn .v5-admin-label{color:var(--v5-primary);}
|
||||
|
||||
/* Avatar */
|
||||
.v5-avatar-w{position:relative;}
|
||||
@@ -376,10 +375,10 @@ html:not(.dark) .v5-hdr{
|
||||
|
||||
/* Admin badge — display:none 대신 opacity/transform 으로 hidden 해서 zoom-in/out 애니메이션 가능 */
|
||||
.v5-admin-badge{display:flex;align-items:center;gap:.4rem;padding:.2rem .6rem;border-radius:999px;
|
||||
background:linear-gradient(135deg,rgba(var(--v5-primary-rgb),.12),rgba(var(--v5-cyan-rgb),.08));
|
||||
background:linear-gradient(135deg,rgba(var(--v5-primary-rgb),.14),rgba(var(--v5-primary-rgb),.06));
|
||||
border:1px solid rgba(var(--v5-primary-rgb),.2);font-size:.58rem;font-weight:700;color:var(--v5-primary);
|
||||
opacity:0;transform:scale(0) rotate(-30deg);pointer-events:none;}
|
||||
.dark .v5-admin-badge{background:linear-gradient(135deg,rgba(var(--v5-primary-rgb),.12),rgba(var(--v5-cyan-rgb),.08));
|
||||
.dark .v5-admin-badge{background:linear-gradient(135deg,rgba(var(--v5-primary-rgb),.14),rgba(var(--v5-primary-rgb),.06));
|
||||
border-color:rgba(var(--v5-primary-rgb),.2);color:var(--v5-primary-light);}
|
||||
.v5-admin-mode .v5-admin-badge{opacity:1;transform:scale(1) rotate(0);pointer-events:auto;}
|
||||
/* badge zoom — 모드 진입 시 bouncy in, 이탈 시 quick out */
|
||||
@@ -392,14 +391,16 @@ html:not(.dark) .v5-hdr{
|
||||
@keyframes v5-badge-zoom-out{
|
||||
0%{opacity:1;transform:scale(1) rotate(0)}
|
||||
100%{opacity:0;transform:scale(0) rotate(30deg)}}
|
||||
.v5-admin-badge .badge-dot{width:6px;height:6px;border-radius:50%;background:var(--v5-cyan);
|
||||
box-shadow:0 0 8px var(--v5-cyan-glow);animation:v5-bdPulse 2s infinite;}
|
||||
@keyframes v5-bdPulse{0%,100%{box-shadow:0 0 4px var(--v5-cyan-glow)}50%{box-shadow:0 0 12px var(--v5-cyan-glow)}}
|
||||
.v5-admin-badge .badge-dot{width:6px;height:6px;border-radius:50%;background:var(--v5-primary);
|
||||
box-shadow:0 0 8px var(--v5-primary-glow);animation:v5-bdPulse 2s infinite;}
|
||||
@keyframes v5-bdPulse{0%,100%{box-shadow:0 0 4px var(--v5-primary-glow)}50%{box-shadow:0 0 12px var(--v5-primary-glow)}}
|
||||
|
||||
/* ===== SOLID TABS ===== */
|
||||
.v5-tabs{height:36px;display:flex;align-items:stretch;padding:0 .5rem;gap:1px;overflow-x:auto;
|
||||
background:var(--v5-surface-solid);
|
||||
border-bottom:1px solid var(--v5-border);position:relative;z-index:15;flex-shrink:0;}
|
||||
border-bottom:1px solid var(--v5-border);position:relative;z-index:15;flex-shrink:0;
|
||||
scrollbar-width:none;-ms-overflow-style:none;}
|
||||
.v5-tabs::-webkit-scrollbar{display:none;}
|
||||
.v5-tab{display:flex;align-items:center;gap:.4rem;padding:0 .85rem;font-size:.7rem;font-weight:500;
|
||||
color:var(--v5-text-muted);cursor:pointer;border-bottom:2px solid transparent;white-space:nowrap;transition:all .25s;}
|
||||
.v5-tab:hover{color:var(--v5-text-sec);background:var(--v5-surface-hover);}
|
||||
@@ -514,9 +515,8 @@ html:not(.dark) .v5-side{
|
||||
/* Content area stretches smoothly with sidebar */
|
||||
.v5-body .v5-content{transition:all .5s cubic-bezier(.4,0,.2,1);}
|
||||
|
||||
.v5-side.collapsed{width:56px;padding:.85rem .4rem;overflow:visible;z-index:30;
|
||||
border-right-color:var(--v5-primary);box-shadow:var(--v5-glow-sm);}
|
||||
.v5-side.collapsed .v5-side-toggle{box-shadow:var(--v5-glow-sm);border-color:var(--v5-primary);color:var(--v5-primary);}
|
||||
.v5-side.collapsed{width:56px;padding:.85rem .4rem;overflow:visible;z-index:30;}
|
||||
.v5-side.collapsed .v5-side-toggle{color:var(--v5-primary);}
|
||||
|
||||
/* Collapsed menu items — center icon */
|
||||
.v5-side.collapsed .v5-si{justify-content:center;padding:.55rem;border-radius:10px;gap:0;position:relative;
|
||||
@@ -529,6 +529,18 @@ html:not(.dark) .v5-side{
|
||||
.v5-side.collapsed .v5-si:nth-child(6){animation-delay:.28s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(7){animation-delay:.32s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(8){animation-delay:.36s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(9){animation-delay:.40s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(10){animation-delay:.44s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(11){animation-delay:.48s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(12){animation-delay:.52s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(13){animation-delay:.56s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(14){animation-delay:.60s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(15){animation-delay:.64s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(16){animation-delay:.68s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(17){animation-delay:.72s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(18){animation-delay:.76s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(19){animation-delay:.80s;}
|
||||
.v5-side.collapsed .v5-si:nth-child(20){animation-delay:.84s;}
|
||||
@keyframes v5-iconPop{from{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}
|
||||
|
||||
/* Hide text when collapsed */
|
||||
@@ -564,6 +576,18 @@ html:not(.dark) .v5-side{
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(6){animation-delay:.2s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(7){animation-delay:.23s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(8){animation-delay:.26s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(9){animation-delay:.29s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(10){animation-delay:.32s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(11){animation-delay:.35s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(12){animation-delay:.38s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(13){animation-delay:.41s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(14){animation-delay:.44s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(15){animation-delay:.47s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(16){animation-delay:.50s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(17){animation-delay:.53s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(18){animation-delay:.56s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(19){animation-delay:.59s;}
|
||||
.v5-side:not(.collapsed) .v5-si:nth-child(20){animation-delay:.62s;}
|
||||
@keyframes v5-menuSlideIn{from{opacity:0;transform:translateX(-12px)}to{opacity:1;transform:none}}
|
||||
.v5-side:not(.collapsed) .v5-side-sec{opacity:1;
|
||||
transition:opacity .35s .1s,height .35s .05s,padding .35s .05s;}
|
||||
@@ -650,6 +674,19 @@ html:not(.dark) .v5-side{
|
||||
.v5-side-flyout .fly-item:nth-child(5){animation-delay:.12s;}
|
||||
.v5-side-flyout .fly-item:nth-child(6){animation-delay:.15s;}
|
||||
.v5-side-flyout .fly-item:nth-child(7){animation-delay:.18s;}
|
||||
.v5-side-flyout .fly-item:nth-child(8){animation-delay:.21s;}
|
||||
.v5-side-flyout .fly-item:nth-child(9){animation-delay:.24s;}
|
||||
.v5-side-flyout .fly-item:nth-child(10){animation-delay:.27s;}
|
||||
.v5-side-flyout .fly-item:nth-child(11){animation-delay:.30s;}
|
||||
.v5-side-flyout .fly-item:nth-child(12){animation-delay:.33s;}
|
||||
.v5-side-flyout .fly-item:nth-child(13){animation-delay:.36s;}
|
||||
.v5-side-flyout .fly-item:nth-child(14){animation-delay:.39s;}
|
||||
.v5-side-flyout .fly-item:nth-child(15){animation-delay:.42s;}
|
||||
.v5-side-flyout .fly-item:nth-child(16){animation-delay:.45s;}
|
||||
.v5-side-flyout .fly-item:nth-child(17){animation-delay:.48s;}
|
||||
.v5-side-flyout .fly-item:nth-child(18){animation-delay:.51s;}
|
||||
.v5-side-flyout .fly-item:nth-child(19){animation-delay:.54s;}
|
||||
.v5-side-flyout .fly-item:nth-child(20){animation-delay:.57s;}
|
||||
@keyframes v5-flyItemIn{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:none}}
|
||||
.v5-side-flyout .fly-title{font-size:.58rem;font-weight:700;color:var(--v5-text-muted);
|
||||
text-transform:uppercase;letter-spacing:.08em;padding:.3rem .6rem .45rem;}
|
||||
@@ -663,12 +700,12 @@ html:not(.dark) .v5-side{
|
||||
.v5-side-flyout .fly-item.on .ic{opacity:1;}
|
||||
|
||||
/* Admin sidebar accent */
|
||||
.v5-admin-side .v5-si.on{background:linear-gradient(135deg,rgba(var(--v5-cyan-rgb),.12),rgba(var(--v5-cyan-rgb),.05));
|
||||
color:var(--v5-cyan);border-color:rgba(var(--v5-cyan-rgb),.2);}
|
||||
.v5-admin-side .v5-si.on{background:linear-gradient(135deg,rgba(var(--v5-primary-rgb),.12),rgba(var(--v5-primary-rgb),.05));
|
||||
color:var(--v5-primary);border-color:rgba(var(--v5-primary-rgb),.2);}
|
||||
.v5-admin-side .v5-si.on .ic{opacity:1;}
|
||||
.v5-admin-side .v5-si::before{background:var(--v5-cyan);}
|
||||
.dark .v5-admin-side .v5-si.on{background:linear-gradient(135deg,rgba(var(--v5-cyan-rgb),.12),rgba(var(--v5-cyan-rgb),.05));
|
||||
border-color:rgba(var(--v5-cyan-rgb),.15);}
|
||||
.v5-admin-side .v5-si::before{background:var(--v5-primary);}
|
||||
.dark .v5-admin-side .v5-si.on{background:linear-gradient(135deg,rgba(var(--v5-primary-rgb),.12),rgba(var(--v5-primary-rgb),.05));
|
||||
border-color:rgba(var(--v5-primary-rgb),.15);}
|
||||
|
||||
/* ===== MODE TRANSITION ===== */
|
||||
.v5-mode-fade{position:fixed;inset:0;z-index:9998;pointer-events:none;opacity:0;
|
||||
@@ -699,7 +736,7 @@ html:not(.dark) .v5-side{
|
||||
.v5-hdr-glow{position:absolute;bottom:-1px;left:0;right:0;height:1px;
|
||||
background:linear-gradient(90deg,transparent,var(--v5-primary),transparent);
|
||||
opacity:0;pointer-events:none;}
|
||||
.v5-admin-mode .v5-hdr-glow{background:linear-gradient(90deg,transparent,var(--v5-cyan),transparent);}
|
||||
.v5-admin-mode .v5-hdr-glow{background:linear-gradient(90deg,transparent,var(--v5-primary),transparent);}
|
||||
.v5-hdr-glow.mode-flash{animation:v5-mode-hdr-flash 1.4s cubic-bezier(.16,1,.3,1) forwards;}
|
||||
@keyframes v5-mode-hdr-flash{
|
||||
0%{opacity:0;height:1px;filter:blur(0)}
|
||||
@@ -717,15 +754,13 @@ html:not(.dark) .v5-side{
|
||||
from{opacity:0;transform:translateY(8px) scale(.9);filter:blur(4px)}
|
||||
to{opacity:1;transform:translateY(0) scale(1);filter:blur(0)}}
|
||||
|
||||
/* ===== MODE TRANSITION — toggle button burst (디자인시스템 mode-burst 포팅) =====
|
||||
JS 가 .v5-mode-burst 컨테이너를 fixed 위치(클릭점)에 append.
|
||||
기본 = primary(보라, → 사용자 모드), .admin = cyan(시안, → 관리자 모드). */
|
||||
/* ===== MODE TRANSITION — toggle button burst ===== */
|
||||
.v5-mode-burst{
|
||||
position:fixed;width:0;height:0;
|
||||
pointer-events:none;z-index:9998;
|
||||
--burst-rgb:var(--v5-primary-rgb);
|
||||
}
|
||||
.v5-mode-burst.admin{--burst-rgb:var(--v5-cyan-rgb);}
|
||||
.v5-mode-burst.admin{--burst-rgb:var(--v5-primary-rgb);}
|
||||
|
||||
/* Center expanding ring */
|
||||
.v5-mode-burst .burst-ring{
|
||||
@@ -768,11 +803,9 @@ html:not(.dark) .v5-side{
|
||||
content:'';position:absolute;inset:0;
|
||||
background:linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(var(--v5-cyan-rgb),0) 15%,
|
||||
rgba(var(--v5-cyan-rgb),.9) 40%,
|
||||
rgba(var(--v5-primary-rgb),1) 50%,
|
||||
rgba(var(--v5-pink-rgb),.9) 60%,
|
||||
rgba(var(--v5-cyan-rgb),0) 85%,
|
||||
rgba(var(--v5-primary-rgb),0) 15%,
|
||||
rgba(var(--v5-primary-rgb),.95) 50%,
|
||||
rgba(var(--v5-primary-rgb),0) 85%,
|
||||
transparent 100%);
|
||||
filter:blur(.5px);
|
||||
transform:translateX(-100%);
|
||||
|
||||
Reference in New Issue
Block a user