Files
jongryangje/app/Views/admin/layout.php
taekyoungc 9d7d1e8a94 feat: 리포트 첫 진입 자동조회 + 글자 확대(탭·대메뉴 포함, 버튼 고정)
- 수급 계획·반품/파기 현황·기간별 봉투 수불 현황: 첫 진입에도 기본 조건 자동 조회
- 글자 확대 시 탭 텍스트·상단 대메뉴(font-size)도 확대, A−/A+ 버튼은 위치·크기 고정

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

195 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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 { font-size: 13px; }
.data-table th, .data-table td { text-align: left; padding: 0.55rem 0.5rem; white-space: nowrap; border: 0; border-bottom: 1px solid #e5e7eb; }
.data-table thead th { font-size: 0.6875rem; font-weight: 600; color: #6b7280; background: transparent; vertical-align: middle; }
.data-table tbody td { color: #374151; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover td { background-color: #f9fafb; }
@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('/')]) ?>
<?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?>
<div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;">
<div class="ws-fontctl" title="글씨 크기 조절" style="display:inline-flex;align-items:center;gap:2px;background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);border-radius:6px;padding:1px;">
<button type="button" id="wsFontMinus" title="글씨 작게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:11px;line-height:1;border-radius:5px;">A</button>
<span id="wsFontPct" style="min-width:34px;text-align:center;color:#fff;font-size:.68rem;font-weight:600;">100%</span>
<button type="button" id="wsFontPlus" title="글씨 크게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:14px;line-height:1;border-radius:5px;">A+</button>
</div>
<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);
})();
// 글씨 크기 조절(A/A+) — 본문 + 상단 대메뉴 + 좌측 사이드바에 zoom 적용. 사이트/워크스페이스와 배율 공유.
(function () {
var FONT_KEY = 'jrj_font_scale';
// 상단 헤더는 제외 → A/A+ 버튼이 커지거나 밀리지 않아 연속 클릭이 편하다.
var scaleSelectors = ['.sidebar', '.work-main'];
function curScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; }
function applyScale(s) {
s = Math.min(150, Math.max(70, s));
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
var z = s / 100;
scaleSelectors.forEach(function (sel) { var el = document.querySelector(sel); if (el) el.style.zoom = z; });
// 상단 대메뉴는 font-size 만 키운다(바 높이 48px 고정 → A/A+ 버튼이 안 밀림).
var topnav = document.querySelector('.portal-top-nav');
if (topnav) topnav.style.fontSize = (0.875 * z).toFixed(4) + 'rem';
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
}
applyScale(curScale());
var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus');
if (plus) plus.addEventListener('click', function () { applyScale(curScale() + 10); });
if (minus) minus.addEventListener('click', function () { applyScale(curScale() - 10); });
window.addEventListener('storage', function (e) { if (e.key === FONT_KEY) applyScale(curScale()); });
})();
</script>
</body>
</html>