- robthree/twofactorauth, Auth 설정·TotpService·2FA 뷰·라우트 - member TOTP 컬럼 DDL(login_tables, member_add_totp.sql) - 관리자 메뉴·레이아웃·필터·대시보드 등 연관 변경 - env 샘플에 auth.requireTotp 주석 Made-with: Cursor
280 lines
16 KiB
PHP
280 lines
16 KiB
PHP
<?php
|
||
/**
|
||
* 로그인 후 첫 화면 — 업무 현황 대시보드 (차장님 지시: 재고·구매신청·그래프 + 추가 시안)
|
||
* 레이아웃: 봉투 수불 엔터프라이즈 페이지와 동일한 상단 가로 메뉴·연한 파란 제목바·하단 상태줄
|
||
*
|
||
* @var string $lgLabel 로그인 지자체 표시명
|
||
*/
|
||
$lgLabel = $lgLabel ?? '북구';
|
||
$mbName = session()->get('mb_name') ?? '담당자';
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="utf-8"/>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||
<title>종량제 시스템 — 업무 현황</title>
|
||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>
|
||
<style>
|
||
body {
|
||
font-family: 'Malgun Gothic', 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif;
|
||
font-size: 13px;
|
||
color: #333;
|
||
}
|
||
.nav-top a.nav-active {
|
||
color: #2b4c8c;
|
||
font-weight: 600;
|
||
border-bottom: 2px solid #2b4c8c;
|
||
padding-bottom: 2px;
|
||
margin-bottom: -2px;
|
||
}
|
||
.kpi-card { transition: box-shadow .15s; }
|
||
.kpi-card:hover { box-shadow: 0 4px 14px rgba(43, 76, 140, .12); }
|
||
.bar-fill {
|
||
height: 10px;
|
||
border-radius: 4px;
|
||
background: linear-gradient(90deg, #2b4c8c, #3b82f6);
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="bg-gray-50 flex flex-col min-h-screen">
|
||
<!-- 상단: 수불 엔터프라이즈와 동일 계열 -->
|
||
<header class="border-b border-gray-300 bg-white shadow-sm shrink-0" data-purpose="top-navigation">
|
||
<div class="flex items-center justify-between px-4 py-2 gap-4 flex-wrap">
|
||
<div class="flex items-center gap-3 shrink-0">
|
||
<div class="flex items-center gap-2 text-green-700 font-bold text-lg">
|
||
<i class="fa-solid fa-recycle text-xl"></i>
|
||
<span>종량제 시스템</span>
|
||
</div>
|
||
<span class="hidden sm:inline text-xs text-gray-500 border-l border-gray-300 pl-3">
|
||
<?= esc($lgLabel) ?> · <strong class="text-gray-700"><?= esc($mbName) ?></strong>님
|
||
</span>
|
||
</div>
|
||
<nav class="nav-top hidden lg:flex flex-wrap items-center gap-4 xl:gap-5 text-sm font-medium text-gray-700">
|
||
<a class="nav-active flex items-center gap-1 whitespace-nowrap" href="<?= base_url('dashboard') ?>">
|
||
<i class="fa-solid fa-gauge-high"></i> 업무 현황
|
||
</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-regular fa-file-lines"></i> 문서 관리</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-solid fa-box-open"></i> 규격</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-solid fa-bag-shopping"></i> 봉투 양식</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-solid fa-table"></i> 데이터 양식</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-solid fa-clock-rotate-left"></i> 사용 내역</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="<?= base_url('bag/inventory-inquiry') ?>"><i class="fa-solid fa-boxes-stacked"></i> 재고 현황</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="<?= base_url('bag/waste-suibal-enterprise') ?>"><i class="fa-solid fa-table-list"></i> 수불 현황</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-solid fa-chart-line"></i> 통계 분석</a>
|
||
<a class="flex items-center gap-1 hover:text-blue-600 whitespace-nowrap" href="#"><i class="fa-solid fa-gear"></i> 설정</a>
|
||
</nav>
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<a href="<?= base_url('dashboard/modern') ?>" class="text-xs text-[#2b4c8c] hover:underline whitespace-nowrap hidden sm:inline" title="모던 레이아웃">모던</a>
|
||
<span class="text-gray-300 hidden sm:inline">|</span>
|
||
<a href="<?= base_url('dashboard/dense') ?>" class="text-xs text-[#2b4c8c] hover:underline whitespace-nowrap hidden sm:inline" title="정보 집약 종합">종합</a>
|
||
<span class="text-gray-300 hidden sm:inline">|</span>
|
||
<a href="<?= base_url('dashboard/blend') ?>" class="text-xs text-[#2b4c8c] hover:underline whitespace-nowrap hidden sm:inline" title="표+차트 혼합">혼합</a>
|
||
<span class="text-gray-300 hidden sm:inline">|</span>
|
||
<a href="<?= base_url('dashboard/charts') ?>" class="text-xs text-[#2b4c8c] hover:underline whitespace-nowrap hidden sm:inline" title="그래프 대시보드">차트</a>
|
||
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-gray-800 p-1" title="로그아웃">
|
||
<i class="fa-solid fa-arrow-right-from-bracket text-lg"></i>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="flex-1 flex flex-col min-h-0" data-purpose="dashboard-content">
|
||
<!-- 연한 파란 제목바 (수불 페이지와 동일 톤) -->
|
||
<div class="bg-[#eff5fb] border-b border-gray-300 px-4 py-2 flex flex-wrap justify-between items-center gap-2 text-sm font-semibold text-gray-800 shrink-0" data-purpose="page-title">
|
||
<span>
|
||
<i class="fa-solid fa-chart-pie text-[#2b4c8c] mr-2"></i>업무 현황 대시보드
|
||
<span class="text-xs font-normal text-gray-500">· 봉투 재고 · 구매신청 · 발주/승인 요약</span>
|
||
</span>
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-xs font-normal text-gray-500"><i class="fa-regular fa-calendar mr-1"></i><?= date('Y.m.d (D)') ?></span>
|
||
<button type="button" class="text-gray-500 hover:text-gray-800 p-1" title="조건 설정"><i class="fa-solid fa-sliders"></i></button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 대시보드용 경량 필터 (엔터프라이즈 필터바와 동일 계열) -->
|
||
<section class="p-2 border-b border-gray-300 bg-white shrink-0" data-purpose="dashboard-context">
|
||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||
<div class="flex flex-wrap items-center gap-3 text-xs">
|
||
<span class="text-gray-600 font-medium">기준일</span>
|
||
<input type="text" readonly value="<?= date('Y.m.d') ?>" class="border border-gray-300 px-2 py-1 rounded w-28 shadow-sm">
|
||
<span class="text-gray-500">|</span>
|
||
<span class="text-gray-600">지자체 <strong class="text-gray-800"><?= esc($lgLabel) ?></strong></span>
|
||
<button type="button" class="bg-[#2b4c8c] hover:bg-blue-800 text-white px-3 py-1 rounded text-xs font-medium shadow flex items-center gap-1">
|
||
<i class="fa-solid fa-rotate"></i> 새로고침
|
||
</button>
|
||
</div>
|
||
<p class="text-[11px] text-gray-400">목업 데이터 · 연동 시 실시간 반영</p>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="flex-1 overflow-y-auto p-4">
|
||
<?php if (session()->getFlashdata('success')): ?>
|
||
<div class="mb-3 p-3 rounded-lg bg-emerald-50 text-emerald-800 text-sm border border-emerald-200"><?= esc(session()->getFlashdata('success')) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<!-- KPI -->
|
||
<div class="grid grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
|
||
<div class="kpi-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<p class="text-xs text-gray-500 mb-1"><i class="fa-solid fa-triangle-exclamation text-amber-500 mr-1"></i>재고 부족 품목</p>
|
||
<p class="text-2xl font-bold text-gray-800">3</p>
|
||
<p class="text-[11px] text-gray-400 mt-1">안전재고 미만 봉투 종류</p>
|
||
</div>
|
||
<div class="kpi-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<p class="text-xs text-gray-500 mb-1"><i class="fa-solid fa-inbox text-sky-600 mr-1"></i>미처리 구매신청</p>
|
||
<p class="text-2xl font-bold text-sky-700">12</p>
|
||
<p class="text-[11px] text-gray-400 mt-1">지정판매소 · 금일 기준</p>
|
||
</div>
|
||
<div class="kpi-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<p class="text-xs text-gray-500 mb-1"><i class="fa-solid fa-truck-field text-emerald-600 mr-1"></i>이번 주 발주·입고</p>
|
||
<p class="text-2xl font-bold text-emerald-700">8 <span class="text-sm font-normal text-gray-500">건</span></p>
|
||
<p class="text-[11px] text-gray-400 mt-1">발주 5 · 입고완료 3</p>
|
||
</div>
|
||
<div class="kpi-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<p class="text-xs text-gray-500 mb-1"><i class="fa-solid fa-user-clock text-violet-600 mr-1"></i>승인 대기 회원</p>
|
||
<p class="text-2xl font-bold text-violet-700">4</p>
|
||
<p class="text-[11px] text-gray-400 mt-1">가입·권한 승인 요청</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-4 mb-4">
|
||
<section class="bg-white border border-gray-200 rounded-lg shadow-sm p-4">
|
||
<div class="flex items-center justify-between mb-3">
|
||
<h3 class="font-semibold text-gray-800"><i class="fa-solid fa-chart-bar text-[#2b4c8c] mr-2"></i>봉투별 재고 현황</h3>
|
||
<span class="text-[11px] text-gray-400">낱장 환산 · 목업</span>
|
||
</div>
|
||
<?php
|
||
$stockRows = [
|
||
['label' => '일반용 5L', 'pct' => 92],
|
||
['label' => '일반용 10L', 'pct' => 78],
|
||
['label' => '일반용 20L', 'pct' => 65],
|
||
['label' => '음식물 스티커', 'pct' => 41],
|
||
['label' => '재사용 봉투', 'pct' => 88],
|
||
];
|
||
foreach ($stockRows as $r):
|
||
?>
|
||
<div class="mb-3 last:mb-0">
|
||
<div class="flex justify-between text-xs mb-1">
|
||
<span><?= esc($r['label']) ?></span>
|
||
<span class="text-gray-500"><?= (int) $r['pct'] ?>%</span>
|
||
</div>
|
||
<div class="h-2 bg-gray-100 rounded-full overflow-hidden">
|
||
<div class="bar-fill h-full" style="width: <?= (int) $r['pct'] ?>%"></div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</section>
|
||
|
||
<section class="bg-white border border-gray-200 rounded-lg shadow-sm p-4">
|
||
<div class="flex items-center justify-between mb-3">
|
||
<h3 class="font-semibold text-gray-800"><i class="fa-solid fa-chart-line text-[#2b4c8c] mr-2"></i>최근 7일 구매신청·처리 추이</h3>
|
||
<span class="text-[11px] text-gray-400">건수</span>
|
||
</div>
|
||
<div class="flex items-end justify-between gap-1 h-48 px-1 border-b border-gray-200">
|
||
<?php
|
||
$days = [8, 12, 5, 14, 9, 11, 7];
|
||
$max = max($days);
|
||
foreach ($days as $i => $v):
|
||
$h = $max > 0 ? round(($v / $max) * 100) : 0;
|
||
?>
|
||
<div class="flex-1 flex flex-col items-center justify-end h-full group">
|
||
<span class="text-[10px] text-gray-500 mb-1"><?= $v ?></span>
|
||
<div class="w-full max-w-[2.5rem] rounded-t bg-gradient-to-t from-sky-800 to-sky-400 transition group-hover:opacity-90" style="height: <?= $h ?>%"></div>
|
||
<span class="text-[10px] text-gray-400 mt-1">D-<?= 6 - $i ?></span>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<p class="text-[11px] text-gray-500 mt-2 text-center">일별 신청 건수 · 처리 연동 예정</p>
|
||
</section>
|
||
</div>
|
||
|
||
<section class="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden mb-4">
|
||
<div class="px-4 py-3 border-b border-gray-200 flex flex-wrap items-center justify-between gap-2 bg-gray-50/80">
|
||
<h3 class="font-semibold text-gray-800"><i class="fa-solid fa-list-ul text-[#2b4c8c] mr-2"></i>지정판매소 구매신청 (최근)</h3>
|
||
<button type="button" class="text-xs text-sky-700 hover:underline font-medium">전체 보기</button>
|
||
</div>
|
||
<div class="overflow-x-auto">
|
||
<table class="w-full text-xs">
|
||
<thead class="bg-gray-100 text-gray-600 border-b border-gray-200">
|
||
<tr>
|
||
<th class="text-left font-semibold px-4 py-2">신청일시</th>
|
||
<th class="text-left font-semibold px-4 py-2">판매소</th>
|
||
<th class="text-left font-semibold px-4 py-2">품목</th>
|
||
<th class="text-right font-semibold px-4 py-2">수량</th>
|
||
<th class="text-center font-semibold px-4 py-2">상태</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-100">
|
||
<?php
|
||
$orders = [
|
||
['2025-02-26 09:12', '행복마트 북구점', '일반용 5L', '2,000장', '접수'],
|
||
['2025-02-26 08:45', '○○슈퍼', '음식물 스티커', '500장', '처리중'],
|
||
['2025-02-25 16:20', '△△상회', '일반용 20L', '박스 3', '완료'],
|
||
['2025-02-25 11:03', '□□편의점', '일반용 10L', '팩 12', '접수'],
|
||
['2025-02-24 14:50', '행복마트 북구점', '재사용 봉투', '1,200장', '완료'],
|
||
];
|
||
foreach ($orders as $o):
|
||
?>
|
||
<tr class="hover:bg-gray-50">
|
||
<td class="px-4 py-2.5 whitespace-nowrap"><?= esc($o[0]) ?></td>
|
||
<td class="px-4 py-2.5"><?= esc($o[1]) ?></td>
|
||
<td class="px-4 py-2.5"><?= esc($o[2]) ?></td>
|
||
<td class="px-4 py-2.5 text-right"><?= esc($o[3]) ?></td>
|
||
<td class="px-4 py-2.5 text-center">
|
||
<?php
|
||
$st = $o[4];
|
||
$cls = $st === '완료' ? 'bg-emerald-100 text-emerald-800' : ($st === '처리중' ? 'bg-amber-100 text-amber-800' : 'bg-gray-100 text-gray-700');
|
||
?>
|
||
<span class="inline-block px-2 py-0.5 rounded text-[11px] <?= $cls ?>"><?= esc($st) ?></span>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 mb-4">
|
||
<div class="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<h4 class="text-sm font-semibold text-gray-800 mb-2"><i class="fa-solid fa-boxes-packing text-emerald-600 mr-1"></i>이번 주 발주·입고 요약</h4>
|
||
<ul class="text-xs text-gray-600 space-y-1.5">
|
||
<li class="flex justify-between"><span>발주 접수</span><strong>5건</strong></li>
|
||
<li class="flex justify-between"><span>입고 완료</span><strong class="text-emerald-700">3건</strong></li>
|
||
<li class="flex justify-between"><span>입고 예정(LOT)</span><strong>2건</strong></li>
|
||
</ul>
|
||
</div>
|
||
<div class="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<h4 class="text-sm font-semibold text-gray-800 mb-2"><i class="fa-solid fa-user-check text-violet-600 mr-1"></i>승인 대기 회원</h4>
|
||
<p class="text-3xl font-bold text-violet-700">4</p>
|
||
<p class="text-xs text-gray-500 mt-1">지정판매소 · 일반 가입</p>
|
||
<button type="button" class="mt-3 w-full text-xs py-1.5 rounded border border-violet-200 text-violet-700 hover:bg-violet-50">승인 화면으로</button>
|
||
</div>
|
||
<div class="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||
<h4 class="text-sm font-semibold text-gray-800 mb-2"><i class="fa-solid fa-arrow-right-arrow-left text-orange-600 mr-1"></i>최근 7일 봉투 수불 추이</h4>
|
||
<p class="text-xs text-gray-600">입고 <strong class="text-gray-800">+482</strong> / 출고 <strong class="text-gray-800">−391</strong></p>
|
||
<div class="mt-2 h-16 flex items-end gap-0.5">
|
||
<?php foreach ([3, 5, 4, 6, 6, 5, 2] as $h): ?>
|
||
<div class="flex-1 bg-orange-200 rounded-t" style="height: <?= $h * 8 ?>%"></div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</main>
|
||
|
||
<footer class="bg-[#e9ecef] border-t border-gray-300 px-4 py-1.5 text-xs text-gray-600 flex justify-between items-center shrink-0" data-purpose="status-bar">
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-green-700"><i class="fa-solid fa-circle text-[6px] align-middle mr-1"></i>준비됨</span>
|
||
<span class="text-gray-400">|</span>
|
||
<span><?= esc($lgLabel) ?></span>
|
||
</div>
|
||
<div class="flex gap-4">
|
||
<span>Ver. 목업</span>
|
||
<span><?= date('Y.m.d (D) g:i A') ?></span>
|
||
</div>
|
||
</footer>
|
||
</body>
|
||
</html>
|