Files
jongryangje/app/Views/admin/layout.php
taekyoungc 4d9343e980 feat: GBLS 리브랜딩 + 매뉴얼 보강 + 워크스페이스/코드관리 UX 개선
리브랜딩
- 서비스명 "종량제 시스템" → "GBLS", 헤더 로고에 풀네임(Garbage Bag Logistics System) 병기
  (gov-portal·공통 브랜드·로그인/welcome 셸·타이틀·푸터 전반)

매뉴얼
- 신규 페이지 [로그인·회원가입·계정](01_account.md): 가입 항목·관리자 승인·2차 인증 흐름
- [화면 구성·워크스페이스·단축키]에 계정 전환 시 탭 초기화 안내 추가

워크스페이스(탭)
- 탭 전환 시 좌측 사이드바 강조 동기화(메뉴 없는 화면은 강조 해제, 경로 폴백 매칭)
- 소메뉴 좌측 아이콘(▸/·) 전부 제거 — 활성 메뉴는 배경 강조로만 구분
- 탭을 사용자(mb_idx)별로 격리: 다른 아이디 로그인 시 이전 탭 복원 안 함
- 사이드바 FAQ 링크 제거(자주 묻는 질문은 매뉴얼에 통합)

기본 코드관리 화면
- 업무현황 카드 스타일로 재디자인(가벼운 표·상태/범위 pill·단일 구분선)
- render()에 $bare 옵션 추가 → 이미 카드형인 화면은 바깥 래퍼 생략

기타
- .claude/settings.local.json(개인 권한 설정) .gitignore 추가
- e2e: 워크스페이스(동기화·계정격리) + 매뉴얼(계정·단축키·검색) 케이스 보강

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 14:43:24 +09:00

165 lines
7.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* 관리자 공통 레이아웃 — gov-portal 디자인 적용판.
* 헤더 + 관리자 대메뉴(클릭) + 좌측 사이드바(소메뉴) + 본문($content).
*
* @var string $title
* @var string $content
*/
helper('admin');
$mbLevel = (int) session()->get('mb_level');
$isSuperAdmin = \Config\Roles::isSuperAdminEquivalent($mbLevel);
$mbName = (string) (session()->get('mb_name') ?? '담당자');
$levelName = config(\Config\Roles::class)->getLevelName($mbLevel);
$effectiveLgIdx = admin_effective_lg_idx();
$effectiveLgName = '';
if ($effectiveLgIdx) {
$lgRow = model(\App\Models\LocalGovernmentModel::class)->find($effectiveLgIdx);
$effectiveLgName = $lgRow ? (string) $lgRow->lg_name : '';
}
$adminTree = function_exists('get_admin_nav_tree') ? get_admin_nav_tree() : [];
$gov = gov_portal_nav_context(false, $adminTree);
// 관리자 메뉴가 비어 있으면(지자체 미선택 등) 핵심 항목 폴백 노출
$navItems = $gov['navItems'];
if ($navItems === []) {
$mk = static fn (string $name, string $path): array => [
'idx' => 0, 'name' => $name, 'href' => $path, 'url' => base_url($path),
];
$navItems = [
['idx' => 0, 'name' => '대시보드', 'href' => 'admin', 'url' => base_url('admin'), 'children' => [], 'hasChildren' => false],
['idx' => 0, 'name' => '회원·접근', 'href' => '', 'url' => '', 'hasChildren' => true, 'children' => [
$mk('회원 관리', 'admin/users'), $mk('로그인 이력', 'admin/access/login-history'), $mk('승인 대기', 'admin/access/approvals'),
]],
['idx' => 0, 'name' => '시스템', 'href' => '', 'url' => '', 'hasChildren' => true, 'children' => [
$mk('역할', 'admin/roles'), $mk('메뉴', 'admin/menus'),
]],
];
if ($isSuperAdmin) {
$navItems[] = ['idx' => 0, 'name' => '지자체', 'href' => '', 'url' => '', 'hasChildren' => true, 'children' => [
$mk('지자체 전환', 'admin/select-local-government'), $mk('지자체 관리', 'admin/local-governments'),
]];
}
}
$navJson = json_encode($navItems, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS);
$navPartial = [
'govNavItems' => $navItems,
'govNavJson' => $navJson,
'govActiveParentIdx' => $gov['activeParentIdx'],
'govCurrentPath' => $gov['currentPath'],
'govDashboardAliases' => $gov['dashboardAliases'],
'govActiveChildHref' => $gov['currentPath'],
];
?>
<!DOCTYPE html>
<html lang="ko" class="gov-portal-html">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title><?= esc($title ?? '관리자') ?> - GBLS</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css"/>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { sans: ['Pretendard', '"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
colors: {
'system-header': '#ffffff', 'title-bar': '#1a2b4b', 'control-panel': '#f8f9fa',
'btn-search': '#243a5e', 'btn-excel-border': '#28a745', 'btn-excel-text': '#28a745',
'btn-print-border': '#ced4da', 'btn-exit': '#d9534f',
},
fontSize: { 'xxs': '0.65rem' }
}
}
}
</script>
<style>
<?php include __DIR__ . '/../home/_dashboard_gov_portal_brand_css.php'; ?>
<?php include __DIR__ . '/../home/_dashboard_gov_portal_topnav_css.php'; ?>
<?php include __DIR__ . '/../home/_dashboard_gov_portal_chrome_css.php'; ?>
.data-table { width: 100%; border-collapse: collapse; font-family: 'Pretendard', 'Malgun Gothic', 'Noto Sans KR', sans-serif; }
.data-table th, .data-table td { border: 1px solid #ccc; padding: 4px 8px; white-space: nowrap; font-size: 13px; }
.data-table th { background-color: #e9ecef; text-align: center; vertical-align: middle; font-weight: bold; color: #333; }
.data-table tbody tr:nth-child(even) td { background-color: #f9f9f9; }
.data-table tbody tr:hover td { background-color: #e6f7ff !important; }
@media print {
.portal-header, .sidebar, .portal-footer, .no-print, nav.portal-top-nav { display: none !important; }
body.gov-portal-shell { background: #fff; display: block; }
.gov-portal-shell .main.work-main { overflow: visible !important; padding: 0 !important; }
.print-header { display: block !important; }
}
</style>
</head>
<body class="gov-portal-shell select-none">
<header class="portal-header">
<div class="portal-header-inner">
<?= view('home/_dashboard_gov_portal_brand', ['brandHref' => base_url('admin')]) ?>
<?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?>
<div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;">
<span class="user-line">
<?php if ($effectiveLgName !== ''): ?><strong><?= esc($effectiveLgName) ?></strong> · <?php endif; ?>
<?= esc($levelName) ?> · <?= esc($mbName) ?>님
</span>
<a href="<?= base_url('/') ?>" title="사이트로"
style="display:inline-flex;align-items:center;gap:.3rem;padding:.25rem .6rem;border-radius:6px;background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.3);color:#fff;text-decoration:none;font-size:.75rem;font-weight:600;white-space:nowrap;">
<i class="fa-solid fa-house"></i> 사이트
</a>
<a href="<?= base_url('logout') ?>" title="로그아웃"
style="display:inline-flex;align-items:center;gap:.3rem;padding:.25rem .6rem;border-radius:6px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.22);color:#fff;text-decoration:none;font-size:.75rem;font-weight:600;white-space:nowrap;">
<i class="fa-solid fa-right-from-bracket"></i> 로그아웃
</a>
</div>
</div>
</header>
<div class="layout">
<?= view('home/_dashboard_gov_portal_sidebar', $navPartial) ?>
<main class="main work-main main-content-area">
<?php if (! empty($title)): ?>
<h1 class="work-titlebar"><i class="fa-solid fa-gear tb-ico"></i><?= esc($title) ?></h1>
<?php endif; ?>
<?php if (session()->getFlashdata('success')): ?>
<div class="work-flash ok"><?= esc(session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="work-flash err"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('errors')): ?>
<div class="work-flash err"><?php foreach (session()->getFlashdata('errors') as $err): ?><div><?= esc($err) ?></div><?php endforeach; ?></div>
<?php endif; ?>
<div class="work-surface">
<?= $content ?>
</div>
</main>
</div>
<footer class="portal-footer">
<span>GBLS 관리자</span>
<span><?= date('Y.m.d (D) H:i') ?></span>
</footer>
<?= view('home/_dashboard_gov_portal_nav_script_base', $navPartial) ?>
<script>
(function () {
// bfcache 복원 시 열린 채 남은 모달/팝업으로 회색 레이어가 클릭을 막는 문제 방지
function closeStuckOverlays() {
document.querySelectorAll('.fixed.inset-0[id$="-modal"], .fixed.inset-0[id$="-popup"]').forEach(function (el) {
el.classList.add('hidden'); el.setAttribute('aria-hidden', 'true');
});
document.body.style.overflow = '';
}
window.addEventListener('pageshow', function (e) { if (e.persisted) closeStuckOverlays(); });
window.addEventListener('pagehide', closeStuckOverlays);
})();
</script>
</body>
</html>