Initial project import for team collaboration.
Exclude local docs, MCP, and secrets via gitignore. Made-with: Cursor
This commit is contained in:
66
app/Views/admin/access/approvals.php
Normal file
66
app/Views/admin/access/approvals.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">권한 승인 대기</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2">
|
||||
<form method="get" action="<?= base_url('admin/access/approvals') ?>" class="mb-4 flex flex-wrap items-center gap-2 text-sm">
|
||||
<label for="status" class="font-bold text-gray-700 shrink-0">상태</label>
|
||||
<select id="status" name="status" class="border border-gray-300 rounded px-3 py-1.5 text-sm min-w-[12rem] w-48 max-w-full">
|
||||
<option value="pending" <?= ($status ?? 'pending') === 'pending' ? 'selected' : '' ?>>승인 대기</option>
|
||||
<option value="approved" <?= ($status ?? '') === 'approved' ? 'selected' : '' ?>>승인 완료</option>
|
||||
<option value="rejected" <?= ($status ?? '') === 'rejected' ? 'selected' : '' ?>>반려</option>
|
||||
</select>
|
||||
<button type="submit" class="bg-btn-search text-white px-3 py-1 rounded-sm text-xs">조회</button>
|
||||
</form>
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>요청일</th>
|
||||
<th>아이디</th>
|
||||
<th>이름</th>
|
||||
<th>지자체</th>
|
||||
<th>요청 역할</th>
|
||||
<th>상태</th>
|
||||
<th>처리일</th>
|
||||
<th>관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($list)): ?>
|
||||
<tr><td colspan="8" class="text-center text-gray-500 py-4">해당 상태의 요청이 없습니다.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($list as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->mar_requested_at ?? '-') ?></td>
|
||||
<td class="text-center"><?= esc($row->mb_id ?? '-') ?></td>
|
||||
<td class="text-center"><?= esc($row->mb_name ?? '-') ?></td>
|
||||
<td class="text-center"><?= esc($row->lg_name ?? '-') ?></td>
|
||||
<td class="text-center"><?= esc($roles->getLevelName((int) $row->mar_requested_level)) ?></td>
|
||||
<td class="text-center">
|
||||
<?php if (($row->mar_status ?? '') === 'pending'): ?>승인 대기<?php endif; ?>
|
||||
<?php if (($row->mar_status ?? '') === 'approved'): ?>승인 완료<?php endif; ?>
|
||||
<?php if (($row->mar_status ?? '') === 'rejected'): ?>반려<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= esc($row->mar_processed_at ?? '-') ?></td>
|
||||
<td class="text-center">
|
||||
<?php if (($row->mar_status ?? '') === 'pending'): ?>
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<form action="<?= base_url('admin/access/approve/' . $row->mar_idx) ?>" method="post" class="inline">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" class="bg-green-600 text-white px-2 py-1 text-xs rounded">승인</button>
|
||||
</form>
|
||||
<form action="<?= base_url('admin/access/reject/' . $row->mar_idx) ?>" method="post" class="inline flex items-center gap-1">
|
||||
<?= csrf_field() ?>
|
||||
<input type="text" name="reject_reason" placeholder="반려사유(선택)" class="border border-gray-300 rounded px-2 py-1 text-xs w-28"/>
|
||||
<button type="submit" class="bg-red-600 text-white px-2 py-1 text-xs rounded">반려</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<span class="text-gray-500">처리완료</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
37
app/Views/admin/access/login_history.php
Normal file
37
app/Views/admin/access/login_history.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<label class="font-bold text-gray-700">조회기간:</label>
|
||||
<form method="GET" action="<?= base_url('admin/access/login-history') ?>" class="flex items-center gap-2">
|
||||
<input type="date" name="start" class="border border-gray-300 rounded px-2 py-1 text-sm" value="<?= esc($start ?? '') ?>"/>
|
||||
<span>~</span>
|
||||
<input type="date" name="end" class="border border-gray-300 rounded px-2 py-1 text-sm" value="<?= esc($end ?? '') ?>"/>
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">조회</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>일시</th>
|
||||
<th>아이디</th>
|
||||
<th>성공</th>
|
||||
<th>IP</th>
|
||||
<th>메시지</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($list as $row): ?>
|
||||
<tr>
|
||||
<td class="text-left pl-2"><?= esc($row->mll_regdate ?? '') ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->mb_id ?? '') ?></td>
|
||||
<td class="text-center"><?= ! empty($row->mll_success) ? '성공' : '실패' ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->mll_ip ?? '') ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->mll_msg ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
3
app/Views/admin/dashboard/index.php
Normal file
3
app/Views/admin/dashboard/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="border border-gray-300 p-4">
|
||||
<p class="text-sm text-gray-600">관리자 메인 화면입니다. 상단 메뉴에서 기능을 선택하세요.</p>
|
||||
</div>
|
||||
102
app/Views/admin/designated_shop/create.php
Normal file
102
app/Views/admin/designated_shop/create.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">지정판매소 등록</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-3xl">
|
||||
<form action="<?= base_url('admin/designated-shops/store') ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<?php if (! empty($localGovs)): ?>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지자체 <span class="text-red-500">*</span></label>
|
||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_lg_idx" required>
|
||||
<option value="">선택</option>
|
||||
<?php foreach ($localGovs as $lg): ?>
|
||||
<option value="<?= esc($lg->lg_idx) ?>" <?= (string) old('ds_lg_idx') === (string) $lg->lg_idx ? 'selected' : '' ?>>
|
||||
<?= esc($lg->lg_name) ?> (<?= esc($lg->lg_code) ?>)
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php elseif (! empty($currentLg)): ?>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지자체</label>
|
||||
<div class="text-sm">
|
||||
<span class="font-semibold"><?= esc($currentLg->lg_name) ?></span>
|
||||
<span class="text-gray-500 ml-2">(<?= esc($currentLg->lg_code) ?>)</span>
|
||||
</div>
|
||||
<input type="hidden" name="ds_lg_idx" value="<?= esc($currentLg->lg_idx) ?>"/>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">판매소번호</label>
|
||||
<div class="text-sm text-gray-600">등록 시 자동 부여 (지자체코드 + 일련번호 3자리)</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">상호명 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-72" name="ds_name" type="text" value="<?= esc(old('ds_name')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">사업자번호 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_biz_no" type="text" value="<?= esc(old('ds_biz_no')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">대표자명 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_rep_name" type="text" value="<?= esc(old('ds_rep_name')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">가상계좌</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_va_number" type="text" value="<?= esc(old('ds_va_number')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">우편번호</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32" name="ds_zip" type="text" value="<?= esc(old('ds_zip')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">도로명주소</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr" type="text" value="<?= esc(old('ds_addr')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지번주소</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr_jibun" type="text" value="<?= esc(old('ds_addr_jibun')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">일반전화</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_tel" type="text" value="<?= esc(old('ds_tel')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">대표 휴대전화</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_rep_phone" type="text" value="<?= esc(old('ds_rep_phone')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">이메일</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_email" type="email" value="<?= esc(old('ds_email')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">구코드</label>
|
||||
<div class="text-sm text-gray-600">해당 지자체(구·군) 코드로 등록 시 자동 설정</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지정일자</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_designated_at" type="date" value="<?= esc(old('ds_designated_at')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
||||
<a href="<?= base_url('admin/designated-shops') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
105
app/Views/admin/designated_shop/edit.php
Normal file
105
app/Views/admin/designated_shop/edit.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
$shop = $shop ?? null;
|
||||
$currentLg = $currentLg ?? null;
|
||||
if ($shop === null) {
|
||||
return;
|
||||
}
|
||||
$v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($key) : ($shop->{$key} ?? $default);
|
||||
?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">지정판매소 수정</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-3xl">
|
||||
<form action="<?= base_url('admin/designated-shops/update/' . (int) $shop->ds_idx) ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<?php if ($currentLg !== null): ?>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지자체</label>
|
||||
<div class="text-sm">
|
||||
<span class="font-semibold"><?= esc($currentLg->lg_name) ?></span>
|
||||
<span class="text-gray-500 ml-2">(<?= esc($currentLg->lg_code) ?>)</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">판매소번호</label>
|
||||
<div class="text-sm font-mono"><?= esc($shop->ds_shop_no) ?></div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">구코드</label>
|
||||
<div class="text-sm font-mono"><?= esc($shop->ds_gugun_code) ?></div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">상호명 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-72" name="ds_name" type="text" value="<?= esc($v('ds_name')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">사업자번호 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_biz_no" type="text" value="<?= esc($v('ds_biz_no')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">대표자명 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_rep_name" type="text" value="<?= esc($v('ds_rep_name')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">가상계좌</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_va_number" type="text" value="<?= esc($v('ds_va_number')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">우편번호</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32" name="ds_zip" type="text" value="<?= esc($v('ds_zip')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">도로명주소</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr" type="text" value="<?= esc($v('ds_addr')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지번주소</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr_jibun" type="text" value="<?= esc($v('ds_addr_jibun')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">일반전화</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_tel" type="text" value="<?= esc($v('ds_tel')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">대표 휴대전화</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_rep_phone" type="text" value="<?= esc($v('ds_rep_phone')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">이메일</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_email" type="email" value="<?= esc($v('ds_email')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">지정일자</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_designated_at" type="date" value="<?= esc($v('ds_designated_at')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-28">영업상태</label>
|
||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_state">
|
||||
<option value="1" <?= (string) $v('ds_state', '1') === '1' ? 'selected' : '' ?>>정상</option>
|
||||
<option value="2" <?= (string) $v('ds_state', '1') === '2' ? 'selected' : '' ?>>폐업</option>
|
||||
<option value="3" <?= (string) $v('ds_state', '1') === '3' ? 'selected' : '' ?>>직권해지</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
|
||||
<a href="<?= base_url('admin/designated-shops') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
47
app/Views/admin/designated_shop/index.php
Normal file
47
app/Views/admin/designated_shop/index.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">지정판매소 목록</span>
|
||||
<a href="<?= base_url('admin/designated-shops/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">지정판매소 등록</a>
|
||||
</div>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-16">번호</th>
|
||||
<th>지자체</th>
|
||||
<th>판매소번호</th>
|
||||
<th>상호명</th>
|
||||
<th>대표자</th>
|
||||
<th>사업자번호</th>
|
||||
<th>가상계좌</th>
|
||||
<th>상태</th>
|
||||
<th>등록일</th>
|
||||
<th class="w-28">작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($list as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->ds_idx) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($lgMap[$row->ds_lg_idx] ?? '') ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->ds_shop_no) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->ds_name) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->ds_rep_name) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->ds_biz_no) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->ds_va_number) ?></td>
|
||||
<td class="text-center"><?= (int) $row->ds_state === 1 ? '정상' : ((int) $row->ds_state === 2 ? '폐업' : '직권해지') ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->ds_regdate ?? '') ?></td>
|
||||
<td class="text-center">
|
||||
<a href="<?= base_url('admin/designated-shops/edit/' . (int) $row->ds_idx) ?>" class="text-blue-600 hover:underline text-sm">수정</a>
|
||||
<form action="<?= base_url('admin/designated-shops/delete/' . (int) $row->ds_idx) ?>" method="POST" class="inline ml-1" onsubmit="return confirm('이 지정판매소를 삭제하시겠습니까?');">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
154
app/Views/admin/layout.php
Normal file
154
app/Views/admin/layout.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
helper('admin');
|
||||
$uriObj = service('request')->getUri();
|
||||
$n = $uriObj->getTotalSegments();
|
||||
$uri = $n >= 2 ? $uriObj->getSegment(2) : '';
|
||||
$seg3 = $n >= 3 ? $uriObj->getSegment(3) : '';
|
||||
$mbLevel = (int) session()->get('mb_level');
|
||||
$isSuperAdmin = ($mbLevel === \Config\Roles::LEVEL_SUPER_ADMIN);
|
||||
$effectiveLgIdx = admin_effective_lg_idx();
|
||||
$effectiveLgName = null;
|
||||
if ($effectiveLgIdx) {
|
||||
$lgRow = model(\App\Models\LocalGovernmentModel::class)->find($effectiveLgIdx);
|
||||
$effectiveLgName = $lgRow ? $lgRow->lg_name : null;
|
||||
}
|
||||
$currentPath = trim((string) $uriObj->getPath(), '/');
|
||||
if (str_starts_with($currentPath, 'index.php/')) {
|
||||
$currentPath = substr($currentPath, strlen('index.php/'));
|
||||
}
|
||||
$adminNavTree = get_admin_nav_tree();
|
||||
$isActive = static function (string $path) use ($uri, $seg3, $currentPath, $adminNavTree) {
|
||||
if (! empty($adminNavTree)) {
|
||||
return $currentPath === trim($path, '/');
|
||||
}
|
||||
if ($path === 'admin' || $path === '') return $uri === '';
|
||||
if ($path === 'users') return $uri === 'users';
|
||||
if ($path === 'login-history') return $uri === 'access' && $seg3 === 'login-history';
|
||||
if ($path === 'approvals') return $uri === 'access' && $seg3 === 'approvals';
|
||||
if ($path === 'roles') return $uri === 'roles';
|
||||
if ($path === 'menus') return $uri === 'menus';
|
||||
if ($path === 'local-governments') return $uri === 'local-governments';
|
||||
if ($path === 'select-local-government') return $uri === 'select-local-government';
|
||||
if ($path === 'designated-shops') return $uri === 'designated-shops';
|
||||
return false;
|
||||
};
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title><?= esc($title ?? '관리자') ?> - 쓰레기봉투 물류시스템</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"/>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: { sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
|
||||
colors: {
|
||||
'system-header': '#ffffff',
|
||||
'title-bar': '#2c3e50',
|
||||
'control-panel': '#f8f9fa',
|
||||
'btn-search': '#1c4e80',
|
||||
'btn-excel-border': '#28a745',
|
||||
'btn-excel-text': '#28a745',
|
||||
'btn-print-border': '#ced4da',
|
||||
'btn-exit': '#d9534f',
|
||||
},
|
||||
fontSize: { 'xxs': '0.65rem' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style data-purpose="table-layout">
|
||||
.data-table { width: 100%; border-collapse: collapse; font-family: '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:nth-child(even) { background-color: #f9f9f9; }
|
||||
.data-table tbody tr:hover td { background-color: #e6f7ff !important; }
|
||||
.main-content-area { height: calc(100vh - 170px); overflow: auto; }
|
||||
body { overflow: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased select-none">
|
||||
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0 z-20">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 flex items-center justify-center shrink-0">
|
||||
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="<?= base_url('admin') ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
|
||||
</div>
|
||||
<nav class="hidden md:flex gap-5 text-sm font-medium text-gray-600">
|
||||
<?php if (! empty($adminNavTree)): ?>
|
||||
<?php foreach ($adminNavTree as $navItem): ?>
|
||||
<?php $hasChildren = ! empty($navItem->children); ?>
|
||||
<div class="relative group">
|
||||
<a class="<?= $isActive($navItem->mm_link) ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>"
|
||||
href="<?= base_url($navItem->mm_link) ?>">
|
||||
<?= esc($navItem->mm_name) ?>
|
||||
</a>
|
||||
<?php if ($hasChildren): ?>
|
||||
<div class="absolute left-0 top-full hidden group-hover:block bg-white border border-gray-200 rounded shadow-lg min-w-[10rem] z-30">
|
||||
<?php foreach ($navItem->children as $child): ?>
|
||||
<a href="<?= base_url($child->mm_link) ?>"
|
||||
class="block px-3 py-1.5 text-sm text-gray-700 hover:bg-blue-50 whitespace-nowrap">
|
||||
<?= esc($child->mm_name) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<a class="<?= $isActive('') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin') ?>">대시보드</a>
|
||||
<a class="<?= $isActive('users') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/users') ?>">회원 관리</a>
|
||||
<a class="<?= $isActive('login-history') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/access/login-history') ?>">로그인 이력</a>
|
||||
<a class="<?= $isActive('approvals') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/access/approvals') ?>">승인 대기</a>
|
||||
<a class="<?= $isActive('roles') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/roles') ?>">역할</a>
|
||||
<a class="<?= $isActive('menus') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/menus') ?>">메뉴</a>
|
||||
<?php if ($isSuperAdmin): ?>
|
||||
<a class="<?= $isActive('select-local-government') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/select-local-government') ?>">지자체 전환</a>
|
||||
<a class="<?= $isActive('local-governments') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/local-governments') ?>">지자체</a>
|
||||
<?php endif; ?>
|
||||
<a class="<?= $isActive('designated-shops') ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>" href="<?= base_url('admin/designated-shops') ?>">지정판매소</a>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<?php if ($effectiveLgName !== null): ?>
|
||||
<span class="text-sm text-gray-600" title="현재 작업 지자체"><?= esc($effectiveLgName) ?></span>
|
||||
<?php endif; ?>
|
||||
<a href="<?= base_url('/') ?>" class="text-gray-500 hover:text-blue-600 text-sm">사이트</a>
|
||||
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-red-600 transition-colors inline-block p-1 rounded hover:bg-red-50" title="로그아웃">
|
||||
<svg class="h-5 w-5 inline" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/></svg> 종료
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
|
||||
<?= esc($title ?? '관리자') ?>
|
||||
</div>
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-green-50 text-green-700 text-sm" role="alert"><?= esc(session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm" role="alert"><?= esc(session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('errors')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm space-y-1" role="alert">
|
||||
<?php foreach (session()->getFlashdata('errors') as $err): ?><p><?= esc($err) ?></p><?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<main class="main-content-area flex-grow bg-white p-4">
|
||||
<?= $content ?>
|
||||
</main>
|
||||
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 flex items-center justify-between shrink-0">
|
||||
<span>쓰레기봉투 물류시스템 관리자</span>
|
||||
<span><?= date('Y.m.d (D) g:i:sA') ?></span>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
40
app/Views/admin/local_government/create.php
Normal file
40
app/Views/admin/local_government/create.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">지자체 등록</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-xl">
|
||||
<form action="<?= base_url('admin/local-governments/store') ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-24">지자체명 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="lg_name" type="text" value="<?= esc(old('lg_name')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-24">코드 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="lg_code" type="text" value="<?= esc(old('lg_code')) ?>" required/>
|
||||
<span class="text-xs text-gray-500">행정 코드 등 식별용</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-24">시/도 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="lg_sido" type="text" value="<?= esc(old('lg_sido')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-24">구/군 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="lg_gugun" type="text" value="<?= esc(old('lg_gugun')) ?>" required/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-24">주소</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-72" name="lg_addr" type="text" value="<?= esc(old('lg_addr')) ?>"/>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
||||
<a href="<?= base_url('admin/local-governments') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
35
app/Views/admin/local_government/index.php
Normal file
35
app/Views/admin/local_government/index.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">지자체 목록</span>
|
||||
<a href="<?= base_url('admin/local-governments/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">지자체 등록</a>
|
||||
</div>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-16">번호</th>
|
||||
<th>지자체명</th>
|
||||
<th>코드</th>
|
||||
<th>시/도</th>
|
||||
<th>구/군</th>
|
||||
<th>상태</th>
|
||||
<th>등록일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($list as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->lg_idx) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->lg_name) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->lg_code) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->lg_sido) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->lg_gugun) ?></td>
|
||||
<td class="text-center"><?= (int) $row->lg_state === 1 ? '사용' : '미사용' ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->lg_regdate ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
469
app/Views/admin/menu/index.php
Normal file
469
app/Views/admin/menu/index.php
Normal file
@@ -0,0 +1,469 @@
|
||||
<?php
|
||||
$types = $types ?? [];
|
||||
$list = $list ?? [];
|
||||
$mtIdx = (int) ($mtIdx ?? 0);
|
||||
$mtCode = (string) ($mtCode ?? '');
|
||||
$levelNames = $levelNames ?? [];
|
||||
$superAdminLevel = \Config\Roles::LEVEL_SUPER_ADMIN;
|
||||
?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">메뉴 관리</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<?php if (! empty($types)): ?>
|
||||
<form method="get" action="<?= base_url('admin/menus') ?>" class="flex items-center gap-2">
|
||||
<label class="text-sm font-medium text-gray-700">메뉴 종류</label>
|
||||
<select name="mt_idx" onchange="this.form.submit()" class="border border-gray-300 rounded pl-2 pr-7 py-1 text-sm min-w-[8rem]">
|
||||
<?php foreach ($types as $t): ?>
|
||||
<option value="<?= (int) $t->mt_idx ?>" <?= $mtIdx === (int) $t->mt_idx ? 'selected' : '' ?>><?= esc($t->mt_name) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="flex gap-4 mt-2 flex-wrap">
|
||||
<div class="border border-gray-300 bg-white rounded p-4 flex-1 min-w-0" style="min-width: 320px;">
|
||||
<h3 class="text-sm font-bold text-gray-700 mb-2">메뉴 목록</h3>
|
||||
<?php if ($mtIdx <= 0): ?>
|
||||
<p class="text-sm text-gray-600">메뉴 종류를 선택하세요.</p>
|
||||
<?php elseif (empty($list)): ?>
|
||||
<p class="text-sm text-gray-600">등록된 메뉴가 없습니다. 아래에서 등록하세요.</p>
|
||||
<?php else: ?>
|
||||
<form id="menu-move-form" method="post" action="<?= base_url('admin/menus/move') ?>">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mt_idx" value="<?= $mtIdx ?>"/>
|
||||
<table class="data-table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-20 text-center text-xs font-medium text-gray-600">순서변경</th>
|
||||
<th class="w-10">#</th>
|
||||
<th>메뉴명</th>
|
||||
<th>링크</th>
|
||||
<th>노출 대상</th>
|
||||
<th>사용</th>
|
||||
<th class="w-24">작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($list as $i => $row): ?>
|
||||
<tr class="menu-row" data-mm-idx="<?= (int) $row->mm_idx ?>" data-mm-pidx="<?= (int) $row->mm_pidx ?>" data-mm-dep="<?= (int) $row->mm_dep ?>">
|
||||
<td class="text-center align-middle">
|
||||
<span class="menu-drag-handle cursor-move text-gray-400 select-none" title="드래그해서 순서를 변경하세요">↕</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<input type="hidden" name="mm_idx[]" value="<?= (int) $row->mm_idx ?>"/>
|
||||
<span class="menu-order-no"><?= (int) $row->mm_num + 1 ?></span>
|
||||
</td>
|
||||
<td class="text-left pl-2" style="padding-left: <?= (int) $row->mm_dep * 12 + 8 ?>px;">
|
||||
<?php $dep = (int) $row->mm_dep; ?>
|
||||
<span class="text-xs text-gray-400">
|
||||
<?php if ($dep === 0): ?>
|
||||
●
|
||||
<?php elseif ($dep === 1): ?>
|
||||
└
|
||||
<?php else: ?>
|
||||
└─
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
<span class="ml-1"><?= esc($row->mm_name) ?></span>
|
||||
</td>
|
||||
<td class="text-left pl-2 text-xs"><?= esc($row->mm_link) ?></td>
|
||||
<td class="text-left pl-2 text-xs">
|
||||
<?php
|
||||
if ((string) $row->mm_level === '') {
|
||||
echo '전체';
|
||||
} else {
|
||||
$levels = array_filter(explode(',', $row->mm_level), fn ($lv) => (int) trim($lv) !== $superAdminLevel);
|
||||
$labels = array_map(fn ($lv) => $levelNames[trim($lv)] ?? trim($lv), $levels);
|
||||
echo esc(implode(', ', $labels) ?: '전체');
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td class="text-center"><?= (string) $row->mm_is_view === 'Y' ? 'Y' : 'N' ?></td>
|
||||
<td class="text-center">
|
||||
<button type="button"
|
||||
class="text-blue-600 hover:underline text-sm menu-edit"
|
||||
data-id="<?= (int) $row->mm_idx ?>"
|
||||
data-name="<?= esc($row->mm_name) ?>"
|
||||
data-link="<?= esc($row->mm_link) ?>"
|
||||
data-level="<?= esc($row->mm_level) ?>"
|
||||
data-view="<?= (string) $row->mm_is_view ?>"
|
||||
data-dep="<?= (int) $row->mm_dep ?>">
|
||||
수정
|
||||
</button>
|
||||
<?php if ($dep === 0): ?>
|
||||
<button type="button"
|
||||
class="text-green-700 hover:underline text-sm ml-1 menu-add-child"
|
||||
data-id="<?= (int) $row->mm_idx ?>"
|
||||
data-dep="<?= (int) $row->mm_dep ?>">
|
||||
하위 메뉴 추가
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<form action="<?= base_url('admin/menus/delete/' . (int) $row->mm_idx) ?>" method="post" class="inline ml-1" onsubmit="return confirm('이 메뉴를 삭제하시겠습니까?');">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="bg-gray-600 text-white px-3 py-1 rounded text-sm">순서 적용</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="border border-gray-300 bg-white rounded p-4 w-80 shrink-0">
|
||||
<h3 class="text-sm font-bold text-gray-700 mb-2" id="form-title">메뉴 등록</h3>
|
||||
<form id="menu-form" method="post" action="<?= base_url('admin/menus/store') ?>">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="mt_idx" value="<?= $mtIdx ?>"/>
|
||||
<input type="hidden" name="mm_pidx" value="0"/>
|
||||
<input type="hidden" name="mm_dep" value="0"/>
|
||||
<input type="hidden" name="mm_idx_edit" id="mm_idx_edit" value=""/>
|
||||
|
||||
<div class="space-y-2 mb-3">
|
||||
<label class="block text-sm font-medium text-gray-700">메뉴명</label>
|
||||
<input type="text" name="mm_name" id="mm_name" required class="border border-gray-300 rounded px-2 py-1 w-full text-sm" placeholder="예: 대시보드"/>
|
||||
</div>
|
||||
<div class="space-y-2 mb-3">
|
||||
<label class="block text-sm font-medium text-gray-700">링크</label>
|
||||
<input type="text" name="mm_link" id="mm_link" class="border border-gray-300 rounded px-2 py-1 w-full text-sm" placeholder="예: admin/users"/>
|
||||
</div>
|
||||
<div class="space-y-2 mb-3">
|
||||
<label class="block text-sm font-medium text-gray-700">노출 대상</label>
|
||||
<?php if ($mtCode === 'admin'): ?>
|
||||
<p class="text-sm text-gray-600">관리자 메뉴는 <b>지자체관리자</b>에게만 노출됩니다. (고정)</p>
|
||||
<?php else: ?>
|
||||
<div class="flex flex-wrap gap-x-4 gap-y-1">
|
||||
<label class="inline-flex items-center gap-1">
|
||||
<input type="checkbox" name="mm_level_all" id="mm_level_all" value="1" checked/>
|
||||
<span class="text-sm">전체</span>
|
||||
</label>
|
||||
<?php foreach ($levelNames as $lv => $name): ?>
|
||||
<?php if ((int) $lv === $superAdminLevel) { continue; } ?>
|
||||
<label class="inline-flex items-center gap-1 mm-level-label">
|
||||
<input type="checkbox" name="mm_level[]" value="<?= (int) $lv ?>" class="mm-level-cb"/>
|
||||
<span class="text-sm"><?= esc($name) ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="space-y-2 mb-3">
|
||||
<label class="inline-flex items-center gap-1">
|
||||
<input type="checkbox" name="mm_is_view" value="1" id="mm_is_view" checked/>
|
||||
<span class="text-sm">노출</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded text-sm" id="btn-submit">등록</button>
|
||||
<button type="button" class="bg-gray-200 text-gray-700 px-4 py-1.5 rounded text-sm" id="btn-cancel-edit" style="display:none;">취소</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
tr.menu-row.dragging {
|
||||
opacity: 0.35;
|
||||
}
|
||||
body.menu-row-dragging {
|
||||
user-select: none;
|
||||
cursor: move;
|
||||
}
|
||||
tr.menu-drop-placeholder td {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.menu-drop-placeholder-box {
|
||||
margin: 4px 0;
|
||||
height: 34px;
|
||||
border: 2px dashed #60a5fa;
|
||||
border-radius: 6px;
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const form = document.getElementById('menu-form');
|
||||
const formTitle = document.getElementById('form-title');
|
||||
const btnSubmit = document.getElementById('btn-submit');
|
||||
const btnCancel = document.getElementById('btn-cancel-edit');
|
||||
const editIdInput = document.getElementById('mm_idx_edit');
|
||||
const mmPidxInput = form.querySelector('[name="mm_pidx"]');
|
||||
const mmDepInput = form.querySelector('[name="mm_dep"]');
|
||||
const levelAll = document.getElementById('mm_level_all');
|
||||
const levelCbs = document.querySelectorAll('.mm-level-cb');
|
||||
const isAdminType = '<?= esc($mtCode) ?>' === 'admin';
|
||||
const moveForm = document.getElementById('menu-move-form');
|
||||
const tbody = moveForm ? moveForm.querySelector('tbody') : null;
|
||||
let draggingRow = null;
|
||||
|
||||
if (!isAdminType && levelAll) {
|
||||
// "전체" 체크 시: 다른 체크 해제
|
||||
levelAll.addEventListener('change', function(){
|
||||
if (levelAll.checked) {
|
||||
levelCbs.forEach(function(cb){ cb.checked = false; });
|
||||
}
|
||||
});
|
||||
|
||||
// 다른 체크 선택 시: "전체" 자동 해제
|
||||
levelCbs.forEach(function(cb){
|
||||
cb.addEventListener('change', function(){
|
||||
if (cb.checked) levelAll.checked = false;
|
||||
});
|
||||
});
|
||||
|
||||
form.addEventListener('submit', function(){
|
||||
if (!levelAll.checked) {
|
||||
var checked = [];
|
||||
levelCbs.forEach(function(cb){ if (cb.checked) checked.push(cb.value); });
|
||||
if (checked.length === 0) levelAll.checked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.menu-edit').forEach(function(btn){
|
||||
btn.addEventListener('click', function(){
|
||||
const id = this.dataset.id;
|
||||
const name = this.dataset.name;
|
||||
const link = this.dataset.link || '';
|
||||
const level = (this.dataset.level || '').toString().trim();
|
||||
const view = this.dataset.view || 'Y';
|
||||
const dep = parseInt(this.dataset.dep || '0', 10);
|
||||
form.action = '<?= base_url('admin/menus/update/') ?>' + id;
|
||||
form.querySelector('[name="mm_name"]').value = name;
|
||||
form.querySelector('[name="mm_link"]').value = link;
|
||||
mmPidxInput.value = '0';
|
||||
mmDepInput.value = String(dep);
|
||||
if (!isAdminType && levelAll) {
|
||||
levelAll.checked = (level === '');
|
||||
levelCbs.forEach(function(cb){
|
||||
cb.checked = level !== '' && level.split(',').indexOf(cb.value) >= 0;
|
||||
cb.setAttribute('name', 'mm_level[]');
|
||||
});
|
||||
if (level !== '' && !Array.prototype.some.call(levelCbs, function(cb){ return cb.checked; })) {
|
||||
levelAll.checked = true;
|
||||
levelCbs.forEach(function(cb){ cb.checked = false; });
|
||||
}
|
||||
}
|
||||
form.querySelector('[name="mm_is_view"]').checked = (view === 'Y');
|
||||
editIdInput.value = id;
|
||||
formTitle.textContent = '메뉴 수정';
|
||||
btnSubmit.textContent = '수정';
|
||||
btnCancel.style.display = 'inline-block';
|
||||
});
|
||||
});
|
||||
|
||||
// 하위 메뉴 추가
|
||||
document.querySelectorAll('.menu-add-child').forEach(function(btn){
|
||||
btn.addEventListener('click', function(){
|
||||
const parentId = this.dataset.id;
|
||||
const parentDep = parseInt(this.dataset.dep || '0', 10);
|
||||
form.action = '<?= base_url('admin/menus/store') ?>';
|
||||
form.reset();
|
||||
form.querySelector('[name="mt_idx"]').value = '<?= $mtIdx ?>';
|
||||
mmPidxInput.value = parentId;
|
||||
mmDepInput.value = String(parentDep + 1);
|
||||
editIdInput.value = '';
|
||||
formTitle.textContent = '하위 메뉴 등록';
|
||||
btnSubmit.textContent = '등록';
|
||||
btnCancel.style.display = 'inline-block';
|
||||
// 노출 기본값 재설정
|
||||
form.querySelector('[name="mm_is_view"]').checked = true;
|
||||
if (!isAdminType && levelAll) {
|
||||
levelAll.checked = true;
|
||||
levelCbs.forEach(function(cb){
|
||||
cb.checked = false;
|
||||
cb.setAttribute('name', 'mm_level[]');
|
||||
});
|
||||
}
|
||||
document.getElementById('mm_name').focus();
|
||||
});
|
||||
});
|
||||
|
||||
btnCancel.addEventListener('click', function(){
|
||||
form.action = '<?= base_url('admin/menus/store') ?>';
|
||||
form.reset();
|
||||
form.querySelector('[name="mt_idx"]').value = '<?= $mtIdx ?>';
|
||||
mmPidxInput.value = '0';
|
||||
mmDepInput.value = '0';
|
||||
form.querySelector('[name="mm_is_view"]').checked = true;
|
||||
if (!isAdminType && levelAll) {
|
||||
levelAll.checked = true;
|
||||
levelCbs.forEach(function(cb){ cb.checked = false; cb.setAttribute('name', 'mm_level[]'); });
|
||||
}
|
||||
editIdInput.value = '';
|
||||
formTitle.textContent = '메뉴 등록';
|
||||
btnSubmit.textContent = '등록';
|
||||
btnCancel.style.display = 'none';
|
||||
});
|
||||
|
||||
// 메뉴 목록 행 드래그 정렬 (마우스 이벤트 기반)
|
||||
if (tbody) {
|
||||
const colCount = document.querySelectorAll('.data-table thead th').length || 7;
|
||||
const makePlaceholder = function() {
|
||||
const tr = document.createElement('tr');
|
||||
tr.className = 'menu-drop-placeholder';
|
||||
const td = document.createElement('td');
|
||||
td.colSpan = colCount;
|
||||
td.innerHTML = '<div class="menu-drop-placeholder-box">여기에 놓기</div>';
|
||||
tr.appendChild(td);
|
||||
return tr;
|
||||
};
|
||||
|
||||
const refreshOrderNos = function() {
|
||||
tbody.querySelectorAll('tr.menu-row').forEach(function(row, idx){
|
||||
const noEl = row.querySelector('.menu-order-no');
|
||||
if (noEl) noEl.textContent = String(idx + 1);
|
||||
});
|
||||
};
|
||||
|
||||
let placeholderRow = null;
|
||||
let originalOrderIds = [];
|
||||
let rafId = null;
|
||||
let lastClientY = 0;
|
||||
let draggingActive = false;
|
||||
|
||||
const collectCurrentOrderIds = function() {
|
||||
return Array.prototype.slice.call(tbody.querySelectorAll('tr.menu-row')).map(function(row){
|
||||
const idInput = row.querySelector('input[name="mm_idx[]"]');
|
||||
return idInput ? idInput.value : '';
|
||||
}).filter(Boolean);
|
||||
};
|
||||
|
||||
const restoreOrderByIds = function(ids) {
|
||||
if (!ids.length) return;
|
||||
const rowMap = {};
|
||||
tbody.querySelectorAll('tr.menu-row').forEach(function(row){
|
||||
const idInput = row.querySelector('input[name="mm_idx[]"]');
|
||||
if (idInput) rowMap[idInput.value] = row;
|
||||
});
|
||||
ids.forEach(function(id){
|
||||
if (rowMap[id]) tbody.appendChild(rowMap[id]);
|
||||
});
|
||||
refreshOrderNos();
|
||||
};
|
||||
|
||||
const updatePlaceholderPosition = function() {
|
||||
rafId = null;
|
||||
if (!draggingActive || !draggingRow || !placeholderRow) return;
|
||||
const rows = Array.prototype.slice.call(tbody.querySelectorAll('tr.menu-row:not(.dragging)'));
|
||||
let placed = false;
|
||||
for (var i = 0; i < rows.length; i += 1) {
|
||||
var box = rows[i].getBoundingClientRect();
|
||||
var triggerY = box.top + (box.height * 0.25);
|
||||
if (lastClientY < triggerY) {
|
||||
if (placeholderRow !== rows[i]) {
|
||||
tbody.insertBefore(placeholderRow, rows[i]);
|
||||
}
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!placed) {
|
||||
tbody.appendChild(placeholderRow);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = function(e) {
|
||||
if (!draggingActive) return;
|
||||
lastClientY = e.clientY;
|
||||
if (!rafId) {
|
||||
rafId = window.requestAnimationFrame(updatePlaceholderPosition);
|
||||
}
|
||||
};
|
||||
|
||||
const clearDragState = function() {
|
||||
if (rafId) {
|
||||
window.cancelAnimationFrame(rafId);
|
||||
rafId = null;
|
||||
}
|
||||
document.body.classList.remove('menu-row-dragging');
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
window.removeEventListener('mouseup', onMouseUp);
|
||||
if (draggingRow) {
|
||||
draggingRow.classList.remove('dragging');
|
||||
}
|
||||
if (placeholderRow && placeholderRow.parentNode) {
|
||||
placeholderRow.parentNode.removeChild(placeholderRow);
|
||||
}
|
||||
placeholderRow = null;
|
||||
draggingRow = null;
|
||||
draggingActive = false;
|
||||
};
|
||||
|
||||
var onMouseUp = function() {
|
||||
if (!draggingActive || !draggingRow || !placeholderRow) {
|
||||
clearDragState();
|
||||
return;
|
||||
}
|
||||
var prevRow = placeholderRow.previousElementSibling;
|
||||
tbody.insertBefore(draggingRow, placeholderRow);
|
||||
refreshOrderNos();
|
||||
|
||||
var draggedDep = parseInt(draggingRow.dataset.mmDep || '0', 10);
|
||||
var draggedPidx = parseInt(draggingRow.dataset.mmPidx || '0', 10);
|
||||
|
||||
if (draggedDep > 0) {
|
||||
var valid = false;
|
||||
if (prevRow && prevRow.classList.contains('menu-row')) {
|
||||
var prevIdx = parseInt(prevRow.dataset.mmIdx || '0', 10);
|
||||
var prevPidx = parseInt(prevRow.dataset.mmPidx || '0', 10);
|
||||
if (prevRow === draggingRow) {
|
||||
valid = true;
|
||||
} else if (prevIdx === draggedPidx || prevPidx === draggedPidx) {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
if (!valid) {
|
||||
alert('하위 메뉴는 다른 상위 메뉴에는 들어갈 수 없습니다.');
|
||||
restoreOrderByIds(originalOrderIds);
|
||||
clearDragState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const approved = window.confirm('변경한 순서를 적용할까요?');
|
||||
if (!approved) {
|
||||
restoreOrderByIds(originalOrderIds);
|
||||
} else {
|
||||
moveForm.submit();
|
||||
}
|
||||
clearDragState();
|
||||
};
|
||||
|
||||
tbody.querySelectorAll('.menu-drag-handle').forEach(function(handle){
|
||||
handle.addEventListener('mousedown', function(e){
|
||||
if (e.button !== 0) return;
|
||||
const row = handle.closest('tr.menu-row');
|
||||
if (!row) return;
|
||||
e.preventDefault();
|
||||
originalOrderIds = collectCurrentOrderIds();
|
||||
draggingRow = row;
|
||||
placeholderRow = makePlaceholder();
|
||||
draggingActive = true;
|
||||
row.classList.add('dragging');
|
||||
document.body.classList.add('menu-row-dragging');
|
||||
tbody.insertBefore(placeholderRow, row.nextSibling);
|
||||
lastClientY = e.clientY;
|
||||
updatePlaceholderPosition();
|
||||
window.addEventListener('mousemove', onMouseMove, { passive: true });
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
22
app/Views/admin/role/index.php
Normal file
22
app/Views/admin/role/index.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">역할 (mb_level)</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2">
|
||||
<p class="text-sm text-gray-600 mb-4">Config\Roles 기반 역할 목록입니다.</p>
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-24">코드</th>
|
||||
<th>이름</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($roles->levelNames as $code => $name): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= (int) $code ?></td>
|
||||
<td class="text-left pl-2"><?= esc($name) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
21
app/Views/admin/select_local_government/index.php
Normal file
21
app/Views/admin/select_local_government/index.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<p class="text-sm text-gray-700 mb-2">관리자 페이지에서 사용할 지자체를 선택하세요. 선택한 지자체 기준으로 목록·등록이 표시됩니다.</p>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2 p-4">
|
||||
<?php if (empty($list)): ?>
|
||||
<p class="text-gray-600 py-4">등록된 지자체가 없습니다. <a href="<?= base_url('admin/local-governments') ?>" class="text-blue-600 hover:underline">지자체 관리</a>에서 먼저 등록하세요.</p>
|
||||
<?php else: ?>
|
||||
<form action="<?= base_url('admin/select-local-government') ?>" method="POST" class="space-y-3">
|
||||
<?= csrf_field() ?>
|
||||
<ul class="space-y-2">
|
||||
<?php foreach ($list as $lg): ?>
|
||||
<li class="flex items-center gap-2">
|
||||
<input type="radio" name="lg_idx" id="lg_<?= $lg->lg_idx ?>" value="<?= (int) $lg->lg_idx ?>" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"/>
|
||||
<label for="lg_<?= $lg->lg_idx ?>" class="text-sm font-medium text-gray-800 cursor-pointer"><?= esc($lg->lg_name) ?> (<?= esc($lg->lg_code) ?>)</label>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-2 rounded-sm text-sm font-medium shadow hover:opacity-90 transition">선택하고 관리자 페이지로 이동</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
40
app/Views/admin/user/create.php
Normal file
40
app/Views/admin/user/create.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">회원 등록</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-xl">
|
||||
<form action="<?= base_url('admin/users/store') ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">아이디 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_id" name="mb_id" type="text" value="<?= esc(old('mb_id')) ?>" required/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">비밀번호 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_passwd" name="mb_passwd" type="password" required/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">이름 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_name" name="mb_name" type="text" value="<?= esc(old('mb_name')) ?>" required/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">이메일</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_email" name="mb_email" type="email" value="<?= esc(old('mb_email')) ?>"/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">연락처</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_phone" name="mb_phone" type="text" value="<?= esc(old('mb_phone')) ?>"/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">역할 <span class="text-red-500">*</span></label>
|
||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_level" name="mb_level" required>
|
||||
<?php foreach (($assignableLevels ?? $roles->levelNames) as $lv => $name): ?>
|
||||
<option value="<?= $lv ?>" <?= (string) old('mb_level') === (string) $lv ? 'selected' : '' ?>><?= esc($name) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
||||
<a href="<?= base_url('admin/users') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
48
app/Views/admin/user/edit.php
Normal file
48
app/Views/admin/user/edit.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<span class="text-sm font-bold text-gray-700">회원 수정</span>
|
||||
</section>
|
||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-xl">
|
||||
<form action="<?= base_url('admin/users/update/' . $member->mb_idx) ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">아이디 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_id" name="mb_id" type="text" value="<?= esc(old('mb_id', $member->mb_id)) ?>" required/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">비밀번호</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_passwd" name="mb_passwd" type="password" placeholder="변경 시에만 입력"/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">이름 <span class="text-red-500">*</span></label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_name" name="mb_name" type="text" value="<?= esc(old('mb_name', $member->mb_name)) ?>" required/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">이메일</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_email" name="mb_email" type="email" value="<?= esc(old('mb_email', $member->mb_email ?? '')) ?>"/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">연락처</label>
|
||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_phone" name="mb_phone" type="text" value="<?= esc(old('mb_phone', $member->mb_phone ?? '')) ?>"/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">역할 <span class="text-red-500">*</span></label>
|
||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_level" name="mb_level" required>
|
||||
<?php foreach (($assignableLevels ?? $roles->levelNames) as $lv => $name): ?>
|
||||
<option value="<?= $lv ?>" <?= (string) old('mb_level', (string) $member->mb_level) === (string) $lv ? 'selected' : '' ?>><?= esc($name) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="block text-sm font-bold text-gray-700 w-20">상태 <span class="text-red-500">*</span></label>
|
||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm min-w-[12rem] w-56 max-w-full" id="mb_state" name="mb_state" required>
|
||||
<option value="1" <?= (int) old('mb_state', (int) $member->mb_state) === 1 ? 'selected' : '' ?>>정상</option>
|
||||
<option value="2" <?= (int) old('mb_state', (int) $member->mb_state) === 2 ? 'selected' : '' ?>>정지</option>
|
||||
<option value="0" <?= (int) old('mb_state', (int) $member->mb_state) === 0 ? 'selected' : '' ?>>탈퇴</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">저장</button>
|
||||
<a href="<?= base_url('admin/users') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
55
app/Views/admin/user/index.php
Normal file
55
app/Views/admin/user/index.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">회원 목록</span>
|
||||
<a href="<?= base_url('admin/users/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">회원 등록</a>
|
||||
</div>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-16">번호</th>
|
||||
<th>아이디</th>
|
||||
<th>이름</th>
|
||||
<th>이메일</th>
|
||||
<th>역할</th>
|
||||
<th>상태</th>
|
||||
<th>가입일</th>
|
||||
<th>관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($list as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->mb_idx) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->mb_id) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->mb_name) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->mb_email ?? '') ?></td>
|
||||
<td class="text-center"><?= esc($roles->getLevelName((int) $row->mb_level)) ?></td>
|
||||
<td class="text-center">
|
||||
<?php
|
||||
$approvalStatus = $approvalMap[(int) $row->mb_idx] ?? null;
|
||||
if ($approvalStatus === 'pending') {
|
||||
echo '승인대기';
|
||||
} elseif ($approvalStatus === 'rejected') {
|
||||
echo '승인반려';
|
||||
} else {
|
||||
echo ((int) $row->mb_state === 1 ? '정상' : ((int) $row->mb_state === 2 ? '정지' : '탈퇴'));
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td class="text-left pl-2"><?= esc($row->mb_regdate ?? '') ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ((int) $row->mb_state !== 0): ?>
|
||||
<a href="<?= base_url('admin/users/edit/' . $row->mb_idx) ?>" class="text-blue-600 hover:underline">수정</a>
|
||||
<form action="<?= base_url('admin/users/delete/' . $row->mb_idx) ?>" method="POST" class="inline ml-1" onsubmit="return confirm('탈퇴 처리하시겠습니까?');">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" class="text-red-600 hover:underline">삭제</button>
|
||||
</form>
|
||||
<?php else: ?>—<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
73
app/Views/auth/login.php
Normal file
73
app/Views/auth/login.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>로그인 - 쓰레기봉투 물류시스템</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"/>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: { sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
|
||||
colors: {
|
||||
'system-header': '#ffffff',
|
||||
'title-bar': '#2c3e50',
|
||||
'control-panel': '#f8f9fa',
|
||||
'btn-search': '#1c4e80',
|
||||
'btn-exit': '#d9534f',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased">
|
||||
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 flex items-center justify-center shrink-0">
|
||||
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
|
||||
로그인
|
||||
</div>
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-green-50 text-green-700 text-sm" role="alert"><?= esc(session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm" role="alert"><?= esc(session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('errors')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm space-y-1" role="alert">
|
||||
<?php foreach (session()->getFlashdata('errors') as $error): ?><p><?= esc($error) ?></p><?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<main class="flex-grow bg-control-panel border-b border-gray-300 p-6 flex items-center justify-center">
|
||||
<section class="w-full max-w-md bg-white border border-gray-300 rounded shadow-sm p-6">
|
||||
<form action="<?= base_url('login') ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="login_id">아이디</label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="login_id" name="login_id" type="text" placeholder="아이디" value="<?= esc(old('login_id')) ?>" autocomplete="username" autofocus/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="password">비밀번호</label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="password" name="password" type="password" placeholder="비밀번호" autocomplete="current-password"/>
|
||||
</div>
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-2 rounded-sm text-sm font-medium shadow hover:opacity-90 transition border border-transparent">로그인</button>
|
||||
<a href="<?= base_url('register') ?>" class="bg-white text-gray-700 border border-gray-300 px-4 py-2 rounded-sm text-sm shadow hover:bg-gray-50 transition">회원가입</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 shrink-0">쓰레기봉투 물류시스템</footer>
|
||||
</body>
|
||||
</html>
|
||||
107
app/Views/auth/register.php
Normal file
107
app/Views/auth/register.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>회원가입 - 쓰레기봉투 물류시스템</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"/>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: { sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
|
||||
colors: {
|
||||
'system-header': '#ffffff',
|
||||
'title-bar': '#2c3e50',
|
||||
'control-panel': '#f8f9fa',
|
||||
'btn-search': '#1c4e80',
|
||||
'btn-exit': '#d9534f',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased">
|
||||
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 flex items-center justify-center shrink-0">
|
||||
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
|
||||
회원가입
|
||||
</div>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm" role="alert"><?= esc(session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('errors')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm space-y-1" role="alert">
|
||||
<?php foreach (session()->getFlashdata('errors') as $error): ?><p><?= esc($error) ?></p><?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<main class="flex-grow bg-control-panel border-b border-gray-300 p-6 overflow-auto">
|
||||
<section class="w-full max-w-md mx-auto bg-white border border-gray-300 rounded shadow-sm p-6">
|
||||
<form action="<?= base_url('register') ?>" method="POST" class="space-y-4">
|
||||
<?= csrf_field() ?>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_id">아이디 <span class="text-red-500">*</span></label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_id" name="mb_id" type="text" value="<?= esc(old('mb_id')) ?>" autocomplete="username" autofocus/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_passwd">비밀번호 <span class="text-red-500">*</span></label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_passwd" name="mb_passwd" type="password" autocomplete="new-password"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_passwd_confirm">비밀번호 확인 <span class="text-red-500">*</span></label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_passwd_confirm" name="mb_passwd_confirm" type="password" autocomplete="new-password"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_name">이름 <span class="text-red-500">*</span></label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_name" name="mb_name" type="text" value="<?= esc(old('mb_name')) ?>" autocomplete="name"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_email">이메일</label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_email" name="mb_email" type="email" value="<?= esc(old('mb_email')) ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_phone">연락처</label>
|
||||
<input class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_phone" name="mb_phone" type="tel" value="<?= esc(old('mb_phone')) ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_lg_idx">지자체</label>
|
||||
<select class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_lg_idx" name="mb_lg_idx">
|
||||
<option value="">선택 안 함</option>
|
||||
<?php if (! empty($localGovernments)): ?>
|
||||
<?php foreach ($localGovernments as $lg): ?>
|
||||
<option value="<?= $lg->lg_idx ?>" <?= (string) old('mb_lg_idx') === (string) $lg->lg_idx ? 'selected' : '' ?>><?= esc($lg->lg_name) ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_level">사용자 역할 <span class="text-red-500">*</span></label>
|
||||
<select class="block w-full border border-gray-300 rounded px-3 py-2 text-sm focus:ring-blue-500 focus:border-blue-500" id="mb_level" name="mb_level">
|
||||
<?php foreach (config('Roles')->levelNames as $level => $name): ?>
|
||||
<?php if ((int) $level === \Config\Roles::LEVEL_SUPER_ADMIN) continue; ?>
|
||||
<option value="<?= $level ?>" <?= old('mb_level', config('Roles')->defaultLevelForSelfRegister) == $level ? 'selected' : '' ?>><?= esc($name) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-1">가입 후 관리자 승인 완료 시 로그인할 수 있습니다.</p>
|
||||
</div>
|
||||
<div class="flex gap-2 pt-2">
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-2 rounded-sm text-sm font-medium shadow hover:opacity-90 transition">가입하기</button>
|
||||
<a href="<?= base_url('login') ?>" class="bg-white text-gray-700 border border-gray-300 px-4 py-2 rounded-sm text-sm shadow hover:bg-gray-50 transition">로그인</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 shrink-0">쓰레기봉투 물류시스템</footer>
|
||||
</body>
|
||||
</html>
|
||||
637
app/Views/bag/daily_inventory.php
Normal file
637
app/Views/bag/daily_inventory.php
Normal file
@@ -0,0 +1,637 @@
|
||||
<?php
|
||||
helper('admin');
|
||||
$siteNavTree = get_site_nav_tree();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>쓰레기봉투 물류시스템</title>
|
||||
<!-- Tailwind CSS v3 with Plugins -->
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<!-- Font: Noto Sans KR -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"/>
|
||||
<!-- Tailwind Configuration for Custom Colors and Fonts -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
'system-header': '#ffffff',
|
||||
'title-bar': '#2c3e50',
|
||||
'control-panel': '#f8f9fa',
|
||||
'btn-search': '#1c4e80',
|
||||
'btn-excel-border': '#28a745',
|
||||
'btn-excel-text': '#28a745',
|
||||
'btn-print-border': '#ced4da',
|
||||
'btn-print-text': '#000000',
|
||||
'btn-exit': '#d9534f',
|
||||
},
|
||||
fontSize: {
|
||||
'xxs': '0.65rem',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- Custom CSS for Table Specifics and Scrollbars -->
|
||||
<style data-purpose="table-layout">
|
||||
/* High density table styles */
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-family: '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;
|
||||
}
|
||||
|
||||
/* Zebra striping */
|
||||
.data-table tbody tr:nth-child(even) td:not([rowspan]) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.data-table tbody tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.data-table tbody tr:hover td {
|
||||
background-color: #e6f7ff !important;
|
||||
}
|
||||
|
||||
/* Column specific alignments handled by classes in HTML, but defaults: */
|
||||
.text-left { text-align: left !important; }
|
||||
.text-right { text-align: right !important; }
|
||||
.text-center { text-align: center !important; }
|
||||
|
||||
/* Layout utilities */
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
.main-content-area {
|
||||
height: calc(100vh - 170px);
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen select-none">
|
||||
<!-- BEGIN: Top Navigation -->
|
||||
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0 z-20">
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- Logo: 파란색 사각형에 흰색 사각형 두 개 겹친 형태 (데스크톱 앱 아이콘 스타일) -->
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 flex items-center justify-center shrink-0">
|
||||
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" fill="#2563eb"/>
|
||||
<rect x="2" y="2" width="7" height="7" fill="white"/>
|
||||
<rect x="5" y="5" width="9" height="9" fill="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Nav Links -->
|
||||
<nav class="hidden md:flex gap-5 text-sm font-medium text-gray-600">
|
||||
<?php if (! empty($siteNavTree)): ?>
|
||||
<?php
|
||||
$uriObj = service('request')->getUri();
|
||||
$currentPath = trim((string) $uriObj->getPath(), '/');
|
||||
if (str_starts_with($currentPath, 'index.php/')) {
|
||||
$currentPath = substr($currentPath, strlen('index.php/'));
|
||||
}
|
||||
?>
|
||||
<?php foreach ($siteNavTree as $navItem): ?>
|
||||
<?php $isActive = ($currentPath === trim((string) $navItem->mm_link, '/')); ?>
|
||||
<div class="relative group">
|
||||
<a class="<?= $isActive ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>"
|
||||
href="<?= base_url($navItem->mm_link) ?>">
|
||||
<?= esc($navItem->mm_name) ?>
|
||||
</a>
|
||||
<?php if (! empty($navItem->children)): ?>
|
||||
<div class="absolute left-0 top-full hidden group-hover:block bg-white border border-gray-200 rounded shadow-lg min-w-[10rem] z-30">
|
||||
<?php foreach ($navItem->children as $child): ?>
|
||||
<a href="<?= base_url($child->mm_link) ?>"
|
||||
class="block px-3 py-1.5 text-sm text-gray-700 hover:bg-blue-50 whitespace-nowrap">
|
||||
<?= esc($child->mm_name) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<!-- DB 메뉴 미구현 시 기존 더미 메뉴 사용 -->
|
||||
<a class="hover:text-blue-600" href="#">기본정보관리</a>
|
||||
<a class="hover:text-blue-600" href="#">발주 입고 관리</a>
|
||||
<a class="hover:text-blue-600" href="#">불출 관리</a>
|
||||
<a class="text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3" href="#">재고 관리</a>
|
||||
<a class="hover:text-blue-600" href="#">판매 관리</a>
|
||||
<a class="hover:text-blue-600" href="#">판매 현황</a>
|
||||
<a class="hover:text-blue-600" href="#">봉투 수불 관리</a>
|
||||
<a class="hover:text-blue-600" href="#">통계 분석 관리</a>
|
||||
<a class="hover:text-blue-600" href="#">창</a>
|
||||
<a class="hover:text-blue-600" href="#">도움말</a>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
<?php
|
||||
$mbLevel = (int) session()->get('mb_level');
|
||||
$isAdmin = ($mbLevel === \Config\Roles::LEVEL_SUPER_ADMIN || $mbLevel === \Config\Roles::LEVEL_LOCAL_ADMIN);
|
||||
?>
|
||||
<!-- 관리자 이동 버튼(관리자만) · 종료 -->
|
||||
<div class="flex items-center gap-2">
|
||||
<?php if ($isAdmin): ?>
|
||||
<a href="<?= base_url('admin') ?>" class="bg-btn-search text-white px-3 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent" title="관리자">관리자</a>
|
||||
<?php endif; ?>
|
||||
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-red-600 transition-colors inline-block p-1 rounded hover:bg-red-50" title="로그아웃">
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<!-- END: Top Navigation -->
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-green-50 text-green-700 text-sm" role="alert">
|
||||
<?= esc(session()->getFlashdata('success')) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!-- BEGIN: Title Bar -->
|
||||
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium flex items-center shrink-0 w-full">
|
||||
<span class="opacity-80 mr-2">[w_gm804r]</span>
|
||||
<span>일일 봉투 수불 현황</span>
|
||||
</div>
|
||||
<!-- END: Title Bar -->
|
||||
<!-- BEGIN: Control Panel -->
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<!-- Filter Inputs -->
|
||||
<div class="flex items-center gap-4 text-sm whitespace-nowrap overflow-x-auto pb-1 md:pb-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-bold text-gray-700">조회기간:</label>
|
||||
<div class="flex items-center bg-white border border-gray-300 rounded px-2 py-1">
|
||||
<input class="w-24 text-center border-none p-0 focus:ring-0 text-sm" type="text" value="2024.01.01"/>
|
||||
<span class="mx-1">~</span>
|
||||
<input class="w-24 text-center border-none p-0 focus:ring-0 text-sm" type="text" value="2025.12.12"/>
|
||||
<svg class="w-4 h-4 text-gray-400 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-bold text-gray-700">봉투구분:</label>
|
||||
<select class="border border-gray-300 rounded py-1 pl-2 pr-8 text-sm focus:ring-blue-500 focus:border-blue-500">
|
||||
<option>전체</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-bold text-gray-700">봉투형식:</label>
|
||||
<select class="border border-gray-300 rounded py-1 pl-2 pr-8 text-sm focus:ring-blue-500 focus:border-blue-500">
|
||||
<option>전체 봉투</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-bold text-gray-700">대행소:</label>
|
||||
<select class="border border-gray-300 rounded py-1 pl-2 pr-8 text-sm focus:ring-blue-500 focus:border-blue-500">
|
||||
<option>북구</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex items-center gap-1.5 ml-auto">
|
||||
<button class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
|
||||
조회
|
||||
</button>
|
||||
<button class="bg-white text-btn-excel-text border border-btn-excel-border px-3 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:bg-green-50 transition">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm-1 2l5 5h-5V4zM6 20V4h5v7h7v9H6z"></path></svg>
|
||||
엑셀저장
|
||||
</button>
|
||||
<button class="bg-white text-black border border-btn-print-border px-3 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:bg-gray-50 transition">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
|
||||
인쇄
|
||||
</button>
|
||||
<a href="<?= base_url('logout') ?>" class="bg-btn-exit text-white px-3 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:bg-red-700 transition border border-transparent">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
||||
종료
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- END: Control Panel -->
|
||||
<!-- BEGIN: Main Content Area (Table) -->
|
||||
<main class="main-content-area flex-grow bg-white p-2">
|
||||
<div class="border border-gray-300 h-full overflow-auto">
|
||||
<table class="w-full data-table border-collapse">
|
||||
<!-- BEGIN: Table Head -->
|
||||
<thead><tr>
|
||||
<th class="w-[100px] bg-gray-100 border border-gray-300" rowspan="2">일자</th>
|
||||
<th class="min-w-[200px] bg-gray-100 border border-gray-300" rowspan="2">품 목</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300" rowspan="2">전일재고</th>
|
||||
<th class="bg-gray-100 border border-gray-300" colspan="3">입고</th>
|
||||
<th class="bg-gray-100 border border-gray-300" colspan="5">출고</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300" rowspan="2">잔량</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Under 입고 -->
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">입고</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">반품</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">입고계</th>
|
||||
<!-- Under 출고 -->
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">판매</th>
|
||||
<th class="w-[120px] bg-gray-100 border border-gray-300">일반불출/무료불출</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">반품</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">기타</th>
|
||||
<th class="w-[80px] bg-gray-100 border border-gray-300">출고계</th>
|
||||
</tr></thead>
|
||||
<!-- END: Table Head -->
|
||||
<!-- BEGIN: Table Body -->
|
||||
<tbody class="text-right"><!-- Row 1 -->
|
||||
<tr>
|
||||
<td class="align-top text-center bg-white" rowspan="22">2024.01.01</td>
|
||||
<td class="text-left pl-2">일반용 5L</td>
|
||||
<td class="text-right pr-2">187,240</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">187,240</td>
|
||||
</tr>
|
||||
<!-- Row 2 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 5L</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
</tr>
|
||||
<!-- Row 3 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 10L</td>
|
||||
<td class="text-right pr-2">159,428</td>
|
||||
<td class="text-right pr-2">252,000</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">252,800</td>
|
||||
<td class="text-right pr-2">8,580</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">8,580</td>
|
||||
<td class="text-right pr-2 bg-gray-50">8,990</td>
|
||||
<td class="text-right pr-2">402,848</td>
|
||||
</tr>
|
||||
<!-- Row 4 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 20L</td>
|
||||
<td class="text-right pr-2">212,082</td>
|
||||
<td class="text-right pr-2">201,000</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">201,600</td>
|
||||
<td class="text-right pr-2">11,320</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">11,320</td>
|
||||
<td class="text-right pr-2 bg-gray-50">11,320</td>
|
||||
<td class="text-right pr-2">402,365</td>
|
||||
</tr>
|
||||
<!-- Row 5 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 50L</td>
|
||||
<td class="text-right pr-2">7,605</td>
|
||||
<td class="text-right pr-2">13,000</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">13,000</td>
|
||||
<td class="text-right pr-2">540</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">540</td>
|
||||
<td class="text-right pr-2 bg-gray-50">540</td>
|
||||
<td class="text-right pr-2">20,065</td>
|
||||
</tr>
|
||||
<!-- Row 6 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 75L</td>
|
||||
<td class="text-right pr-2">31,459</td>
|
||||
<td class="text-right pr-2">22,600</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">22,600</td>
|
||||
<td class="text-right pr-2">2,990</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">2,090</td>
|
||||
<td class="text-right pr-2 bg-gray-50">3,640</td>
|
||||
<td class="text-right pr-2">86,240</td>
|
||||
</tr>
|
||||
<!-- Row 7 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 100L</td>
|
||||
<td class="text-right pr-2">11</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">11</td>
|
||||
</tr>
|
||||
<!-- Row 8 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 70L</td>
|
||||
<td class="text-right pr-2">77,400</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">1,000</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">1,000</td>
|
||||
<td class="text-right pr-2 bg-gray-50">1,000</td>
|
||||
<td class="text-right pr-2">76,400</td>
|
||||
</tr>
|
||||
<!-- Row 9 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">공동주택용 20L</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
</tr>
|
||||
<!-- Row 10 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">공동주택용 50L</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
</tr>
|
||||
<!-- Row 11 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">공동주택용 120L</td>
|
||||
<td class="text-right pr-2">11</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
</tr>
|
||||
<!-- Row 12 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">재사용 봉투</td>
|
||||
<td class="text-right pr-2">58,540</td>
|
||||
<td class="text-right pr-2">27,000</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">27,000</td>
|
||||
<td class="text-right pr-2">560</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">560</td>
|
||||
<td class="text-right pr-2 bg-gray-50">560</td>
|
||||
<td class="text-right pr-2">84,990</td>
|
||||
</tr>
|
||||
<!-- Row 13 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 2L</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
</tr>
|
||||
<!-- Row 14 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 스티커 1L</td>
|
||||
<td class="text-right pr-2">376,758</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">100</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">100</td>
|
||||
<td class="text-right pr-2 bg-gray-50">180</td>
|
||||
<td class="text-right pr-2">376,658</td>
|
||||
</tr>
|
||||
<!-- Row 15 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 스티커 2L</td>
|
||||
<td class="text-right pr-2">231,542</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">100</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">100</td>
|
||||
<td class="text-right pr-2 bg-gray-50">100</td>
|
||||
<td class="text-right pr-2">231,422</td>
|
||||
</tr>
|
||||
<!-- Row 16 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 스티커 3L</td>
|
||||
<td class="text-right pr-2">529,938</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">1,200</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">1,200</td>
|
||||
<td class="text-right pr-2 bg-gray-50">1,200</td>
|
||||
<td class="text-right pr-2">529,738</td>
|
||||
</tr>
|
||||
<!-- Row 17 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 스티커 70L</td>
|
||||
<td class="text-right pr-2">751,036</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">1,400</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">1,400</td>
|
||||
<td class="text-right pr-2 bg-gray-50">1,400</td>
|
||||
<td class="text-right pr-2">750,030</td>
|
||||
</tr>
|
||||
<!-- Row 18 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 스티커 60L</td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">6</td>
|
||||
</tr>
|
||||
<!-- Row 19 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">음식물 스티커 120L</td>
|
||||
<td class="text-right pr-2">209,743</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">80</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">80</td>
|
||||
<td class="text-right pr-2 bg-gray-50">80</td>
|
||||
<td class="text-right pr-2">209,663</td>
|
||||
</tr>
|
||||
<!-- Row 20 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">폐기물 스티커 1,000원</td>
|
||||
<td class="text-right pr-2">161,676</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">300</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">300</td>
|
||||
<td class="text-right pr-2 bg-gray-50">300</td>
|
||||
<td class="text-right pr-2">161,376</td>
|
||||
</tr>
|
||||
<!-- Row 21 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">폐기물 스티커 3,000원</td>
|
||||
<td class="text-right pr-2">98,018</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">120</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">120</td>
|
||||
<td class="text-right pr-2 bg-gray-50">120</td>
|
||||
<td class="text-right pr-2">97,999</td>
|
||||
</tr>
|
||||
<!-- Row 22 -->
|
||||
<tr>
|
||||
<td class="text-left pl-2">폐기물 스티커 5,000원</td>
|
||||
<td class="text-right pr-2">61,631</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">40</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">40</td>
|
||||
<td class="text-right pr-2 bg-gray-50">40</td>
|
||||
<td class="text-right pr-2">61,591</td>
|
||||
</tr>
|
||||
<!-- Next Date Group Placeholder for later expansion or testing -->
|
||||
<tr>
|
||||
<td class="align-top text-center bg-white border-t-2 border-gray-400" rowspan="4">2024.01.03</td>
|
||||
<td class="text-left pl-2 border-t-2 border-gray-400">폐기물 스티커 10,000원</td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400">44,860</td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400"></td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400"></td>
|
||||
<td class="text-right pr-2 bg-gray-50 border-t-2 border-gray-400">0</td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400"></td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400"></td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400"></td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50 border-t-2 border-gray-400">0</td>
|
||||
<td class="text-right pr-2 border-t-2 border-gray-400">44,860</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 5L</td>
|
||||
<td class="text-right pr-2">187,240</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">187,240</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 10L</td>
|
||||
<td class="text-right pr-2">402,848</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">402,848</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-left pl-2">일반용 10L</td>
|
||||
<td class="text-right pr-2">402,365</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2"></td>
|
||||
<td class="text-right pr-2">0</td>
|
||||
<td class="text-right pr-2 bg-gray-50">0</td>
|
||||
<td class="text-right pr-2">402,365</td>
|
||||
</tr></tbody>
|
||||
<!-- END: Table Body -->
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
<!-- END: Main Content Area -->
|
||||
<!-- BEGIN: Footer -->
|
||||
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 flex items-center justify-between shrink-0">
|
||||
<div>Ready.....</div>
|
||||
<div class="flex gap-4">
|
||||
<span>북구</span>
|
||||
<span>Ver..</span>
|
||||
<span>2025.12.12 (금) 3:00:32PM</span>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- END: Footer -->
|
||||
</body>
|
||||
</html>
|
||||
347
app/Views/bag/inventory_inquiry.php
Normal file
347
app/Views/bag/inventory_inquiry.php
Normal file
@@ -0,0 +1,347 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<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 data-purpose="custom-styles">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans KR', sans-serif;
|
||||
background-color: #f1f5f9; /* Tailwind slate-100 */
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.custom-table th, .custom-table td {
|
||||
border: 1px solid #cbd5e1; /* Tailwind slate-300 */
|
||||
}
|
||||
.custom-table th {
|
||||
background-color: #eaeaea;
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.custom-table td {
|
||||
padding: 0.5rem;
|
||||
color: #334155; /* Tailwind slate-700 */
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.custom-table tbody tr:hover td {
|
||||
background-color: #f8fafc; /* Tailwind slate-50 */
|
||||
}
|
||||
|
||||
/* Scrollbar for table container */
|
||||
.table-container {
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Sidebar active state */
|
||||
.sidebar-link.active {
|
||||
background-color: #e0f0ff;
|
||||
color: #1e548a;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="h-screen flex flex-col overflow-hidden text-sm">
|
||||
<!-- BEGIN: Top Header -->
|
||||
<header class="bg-white border-b border-slate-200 h-14 flex items-center justify-between px-4 shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-md flex items-center justify-center text-white">
|
||||
<i class="fa-solid fa-leaf"></i>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold text-slate-800">스마트 폐기물 관리 시스템</h1>
|
||||
</div>
|
||||
<div>
|
||||
<button class="p-2 text-slate-500 hover:text-slate-700">
|
||||
<i class="fa-solid fa-arrow-right-from-bracket text-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<!-- END: Top Header -->
|
||||
|
||||
<div class="flex flex-1 overflow-hidden relative">
|
||||
<!-- BEGIN: Left Sidebar -->
|
||||
<aside class="w-20 bg-white border-r border-slate-200 flex flex-col items-center py-4 gap-2 shrink-0 overflow-y-auto">
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-border-all text-xl mb-1"></i>
|
||||
<span class="text-[10px]">기본정보관리</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-boxes-stacked text-xl mb-1"></i>
|
||||
<span class="text-[10px]">발주/입고 관리</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-truck-ramp-box text-xl mb-1"></i>
|
||||
<span class="text-[10px]">불출 관리</span>
|
||||
</a>
|
||||
<a class="sidebar-link active w-full flex flex-col items-center justify-center py-3 text-sky-700 transition-colors" href="#">
|
||||
<i class="fa-solid fa-warehouse text-xl mb-1"></i>
|
||||
<span class="text-[10px]">재고 관리</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-cart-shopping text-xl mb-1"></i>
|
||||
<span class="text-[10px]">판매 관리</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-chart-pie text-xl mb-1"></i>
|
||||
<span class="text-[10px]">판매 현황</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-chart-line text-xl mb-1"></i>
|
||||
<span class="text-[10px]">통계/분석 관리</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-solid fa-gear text-xl mb-1"></i>
|
||||
<span class="text-[10px]">설정</span>
|
||||
</a>
|
||||
<a class="sidebar-link w-full flex flex-col items-center justify-center py-3 text-slate-600 hover:bg-slate-50 transition-colors" href="#">
|
||||
<i class="fa-regular fa-circle-question text-xl mb-1"></i>
|
||||
<span class="text-[10px]">도움말</span>
|
||||
</a>
|
||||
<div class="mt-auto pt-4">
|
||||
<button class="p-2 text-slate-400 hover:text-slate-600">
|
||||
<i class="fa-solid fa-arrow-right-to-bracket text-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
<!-- END: Left Sidebar -->
|
||||
|
||||
<!-- BEGIN: Main Content Area -->
|
||||
<main class="flex-1 p-4 bg-slate-100 flex flex-col overflow-hidden min-w-0">
|
||||
<!-- BEGIN: Filter Bar -->
|
||||
<section class="bg-white border border-slate-200 rounded shadow-sm p-3 mb-4 flex flex-wrap items-center justify-between gap-4 shrink-0">
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-medium text-slate-700 whitespace-nowrap">조회기간:</label>
|
||||
<input class="border border-slate-300 rounded px-2 py-1.5 text-sm focus:ring-sky-500 focus:border-sky-500 w-52" readonly type="text" value="2024.01.01 ~ 2025.12.12"/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-medium text-slate-700 whitespace-nowrap">봉투구분:</label>
|
||||
<select class="border border-slate-300 rounded px-2 py-1.5 text-sm focus:ring-sky-500 focus:border-sky-500 w-24">
|
||||
<option>전체</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-medium text-slate-700 whitespace-nowrap">봉투형식:</label>
|
||||
<select class="border border-slate-300 rounded px-2 py-1.5 text-sm focus:ring-sky-500 focus:border-sky-500 w-28">
|
||||
<option>전체 봉투</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="font-medium text-slate-700 whitespace-nowrap">대행소:</label>
|
||||
<select class="border border-slate-300 rounded px-2 py-1.5 text-sm focus:ring-sky-500 focus:border-sky-500 w-24">
|
||||
<option>북구</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="bg-[#1e548a] hover:bg-blue-900 text-white px-4 py-1.5 rounded text-sm font-medium flex items-center gap-1 transition-colors">
|
||||
<i class="fa-solid fa-magnifying-glass"></i> 검색
|
||||
</button>
|
||||
<button class="bg-white border border-[#2e7d32] text-[#2e7d32] hover:bg-green-50 px-4 py-1.5 rounded text-sm font-medium flex items-center gap-1 transition-colors">
|
||||
<i class="fa-regular fa-file-excel"></i> Excel 내보내기
|
||||
</button>
|
||||
<button class="bg-white border border-slate-300 text-black hover:bg-slate-50 px-4 py-1.5 rounded text-sm font-medium flex items-center gap-1 transition-colors">
|
||||
<i class="fa-solid fa-print"></i> 인쇄
|
||||
</button>
|
||||
<button class="bg-[#e53935] hover:bg-red-600 text-white px-4 py-1.5 rounded text-sm font-medium flex items-center gap-1 transition-colors">
|
||||
<i class="fa-solid fa-power-off"></i> 종료
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<!-- END: Filter Bar -->
|
||||
|
||||
<!-- BEGIN: Data Table -->
|
||||
<section class="bg-white border border-slate-200 rounded shadow-sm flex-1 flex flex-col overflow-hidden">
|
||||
<div class="table-container flex-1 w-full overflow-auto">
|
||||
<table class="custom-table w-full whitespace-nowrap table-auto border-collapse">
|
||||
<thead class="sticky top-0 z-10 shadow-sm">
|
||||
<tr>
|
||||
<th class="w-48 sticky left-0 z-20" rowspan="2">품목</th>
|
||||
<th class="w-24" rowspan="2">전일재고</th>
|
||||
<th class="w-72" colspan="3">입고</th>
|
||||
<th class="w-96" colspan="5">출고</th>
|
||||
<th class="w-24" rowspan="2">잔량</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="w-24">입고</th>
|
||||
<th class="w-24">반품</th>
|
||||
<th class="w-24">입고계</th>
|
||||
<th class="w-24">판매</th>
|
||||
<th class="w-24 text-xs leading-tight">임의불출/무상불출</th>
|
||||
<th class="w-24">반품</th>
|
||||
<th class="w-24">기타</th>
|
||||
<th class="w-24">출고계</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 5L</td>
|
||||
<td class="text-right">187,240</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right font-medium">187,240</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 5L</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 10L</td>
|
||||
<td class="text-right">159,428</td>
|
||||
<td class="text-right">252,000</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">252,800</td>
|
||||
<td class="text-right">8,580</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">8,580</td>
|
||||
<td class="text-right font-medium">8,990</td>
|
||||
<td class="text-right font-medium">402,848</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 20L</td>
|
||||
<td class="text-right">212,082</td>
|
||||
<td class="text-right">201,000</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">201,600</td>
|
||||
<td class="text-right">11,320</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">11,320</td>
|
||||
<td class="text-right font-medium">11,320</td>
|
||||
<td class="text-right font-medium">402,355</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 50L</td>
|
||||
<td class="text-right">7,605</td>
|
||||
<td class="text-right">13,000</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">13,000</td>
|
||||
<td class="text-right">540</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">540</td>
|
||||
<td class="text-right font-medium">540</td>
|
||||
<td class="text-right font-medium">20,065</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 75L</td>
|
||||
<td class="text-right">31,459</td>
|
||||
<td class="text-right">22,600</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">22,600</td>
|
||||
<td class="text-right">2,990</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">2,090</td>
|
||||
<td class="text-right font-medium">3,640</td>
|
||||
<td class="text-right font-medium">86,240</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 100L</td>
|
||||
<td class="text-right">11</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right font-medium">11</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">일반용 70L</td>
|
||||
<td class="text-right">77,400</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">1,000</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">1,000</td>
|
||||
<td class="text-right font-medium">1,000</td>
|
||||
<td class="text-right font-medium">76,400</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">공동주택용 20L</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">공동주택용 50L</td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">0</td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
<td class="text-right font-medium">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sticky left-0 bg-white text-left font-medium z-10 shadow-[1px_0_0_#cbd5e1]">재사용 봉투</td>
|
||||
<td class="text-right">58,540</td>
|
||||
<td class="text-right">27,000</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right font-medium">27,000</td>
|
||||
<td class="text-right">560</td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right"></td>
|
||||
<td class="text-right">560</td>
|
||||
<td class="text-right font-medium">560</td>
|
||||
<td class="text-right font-medium">84,990</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<!-- END: Data Table -->
|
||||
</main>
|
||||
<!-- END: Main Content Area -->
|
||||
</div>
|
||||
|
||||
<!-- BEGIN: Footer / Status Bar -->
|
||||
<footer class="bg-[#e2e8f0] border-t border-slate-300 h-8 flex items-center justify-between px-4 shrink-0 text-xs text-slate-600">
|
||||
<div>Ready.....</div>
|
||||
<div class="flex gap-4">
|
||||
<span>북구</span>
|
||||
<span>Ver..</span>
|
||||
<span>2025.12.12 (금) 3:00:32PM</span>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- END: Footer / Status Bar -->
|
||||
</body>
|
||||
</html>
|
||||
282
app/Views/bag/lg_dashboard.php
Normal file
282
app/Views/bag/lg_dashboard.php
Normal file
@@ -0,0 +1,282 @@
|
||||
<?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/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>
|
||||
|
||||
<p class="text-[11px] text-gray-400 border-t border-gray-200 pt-3">
|
||||
차장님 요청 반영: <strong>봉투별 재고</strong>·<strong>구매신청 리스트</strong>·그래프 /
|
||||
추가 시안: <strong>발주·입고</strong>, <strong>승인 대기</strong>, <strong>수불 추이</strong>.
|
||||
레이아웃은 <strong>수불 엔터프라이즈 화면</strong>과 동일한 상단 메뉴·제목바 스타일입니다.
|
||||
</p>
|
||||
</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>
|
||||
429
app/Views/bag/lg_dashboard_charts.php
Normal file
429
app/Views/bag/lg_dashboard_charts.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
/**
|
||||
* 로그인 후 메인 — 그래프 중심 대시보드 (Chart.js 목업)
|
||||
* 상단 메뉴바는 lg_dashboard.php 와 동일
|
||||
*
|
||||
* @var string $lgLabel
|
||||
*/
|
||||
$lgLabel = $lgLabel ?? '북구';
|
||||
$mbName = session()->get('mb_name') ?? '담당자';
|
||||
$dashClassic = base_url('dashboard/classic-mock');
|
||||
$dashModern = base_url('dashboard/modern');
|
||||
$dashDense = base_url('dashboard/dense');
|
||||
$dashCharts = base_url('dashboard/charts');
|
||||
?>
|
||||
<!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"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||||
<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;
|
||||
}
|
||||
.chart-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0.375rem;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.04);
|
||||
}
|
||||
.chart-card h2 {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
background: #fafafa;
|
||||
}
|
||||
.chart-wrap { position: relative; height: 200px; padding: 0.5rem 0.75rem 0.75rem; }
|
||||
.chart-wrap.tall { height: 280px; }
|
||||
.chart-wrap.wide { height: 240px; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-[#eef1f6] 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-3 py-1.5 gap-3 flex-wrap">
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<div class="flex items-center gap-2 text-green-700 font-bold text-base">
|
||||
<i class="fa-solid fa-recycle text-lg"></i>
|
||||
<span>종량제 시스템</span>
|
||||
</div>
|
||||
<span class="hidden sm:inline text-[11px] text-gray-500 border-l border-gray-300 pl-2">
|
||||
<?= esc($lgLabel) ?> · <strong class="text-gray-700"><?= esc($mbName) ?></strong>님
|
||||
</span>
|
||||
</div>
|
||||
<nav class="nav-top hidden lg:flex flex-wrap items-center gap-3 xl:gap-4 text-[13px] font-medium text-gray-700">
|
||||
<a class="nav-active flex items-center gap-1 whitespace-nowrap" href="<?= esc($dashCharts) ?>">
|
||||
<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-1.5 shrink-0 text-[11px]">
|
||||
<a href="<?= esc($dashClassic) ?>" class="text-[#2b4c8c] hover:underline whitespace-nowrap hidden md:inline">클래식</a>
|
||||
<span class="text-gray-300 hidden md:inline">|</span>
|
||||
<a href="<?= esc($dashModern) ?>" class="text-[#2b4c8c] hover:underline whitespace-nowrap hidden md:inline">모던</a>
|
||||
<span class="text-gray-300 hidden md:inline">|</span>
|
||||
<a href="<?= esc($dashDense) ?>" class="text-[#2b4c8c] hover:underline whitespace-nowrap hidden md:inline">종합</a>
|
||||
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-gray-800 p-1 ml-1" title="로그아웃">
|
||||
<i class="fa-solid fa-arrow-right-from-bracket"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="bg-[#eff5fb] border-b border-gray-300 px-3 py-1.5 flex flex-wrap items-center justify-between gap-2 text-[12px] shrink-0">
|
||||
<span class="font-semibold text-gray-800">
|
||||
<i class="fa-solid fa-chart-column text-[#2b4c8c] mr-1"></i>통계·그래프 대시보드
|
||||
<span class="font-normal text-gray-500 ml-1">· 목업 데이터 · Chart.js</span>
|
||||
</span>
|
||||
<span class="text-gray-600 text-[11px]"><i class="fa-regular fa-calendar mr-0.5"></i><?= date('Y-m-d H:i') ?> · <?= esc($lgLabel) ?></span>
|
||||
</div>
|
||||
|
||||
<main class="flex-1 overflow-y-auto p-2 sm:p-3">
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mb-2 p-2 rounded border border-green-200 bg-green-50 text-green-800 text-[11px]"><?= esc(session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 1행: 도넛 2 + 막대 + 레이더 -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-2 mb-2">
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-chart-pie text-[#2b4c8c] mr-1"></i>봉투 규격 출고 비중</h2>
|
||||
<div class="chart-wrap"><canvas id="chDoughnutSpec"></canvas></div>
|
||||
</section>
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-circle-notch text-[#2b4c8c] mr-1"></i>구매신청 처리 단계</h2>
|
||||
<div class="chart-wrap"><canvas id="chDoughnutFlow"></canvas></div>
|
||||
</section>
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-calendar-week text-[#2b4c8c] mr-1"></i>금주 일별 출고(천장)</h2>
|
||||
<div class="chart-wrap"><canvas id="chBarWeek"></canvas></div>
|
||||
</section>
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-bullseye text-[#2b4c8c] mr-1"></i>운영 지표 (목업)</h2>
|
||||
<div class="chart-wrap"><canvas id="chRadar"></canvas></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 2행: 월별 추이 라인 -->
|
||||
<section class="chart-card mb-2">
|
||||
<h2><i class="fa-solid fa-chart-line text-[#2b4c8c] mr-1"></i>월별 출고 vs 구매신청 건수 (최근 12개월)</h2>
|
||||
<div class="chart-wrap tall"><canvas id="chLineYear"></canvas></div>
|
||||
</section>
|
||||
|
||||
<!-- 3행: 품목 막대 + 판매소 가로막대 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 mb-2">
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-boxes-stacked text-[#2b4c8c] mr-1"></i>품목별 재고 (천 장)</h2>
|
||||
<div class="chart-wrap wide"><canvas id="chBarSku"></canvas></div>
|
||||
</section>
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-store text-[#2b4c8c] mr-1"></i>판매소별 월 출고 TOP</h2>
|
||||
<div class="chart-wrap wide"><canvas id="chBarHStore"></canvas></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 4행: 스택 막대 -->
|
||||
<section class="chart-card mb-2">
|
||||
<h2><i class="fa-solid fa-layer-group text-[#2b4c8c] mr-1"></i>분기별 입고 / 출고 / 조정 (천 장)</h2>
|
||||
<div class="chart-wrap wide"><canvas id="chStackedQ"></canvas></div>
|
||||
</section>
|
||||
|
||||
<!-- 5행: 영역 + 극좌표 누적 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 mb-2">
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-chart-area text-[#2b4c8c] mr-1"></i>누적 출고 추이 (올해)</h2>
|
||||
<div class="chart-wrap"><canvas id="chAreaCum"></canvas></div>
|
||||
</section>
|
||||
<section class="chart-card">
|
||||
<h2><i class="fa-solid fa-compass text-[#2b4c8c] mr-1"></i>요일·시간대 신청 분포 (극좌표)</h2>
|
||||
<div class="chart-wrap"><canvas id="chPolarTime"></canvas></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-[10px] text-gray-400 py-1">
|
||||
<a href="<?= esc($dashClassic) ?>" class="text-[#2b4c8c] hover:underline">/dashboard</a>
|
||||
· <a href="<?= esc($dashModern) ?>" class="text-[#2b4c8c] hover:underline">/dashboard/modern</a>
|
||||
· <a href="<?= esc($dashDense) ?>" class="text-[#2b4c8c] hover:underline">/dashboard/dense</a>
|
||||
· <strong class="text-gray-600">/dashboard/charts</strong>
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const C = {
|
||||
primary: '#2b4c8c',
|
||||
blue: '#3b82f6',
|
||||
teal: '#0d9488',
|
||||
emerald: '#059669',
|
||||
amber: '#d97706',
|
||||
rose: '#e11d48',
|
||||
violet: '#7c3aed',
|
||||
slate: '#64748b',
|
||||
grid: 'rgba(0,0,0,.06)',
|
||||
};
|
||||
|
||||
Chart.defaults.font.family = "'Malgun Gothic','Apple SD Gothic Neo','Noto Sans KR',sans-serif";
|
||||
Chart.defaults.font.size = 11;
|
||||
Chart.defaults.color = '#4b5563';
|
||||
|
||||
const commonOpts = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'bottom', labels: { boxWidth: 10, padding: 8, font: { size: 10 } } },
|
||||
},
|
||||
};
|
||||
|
||||
const axisOpts = {
|
||||
scales: {
|
||||
x: { grid: { color: C.grid }, ticks: { maxRotation: 45, minRotation: 0, font: { size: 10 } } },
|
||||
y: { grid: { color: C.grid }, ticks: { font: { size: 10 } }, beginAtZero: true },
|
||||
},
|
||||
};
|
||||
|
||||
new Chart(document.getElementById('chDoughnutSpec'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['일반 5L', '일반 10·20L', '음식물 스티커', '재사용', '기타'],
|
||||
datasets: [{
|
||||
data: [38, 22, 28, 9, 3],
|
||||
backgroundColor: [C.primary, C.blue, C.teal, C.emerald, C.slate],
|
||||
borderWidth: 0,
|
||||
}],
|
||||
},
|
||||
options: { ...commonOpts, cutout: '58%' },
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chDoughnutFlow'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['접수', '검토', '발주', '납품', '완료'],
|
||||
datasets: [{
|
||||
data: [12, 8, 6, 5, 42],
|
||||
backgroundColor: [C.amber, C.blue, C.violet, C.teal, C.emerald],
|
||||
borderWidth: 0,
|
||||
}],
|
||||
},
|
||||
options: { ...commonOpts, cutout: '55%' },
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chBarWeek'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['월', '화', '수', '목', '금', '토', '일'],
|
||||
datasets: [{
|
||||
label: '출고',
|
||||
data: [42, 55, 48, 61, 58, 22, 8],
|
||||
backgroundColor: C.primary,
|
||||
borderRadius: 4,
|
||||
}],
|
||||
},
|
||||
options: { ...commonOpts, ...axisOpts, plugins: { ...commonOpts.plugins, legend: { display: false } } },
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chRadar'), {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: ['재고안정', '신청처리', '납기준수', '민원응대', '데이터품질'],
|
||||
datasets: [
|
||||
{
|
||||
label: '이번 달',
|
||||
data: [82, 76, 88, 71, 85],
|
||||
borderColor: C.primary,
|
||||
backgroundColor: 'rgba(43, 76, 140, 0.2)',
|
||||
pointBackgroundColor: C.primary,
|
||||
},
|
||||
{
|
||||
label: '전월',
|
||||
data: [78, 80, 84, 75, 80],
|
||||
borderColor: C.blue,
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.12)',
|
||||
pointBackgroundColor: C.blue,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOpts,
|
||||
scales: {
|
||||
r: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
ticks: { stepSize: 20, font: { size: 9 } },
|
||||
grid: { color: C.grid },
|
||||
pointLabels: { font: { size: 10 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chLineYear'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월', '1월', '2월'],
|
||||
datasets: [
|
||||
{
|
||||
label: '출고(천 장)',
|
||||
data: [320, 340, 310, 355, 380, 360, 370, 390, 400, 385, 410, 395],
|
||||
borderColor: C.primary,
|
||||
backgroundColor: 'rgba(43, 76, 140, 0.08)',
|
||||
fill: true,
|
||||
tension: 0.35,
|
||||
pointRadius: 3,
|
||||
},
|
||||
{
|
||||
label: '구매신청(건)',
|
||||
data: [118, 125, 112, 130, 142, 128, 135, 140, 155, 148, 160, 152],
|
||||
borderColor: C.teal,
|
||||
backgroundColor: 'transparent',
|
||||
tension: 0.35,
|
||||
yAxisID: 'y1',
|
||||
pointRadius: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOpts,
|
||||
...axisOpts,
|
||||
scales: {
|
||||
x: axisOpts.scales.x,
|
||||
y: { type: 'linear', position: 'left', grid: { color: C.grid }, title: { display: true, text: '출고', font: { size: 10 } } },
|
||||
y1: {
|
||||
type: 'linear',
|
||||
position: 'right',
|
||||
grid: { drawOnChartArea: false },
|
||||
title: { display: true, text: '건수', font: { size: 10 } },
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chBarSku'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['5L', '10L', '20L', '스티커', '재사용', '특수'],
|
||||
datasets: [{
|
||||
label: '재고',
|
||||
data: [12.4, 8.2, 2.1, 15.0, 4.3, 0.9],
|
||||
backgroundColor: [C.primary, C.blue, C.amber, C.teal, C.emerald, C.rose],
|
||||
borderRadius: 4,
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
...commonOpts,
|
||||
...axisOpts,
|
||||
indexAxis: 'x',
|
||||
plugins: { ...commonOpts.plugins, legend: { display: false } },
|
||||
},
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chBarHStore'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['행복마트 북구', '◇◇할인점', '□□마트', '○○슈퍼', '△△상회'],
|
||||
datasets: [{
|
||||
label: '천 장',
|
||||
data: [5.2, 4.8, 3.9, 3.5, 2.1],
|
||||
backgroundColor: C.primary,
|
||||
borderRadius: 4,
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
...commonOpts,
|
||||
indexAxis: 'y',
|
||||
plugins: { ...commonOpts.plugins, legend: { display: false } },
|
||||
scales: {
|
||||
x: { grid: { color: C.grid }, beginAtZero: true, ticks: { font: { size: 10 } } },
|
||||
y: { grid: { display: false }, ticks: { font: { size: 10 } } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chStackedQ'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['1분기', '2분기', '3분기', '4분기(예)'],
|
||||
datasets: [
|
||||
{ label: '입고', data: [420, 450, 480, 460], backgroundColor: C.emerald, stack: 's' },
|
||||
{ label: '출고', data: [380, 410, 440, 430], backgroundColor: C.primary, stack: 's' },
|
||||
{ label: '조정', data: [12, 8, 15, 10], backgroundColor: C.amber, stack: 's' },
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOpts,
|
||||
scales: {
|
||||
x: { stacked: true, grid: { display: false }, ticks: { font: { size: 10 } } },
|
||||
y: { stacked: true, grid: { color: C.grid }, ticks: { font: { size: 10 } }, beginAtZero: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chAreaCum'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
datasets: [{
|
||||
label: '누적 출고(만 장)',
|
||||
data: [3.2, 6.8, 10.5, 14.2, 18.0, 21.5, 25.1, 28.9, 32.4, 36.0, 39.8, 43.5],
|
||||
borderColor: C.blue,
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.25)',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
}],
|
||||
},
|
||||
options: { ...commonOpts, ...axisOpts, plugins: { ...commonOpts.plugins, legend: { display: true } } },
|
||||
});
|
||||
|
||||
new Chart(document.getElementById('chPolarTime'), {
|
||||
type: 'polarArea',
|
||||
data: {
|
||||
labels: ['평일 오전', '평일 오후', '평일 야간', '주말'],
|
||||
datasets: [{
|
||||
data: [28, 45, 8, 19],
|
||||
backgroundColor: [
|
||||
'rgba(43, 76, 140, 0.75)',
|
||||
'rgba(13, 148, 136, 0.7)',
|
||||
'rgba(217, 119, 6, 0.65)',
|
||||
'rgba(124, 58, 237, 0.65)',
|
||||
],
|
||||
borderWidth: 1,
|
||||
borderColor: '#fff',
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
...commonOpts,
|
||||
scales: {
|
||||
r: {
|
||||
ticks: { backdropColor: 'transparent', font: { size: 9 } },
|
||||
grid: { color: C.grid },
|
||||
pointLabels: { font: { size: 10 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
391
app/Views/bag/lg_dashboard_dense.php
Normal file
391
app/Views/bag/lg_dashboard_dense.php
Normal file
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
/**
|
||||
* 로그인 후 메인 — 정보 집약형 대시보드 (KPI·다중 표·알림·로그 한 화면)
|
||||
* 상단 메뉴바는 lg_dashboard.php 와 동일
|
||||
*
|
||||
* @var string $lgLabel
|
||||
*/
|
||||
$lgLabel = $lgLabel ?? '북구';
|
||||
$mbName = session()->get('mb_name') ?? '담당자';
|
||||
$dashClassic = base_url('dashboard/classic-mock');
|
||||
$dashModern = base_url('dashboard/modern');
|
||||
$dashDense = base_url('dashboard/dense');
|
||||
$dashCharts = base_url('dashboard/charts');
|
||||
|
||||
$kpiTop = [
|
||||
['icon' => 'fa-triangle-exclamation', 'c' => 'text-amber-700', 'bg' => 'bg-amber-50', 'v' => '3', 'l' => '재고부족', 'sub' => '품목'],
|
||||
['icon' => 'fa-cart-shopping', 'c' => 'text-sky-700', 'bg' => 'bg-sky-50', 'v' => '12', 'l' => '구매신청', 'sub' => '미처리'],
|
||||
['icon' => 'fa-truck', 'c' => 'text-emerald-700', 'bg' => 'bg-emerald-50', 'v' => '8', 'l' => '발주·입고', 'sub' => '금주'],
|
||||
['icon' => 'fa-user-clock', 'c' => 'text-violet-700', 'bg' => 'bg-violet-50', 'v' => '4', 'l' => '회원승인', 'sub' => '대기'],
|
||||
['icon' => 'fa-store', 'c' => 'text-rose-700', 'bg' => 'bg-rose-50', 'v' => '127', 'l' => '지정판매소', 'sub' => '등록'],
|
||||
['icon' => 'fa-boxes-stacked', 'c' => 'text-slate-700', 'bg' => 'bg-slate-100', 'v' => '48.2k', 'l' => '봉투재고', 'sub' => '장 합계'],
|
||||
['icon' => 'fa-file-invoice', 'c' => 'text-orange-700', 'bg' => 'bg-orange-50', 'v' => '6', 'l' => '세금계산서', 'sub' => '발행대기'],
|
||||
['icon' => 'fa-headset', 'c' => 'text-cyan-700', 'bg' => 'bg-cyan-50', 'v' => '2', 'l' => '민원·문의', 'sub' => '오늘'],
|
||||
];
|
||||
|
||||
$stockRows = [
|
||||
['일반 5L', '12,400', '안전', '3.2주'],
|
||||
['일반 10L', '8,200', '주의', '1.8주'],
|
||||
['일반 20L', '2,100', '부족', '0.6주'],
|
||||
['음식물 스티커', '15,000', '안전', '5.1주'],
|
||||
['재사용봉투', '4,300', '안전', '2.4주'],
|
||||
['특수규격 A', '890', '부족', '0.3주'],
|
||||
];
|
||||
|
||||
$orderRows = [
|
||||
['PO-2025-0218', '○○상사', '일반 5L×2박스', '발주확인', '02-26 10:20'],
|
||||
['PO-2025-0217', '△△유통', '스티커 500매', '납품중', '02-26 09:05'],
|
||||
['PO-2025-0216', '□□종량제', '20L 혼합', '입고완료', '02-25 16:40'],
|
||||
['REQ-8841', '행복마트 북구점', '5L 2,000장', '접수', '02-26 09:12'],
|
||||
['REQ-8839', '○○슈퍼', '스티커 500', '처리중', '02-26 08:45'],
|
||||
];
|
||||
|
||||
$logRows = [
|
||||
['10:42', 'system', '일일 재고 스냅샷 생성 완료'],
|
||||
['10:18', 'user', esc($mbName) . ' 로그인 (IP 마스킹)'],
|
||||
['09:55', 'batch', '구매신청 자동 분배 3건'],
|
||||
['09:30', 'admin', '판매소 코드 2건 갱신'],
|
||||
['08:12', 'api', '세금계산서 연동 응답 정상'],
|
||||
];
|
||||
|
||||
$storeSummary = [
|
||||
['행복마트 북구점', '42', '정상', '02-26'],
|
||||
['○○슈퍼', '38', '정상', '02-25'],
|
||||
['△△상회', '15', '연체1건', '02-20'],
|
||||
['□□마트', '29', '정상', '02-26'],
|
||||
['◇◇할인점', '51', '정상', '02-26'],
|
||||
];
|
||||
|
||||
$approvals = [
|
||||
['김○○', '판매소', '02-26', '서류검토'],
|
||||
['이○○', '일반', '02-25', '본인확인'],
|
||||
['박○○', '판매소', '02-25', '주소불일치'],
|
||||
];
|
||||
|
||||
$notices = [
|
||||
'2월 말 정기 재고 실사 안내 — 2/28 17:00 마감',
|
||||
'봉투 단가 조정 예고 — 3/1 적용 예정 (안내문 배포 완료)',
|
||||
];
|
||||
?>
|
||||
<!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: 12px;
|
||||
color: #333;
|
||||
}
|
||||
.nav-top a.nav-active {
|
||||
color: #2b4c8c;
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid #2b4c8c;
|
||||
padding-bottom: 2px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
.dense-table th, .dense-table td { padding: 0.25rem 0.4rem; line-height: 1.25; }
|
||||
.dense-table thead th { font-size: 11px; font-weight: 600; color: #555; background: #f3f4f6; border-bottom: 1px solid #d1d5db; }
|
||||
.dense-table tbody td { border-bottom: 1px solid #eee; font-size: 11px; }
|
||||
.spark { display: flex; align-items: flex-end; gap: 2px; height: 36px; }
|
||||
.spark span { flex: 1; background: linear-gradient(180deg, #3b82f6, #93c5fd); border-radius: 1px; min-width: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-[#f0f2f5] 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-3 py-1.5 gap-3 flex-wrap">
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<div class="flex items-center gap-2 text-green-700 font-bold text-base">
|
||||
<i class="fa-solid fa-recycle text-lg"></i>
|
||||
<span>종량제 시스템</span>
|
||||
</div>
|
||||
<span class="hidden sm:inline text-[11px] text-gray-500 border-l border-gray-300 pl-2">
|
||||
<?= esc($lgLabel) ?> · <strong class="text-gray-700"><?= esc($mbName) ?></strong>님
|
||||
</span>
|
||||
</div>
|
||||
<nav class="nav-top hidden lg:flex flex-wrap items-center gap-3 xl:gap-4 text-[13px] font-medium text-gray-700">
|
||||
<a class="nav-active flex items-center gap-1 whitespace-nowrap" href="<?= esc($dashDense) ?>">
|
||||
<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-1.5 shrink-0 text-[11px]">
|
||||
<a href="<?= esc($dashClassic) ?>" class="text-[#2b4c8c] hover:underline whitespace-nowrap hidden md:inline">클래식</a>
|
||||
<span class="text-gray-300 hidden md:inline">|</span>
|
||||
<a href="<?= esc($dashModern) ?>" class="text-[#2b4c8c] hover:underline whitespace-nowrap hidden md:inline">모던</a>
|
||||
<span class="text-gray-300 hidden md:inline">|</span>
|
||||
<a href="<?= esc($dashCharts) ?>" class="text-[#2b4c8c] hover:underline whitespace-nowrap hidden md:inline" title="그래프 대시보드">차트</a>
|
||||
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-gray-800 p-1 ml-1" title="로그아웃">
|
||||
<i class="fa-solid fa-arrow-right-from-bracket"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 컴팩트 컨텍스트 바 -->
|
||||
<div class="bg-[#eff5fb] border-b border-gray-300 px-3 py-1 flex flex-wrap items-center justify-between gap-2 text-[11px] shrink-0">
|
||||
<span class="font-semibold text-gray-800">
|
||||
<i class="fa-solid fa-table-columns text-[#2b4c8c] mr-1"></i>종합 운영 현황
|
||||
<span class="font-normal text-gray-500 ml-1">· KPI · 재고 · 발주/신청 · 로그 · 판매소</span>
|
||||
</span>
|
||||
<div class="flex flex-wrap items-center gap-2 text-gray-600">
|
||||
<span><i class="fa-regular fa-calendar mr-0.5"></i><?= date('Y-m-d (D) H:i') ?></span>
|
||||
<span class="text-gray-300">|</span>
|
||||
<span>기준지자체 <strong class="text-gray-800"><?= esc($lgLabel) ?></strong></span>
|
||||
<button type="button" class="bg-[#2b4c8c] text-white px-2 py-0.5 rounded text-[11px]"><i class="fa-solid fa-rotate mr-0.5"></i>새로고침</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="flex-1 overflow-y-auto p-2 sm:p-3">
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mb-2 p-2 rounded border border-green-200 bg-green-50 text-green-800 text-[11px]"><?= esc(session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 공지 티커 -->
|
||||
<div class="mb-2 flex flex-wrap gap-2">
|
||||
<?php foreach ($notices as $n): ?>
|
||||
<div class="flex-1 min-w-[200px] flex items-center gap-2 bg-amber-50 border border-amber-200 text-amber-900 px-2 py-1 rounded text-[11px]">
|
||||
<i class="fa-solid fa-bullhorn shrink-0"></i>
|
||||
<span class="truncate" title="<?= esc($n) ?>"><?= esc($n) ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- KPI 8칸 -->
|
||||
<div class="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-1.5 mb-2">
|
||||
<?php foreach ($kpiTop as $k): ?>
|
||||
<div class="bg-white border border-gray-200 rounded px-2 py-1.5 flex items-center gap-2 shadow-sm">
|
||||
<div class="w-8 h-8 rounded <?= $k['bg'] ?> <?= $k['c'] ?> flex items-center justify-center shrink-0 text-sm">
|
||||
<i class="fa-solid <?= esc($k['icon'], 'attr') ?>"></i>
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="text-base font-bold text-gray-900 leading-tight"><?= esc($k['v']) ?></div>
|
||||
<div class="text-[10px] text-gray-500 leading-tight"><?= esc($k['l']) ?></div>
|
||||
<div class="text-[9px] text-gray-400"><?= esc($k['sub']) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2 mb-2">
|
||||
<!-- 재고 품목 -->
|
||||
<section class="xl:col-span-4 bg-white border border-gray-200 rounded shadow-sm overflow-hidden">
|
||||
<div class="px-2 py-1 border-b border-gray-200 bg-gray-50 flex justify-between items-center">
|
||||
<h2 class="text-[11px] font-bold text-gray-800"><i class="fa-solid fa-warehouse text-[#2b4c8c] mr-1"></i>품목별 재고·소진예상</h2>
|
||||
<a href="<?= base_url('bag/inventory-inquiry') ?>" class="text-[10px] text-blue-600 hover:underline">상세</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto max-h-[220px] overflow-y-auto">
|
||||
<table class="w-full dense-table text-left">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>품목</th>
|
||||
<th class="text-right">재고(장)</th>
|
||||
<th>상태</th>
|
||||
<th class="text-right">소진</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stockRows as $r): ?>
|
||||
<tr>
|
||||
<td class="font-medium text-gray-800"><?= esc($r[0]) ?></td>
|
||||
<td class="text-right tabular-nums"><?= esc($r[1]) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$badge = match ($r[2]) {
|
||||
'안전' => 'bg-emerald-100 text-emerald-800',
|
||||
'주의' => 'bg-amber-100 text-amber-800',
|
||||
'부족' => 'bg-red-100 text-red-800',
|
||||
default => 'bg-gray-100 text-gray-700',
|
||||
};
|
||||
?>
|
||||
<span class="text-[10px] px-1 py-0 rounded <?= $badge ?>"><?= esc($r[2]) ?></span>
|
||||
</td>
|
||||
<td class="text-right text-gray-600"><?= esc($r[3]) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 발주·구매신청 -->
|
||||
<section class="xl:col-span-4 bg-white border border-gray-200 rounded shadow-sm overflow-hidden">
|
||||
<div class="px-2 py-1 border-b border-gray-200 bg-gray-50 flex justify-between items-center">
|
||||
<h2 class="text-[11px] font-bold text-gray-800"><i class="fa-solid fa-list-check text-[#2b4c8c] mr-1"></i>발주 / 구매신청 진행</h2>
|
||||
<span class="text-[10px] text-gray-500">최근 5건</span>
|
||||
</div>
|
||||
<div class="overflow-x-auto max-h-[220px] overflow-y-auto">
|
||||
<table class="w-full dense-table text-left">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>문서</th>
|
||||
<th>상대</th>
|
||||
<th>내용</th>
|
||||
<th>단계</th>
|
||||
<th class="text-right">시각</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($orderRows as $r): ?>
|
||||
<tr>
|
||||
<td class="text-blue-700 font-mono text-[10px]"><?= esc($r[0]) ?></td>
|
||||
<td class="truncate max-w-[4.5rem]" title="<?= esc($r[1]) ?>"><?= esc($r[1]) ?></td>
|
||||
<td class="truncate max-w-[5rem]" title="<?= esc($r[2]) ?>"><?= esc($r[2]) ?></td>
|
||||
<td><span class="text-[10px] bg-slate-100 px-1 rounded"><?= esc($r[3]) ?></span></td>
|
||||
<td class="text-right text-gray-500 text-[10px] whitespace-nowrap"><?= esc($r[4]) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 시스템 로그 + 주간 막대 -->
|
||||
<section class="xl:col-span-4 bg-white border border-gray-200 rounded shadow-sm overflow-hidden flex flex-col">
|
||||
<div class="px-2 py-1 border-b border-gray-200 bg-gray-50">
|
||||
<h2 class="text-[11px] font-bold text-gray-800"><i class="fa-solid fa-clock-rotate-left text-[#2b4c8c] mr-1"></i>최근 이벤트 로그</h2>
|
||||
</div>
|
||||
<ul class="flex-1 max-h-[140px] overflow-y-auto divide-y divide-gray-100 px-2 py-0.5">
|
||||
<?php foreach ($logRows as $L): ?>
|
||||
<li class="py-1 flex gap-2 text-[10px]">
|
||||
<span class="text-gray-400 font-mono shrink-0 w-8"><?= esc($L[0]) ?></span>
|
||||
<span class="shrink-0 w-12 text-center rounded bg-gray-100 text-gray-600"><?= esc($L[1]) ?></span>
|
||||
<span class="text-gray-700"><?= $L[2] ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<div class="border-t border-gray-100 px-2 py-1.5 bg-gray-50/80">
|
||||
<div class="text-[10px] font-semibold text-gray-600 mb-1">주간 봉투 출고(천 장, 목업)</div>
|
||||
<div class="spark" title="주간 추이">
|
||||
<?php foreach ([40, 55, 48, 62, 58, 71, 65] as $h): ?>
|
||||
<span style="height: <?= (int) $h ?>%"></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="flex justify-between text-[9px] text-gray-400 mt-0.5">
|
||||
<span>월</span><span>화</span><span>수</span><span>목</span><span>금</span><span>토</span><span>일</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 mb-2">
|
||||
<!-- 판매소 요약 -->
|
||||
<section class="bg-white border border-gray-200 rounded shadow-sm overflow-hidden">
|
||||
<div class="px-2 py-1 border-b border-gray-200 bg-gray-50 flex justify-between items-center">
|
||||
<h2 class="text-[11px] font-bold text-gray-800"><i class="fa-solid fa-store text-[#2b4c8c] mr-1"></i>지정판매소 요약</h2>
|
||||
<span class="text-[10px] text-gray-500">상위 5곳</span>
|
||||
</div>
|
||||
<table class="w-full dense-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>판매소명</th>
|
||||
<th class="text-right">월 봉투(백장)</th>
|
||||
<th>상태</th>
|
||||
<th class="text-right">최종거래</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($storeSummary as $s): ?>
|
||||
<tr>
|
||||
<td class="font-medium"><?= esc($s[0]) ?></td>
|
||||
<td class="text-right tabular-nums"><?= esc($s[1]) ?></td>
|
||||
<td>
|
||||
<?php if ($s[2] === '정상'): ?>
|
||||
<span class="text-[10px] text-emerald-700"><?= esc($s[2]) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="text-[10px] text-red-600"><?= esc($s[2]) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-right text-gray-500"><?= esc($s[3]) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- 승인 대기 + 요약 숫자 -->
|
||||
<section class="bg-white border border-gray-200 rounded shadow-sm overflow-hidden">
|
||||
<div class="px-2 py-1 border-b border-gray-200 bg-gray-50">
|
||||
<h2 class="text-[11px] font-bold text-gray-800"><i class="fa-solid fa-user-check text-[#2b4c8c] mr-1"></i>회원·판매소 승인 대기</h2>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-1 p-1.5 border-b border-gray-100 bg-gray-50/50 text-center">
|
||||
<div>
|
||||
<div class="text-lg font-bold text-[#2b4c8c]">4</div>
|
||||
<div class="text-[9px] text-gray-500">전체 대기</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-bold text-amber-600">2</div>
|
||||
<div class="text-[9px] text-gray-500">오늘 접수</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-bold text-gray-600">1.2일</div>
|
||||
<div class="text-[9px] text-gray-500">평균 처리</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="w-full dense-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>신청자</th>
|
||||
<th>유형</th>
|
||||
<th>접수일</th>
|
||||
<th>메모</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($approvals as $a): ?>
|
||||
<tr>
|
||||
<td><?= esc($a[0]) ?></td>
|
||||
<td><?= esc($a[1]) ?></td>
|
||||
<td class="text-gray-600"><?= esc($a[2]) ?></td>
|
||||
<td class="text-gray-500 truncate max-w-[6rem]"><?= esc($a[3]) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 하단: 비율 도넛 + 텍스트 브리핑 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
|
||||
<div class="bg-white border border-gray-200 rounded shadow-sm p-2 flex items-center gap-3">
|
||||
<div class="relative w-16 h-16 shrink-0 rounded-full" style="background: conic-gradient(#2b4c8c 0% 40%, #10b981 40% 70%, #f59e0b 70% 92%, #e5e7eb 92% 100%);">
|
||||
<div class="absolute inset-2 rounded-full bg-white flex items-center justify-center text-[10px] font-bold text-gray-700">비중</div>
|
||||
</div>
|
||||
<div class="text-[10px] text-gray-600 space-y-0.5">
|
||||
<div><span class="inline-block w-2 h-2 rounded-full bg-[#2b4c8c] mr-1"></span>일반 40%</div>
|
||||
<div><span class="inline-block w-2 h-2 rounded-full bg-emerald-500 mr-1"></span>스티커 30%</div>
|
||||
<div><span class="inline-block w-2 h-2 rounded-full bg-amber-500 mr-1"></span>대형/특수 22%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:col-span-2 bg-white border border-gray-200 rounded shadow-sm p-2">
|
||||
<h3 class="text-[11px] font-bold text-gray-800 mb-1"><i class="fa-solid fa-clipboard-list text-[#2b4c8c] mr-1"></i>운영 브리핑 (목업)</h3>
|
||||
<ul class="text-[11px] text-gray-600 space-y-0.5 list-disc list-inside">
|
||||
<li>다음 주 예상 구매신청 <strong class="text-gray-900">약 28건</strong> (전주 대비 +12%)</li>
|
||||
<li><strong class="text-red-700">일반 20L</strong>·<strong class="text-red-700">특수규격 A</strong> 발주 권고 — 안전재고 미달</li>
|
||||
<li>세금계산서 <strong>6건</strong> 미발행 — 담당 회계에 알림 발송됨</li>
|
||||
<li>지정판매소 <strong>△△상회</strong> 연체 1건 — 현장 점검 일정 3/3</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-[10px] text-gray-400 mt-2">
|
||||
레이아웃: <a href="<?= esc($dashClassic) ?>" class="text-[#2b4c8c] hover:underline">/dashboard</a>
|
||||
· <a href="<?= esc($dashModern) ?>" class="text-[#2b4c8c] hover:underline">/dashboard/modern</a>
|
||||
· <strong class="text-gray-600">/dashboard/dense</strong> (이 화면)
|
||||
· <a href="<?= esc($dashCharts) ?>" class="text-[#2b4c8c] hover:underline">/dashboard/charts</a>
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
215
app/Views/bag/lg_dashboard_modern.php
Normal file
215
app/Views/bag/lg_dashboard_modern.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* 로그인 후 메인 대시보드 — 모던 콘텐츠 레이아웃
|
||||
* 상단 메뉴바는 /dashboard(lg_dashboard.php)와 동일
|
||||
*
|
||||
* @var string $lgLabel
|
||||
*/
|
||||
$lgLabel = $lgLabel ?? '북구';
|
||||
$mbName = session()->get('mb_name') ?? '담당자';
|
||||
$dashClassic = base_url('dashboard/classic-mock');
|
||||
$dashModern = base_url('dashboard/modern');
|
||||
$dashDense = base_url('dashboard/dense');
|
||||
$dashCharts = base_url('dashboard/charts');
|
||||
?>
|
||||
<!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"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;600;700&display=swap" 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;
|
||||
}
|
||||
.stat-card { transition: transform .15s, box-shadow .15s; }
|
||||
.stat-card:hover { transform: translateY(-2px); box-shadow: 0 12px 24px rgba(15,23,42,.08); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 flex flex-col min-h-screen">
|
||||
<!-- 상단 메뉴: lg_dashboard.php 와 동일 구조·스타일 (현재 URL만 /dashboard/modern 반영) -->
|
||||
<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="<?= esc($dashModern) ?>">
|
||||
<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="<?= esc($dashClassic) ?>" class="text-xs text-[#2b4c8c] hover:underline whitespace-nowrap hidden sm:inline">클래식</a>
|
||||
<span class="text-gray-300 hidden sm:inline">|</span>
|
||||
<a href="<?= esc($dashDense) ?>" 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="<?= esc($dashCharts) ?>" 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 overflow-y-auto p-4 md:p-6">
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mb-4 p-3 rounded-xl bg-emerald-50 text-emerald-800 text-sm border border-emerald-100"><?= esc(session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 히어로 배너 -->
|
||||
<section class="rounded-2xl bg-gradient-to-r from-slate-800 via-slate-800 to-emerald-900 text-white p-6 md:p-8 mb-6 shadow-lg relative overflow-hidden">
|
||||
<div class="absolute top-0 right-0 w-64 h-64 bg-emerald-500/10 rounded-full blur-3xl -translate-y-1/2 translate-x-1/2 pointer-events-none"></div>
|
||||
<div class="relative">
|
||||
<p class="text-emerald-300/90 text-sm font-medium mb-1"><?= esc($lgLabel) ?></p>
|
||||
<h1 class="text-2xl md:text-3xl font-bold tracking-tight mb-2">안녕하세요, <?= esc($mbName) ?>님</h1>
|
||||
<p class="text-slate-300 text-sm max-w-xl">오늘의 재고·구매신청·발주 요약을 한눈에 확인하세요. 다른 레이아웃은 <a href="<?= esc($dashClassic) ?>" class="text-emerald-300 underline hover:text-white">가로 메뉴형 대시보드</a>에서 볼 수 있습니다.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- KPI 카드 -->
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 gap-3 md:gap-4 mb-6">
|
||||
<div class="stat-card rounded-2xl bg-white border border-slate-100 p-4 shadow-sm">
|
||||
<div class="w-10 h-10 rounded-xl bg-amber-100 text-amber-600 flex items-center justify-center mb-3"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||
<p class="text-2xl font-bold text-slate-900">3</p>
|
||||
<p class="text-xs text-slate-500 mt-1">재고 부족 품목</p>
|
||||
</div>
|
||||
<div class="stat-card rounded-2xl bg-white border border-slate-100 p-4 shadow-sm">
|
||||
<div class="w-10 h-10 rounded-xl bg-sky-100 text-sky-600 flex items-center justify-center mb-3"><i class="fa-solid fa-cart-arrow-down"></i></div>
|
||||
<p class="text-2xl font-bold text-slate-900">12</p>
|
||||
<p class="text-xs text-slate-500 mt-1">미처리 구매신청</p>
|
||||
</div>
|
||||
<div class="stat-card rounded-2xl bg-white border border-slate-100 p-4 shadow-sm">
|
||||
<div class="w-10 h-10 rounded-xl bg-emerald-100 text-emerald-600 flex items-center justify-center mb-3"><i class="fa-solid fa-truck-ramp-box"></i></div>
|
||||
<p class="text-2xl font-bold text-slate-900">8</p>
|
||||
<p class="text-xs text-slate-500 mt-1">이번 주 발주·입고</p>
|
||||
</div>
|
||||
<div class="stat-card rounded-2xl bg-white border border-slate-100 p-4 shadow-sm">
|
||||
<div class="w-10 h-10 rounded-xl bg-violet-100 text-violet-600 flex items-center justify-center mb-3"><i class="fa-solid fa-user-clock"></i></div>
|
||||
<p class="text-2xl font-bold text-slate-900">4</p>
|
||||
<p class="text-xs text-slate-500 mt-1">승인 대기 회원</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
|
||||
<!-- 링 차트 느낌 (CSS) -->
|
||||
<div class="lg:col-span-1 rounded-2xl bg-white border border-slate-100 p-5 shadow-sm">
|
||||
<h3 class="font-semibold text-slate-800 mb-4 text-sm">봉투 재고 비중</h3>
|
||||
<div class="flex items-center justify-center gap-6">
|
||||
<div class="relative w-36 h-36 rounded-full" style="background: conic-gradient(#10b981 0% 35%, #3b82f6 35% 62%, #f59e0b 62% 88%, #e2e8f0 88% 100%);">
|
||||
<div class="absolute inset-3 rounded-full bg-white flex flex-col items-center justify-center text-center">
|
||||
<span class="text-lg font-bold text-slate-800">100%</span>
|
||||
<span class="text-[10px] text-slate-500">목업</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="text-xs space-y-2 text-slate-600">
|
||||
<li class="flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-emerald-500"></span> 일반용</li>
|
||||
<li class="flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-blue-500"></span> 스티커</li>
|
||||
<li class="flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-amber-500"></span> 재사용</li>
|
||||
<li class="flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-slate-200"></span> 기타</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 미니 타임라인 스타일 구매신청 -->
|
||||
<div class="lg:col-span-2 rounded-2xl bg-white border border-slate-100 p-5 shadow-sm">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="font-semibold text-slate-800 text-sm">지정판매소 구매신청</h3>
|
||||
<button type="button" class="text-xs font-medium text-emerald-600 hover:text-emerald-800">전체</button>
|
||||
</div>
|
||||
<ul class="space-y-3">
|
||||
<?php
|
||||
$timeline = [
|
||||
['행복마트 북구점', '일반용 5L · 2,000장', '09:12', '접수'],
|
||||
['○○슈퍼', '음식물 스티커 · 500장', '08:45', '처리중'],
|
||||
['△△상회', '일반용 20L · 박스 3', '어제', '완료'],
|
||||
];
|
||||
foreach ($timeline as $t):
|
||||
$dot = $t[3] === '완료' ? 'bg-emerald-500' : ($t[3] === '처리중' ? 'bg-amber-400' : 'bg-slate-400');
|
||||
?>
|
||||
<li class="flex gap-3 text-sm">
|
||||
<span class="w-2 shrink-0 mt-1.5"><span class="block w-2 h-2 rounded-full <?= $dot ?>"></span></span>
|
||||
<div class="flex-1 min-w-0 border-b border-slate-50 pb-3">
|
||||
<div class="flex justify-between gap-2">
|
||||
<span class="font-medium text-slate-800 truncate"><?= esc($t[0]) ?></span>
|
||||
<span class="text-xs text-slate-400 shrink-0"><?= esc($t[2]) ?></span>
|
||||
</div>
|
||||
<p class="text-xs text-slate-500 mt-0.5"><?= esc($t[1]) ?></p>
|
||||
<span class="inline-block mt-1 text-[10px] px-2 py-0.5 rounded-md bg-slate-100 text-slate-600"><?= esc($t[3]) ?></span>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 표 형태 요약 (카드 안) -->
|
||||
<div class="rounded-2xl bg-white border border-slate-100 shadow-sm overflow-hidden mb-4">
|
||||
<div class="px-5 py-3 border-b border-slate-100 bg-slate-50/80">
|
||||
<h3 class="font-semibold text-slate-800 text-sm">최근 신청 상세</h3>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="text-left text-xs text-slate-500 border-b border-slate-100">
|
||||
<th class="px-5 py-3 font-medium">일시</th>
|
||||
<th class="px-5 py-3 font-medium">판매소</th>
|
||||
<th class="px-5 py-3 font-medium">품목</th>
|
||||
<th class="px-5 py-3 font-medium text-right">수량</th>
|
||||
<th class="px-5 py-3 font-medium text-center">상태</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-50">
|
||||
<tr class="hover:bg-slate-50/80">
|
||||
<td class="px-5 py-3 text-slate-600">2025-02-26 09:12</td>
|
||||
<td class="px-5 py-3">행복마트 북구점</td>
|
||||
<td class="px-5 py-3">일반용 5L</td>
|
||||
<td class="px-5 py-3 text-right tabular-nums">2,000장</td>
|
||||
<td class="px-5 py-3 text-center"><span class="text-xs px-2 py-1 rounded-lg bg-slate-100">접수</span></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-slate-50/80">
|
||||
<td class="px-5 py-3 text-slate-600">2025-02-26 08:45</td>
|
||||
<td class="px-5 py-3">○○슈퍼</td>
|
||||
<td class="px-5 py-3">음식물 스티커</td>
|
||||
<td class="px-5 py-3 text-right tabular-nums">500장</td>
|
||||
<td class="px-5 py-3 text-center"><span class="text-xs px-2 py-1 rounded-lg bg-amber-50 text-amber-800">처리중</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-[11px] text-slate-400">
|
||||
URL 비교 — <strong class="text-slate-600">클래식 레이아웃</strong> <code class="bg-slate-100 px-1 rounded">/dashboard</code>
|
||||
· <strong class="text-slate-600">모던 콘텐츠(이 화면)</strong> <code class="bg-slate-100 px-1 rounded">/dashboard/modern</code>
|
||||
· <a href="<?= esc($dashCharts) ?>" class="text-[#2b4c8c] hover:underline">/dashboard/charts</a>
|
||||
<span class="block sm:inline mt-1 sm:mt-0">· 상단 메뉴는 동일</span>
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
446
app/Views/bag/waste_suibal_enterprise.php
Normal file
446
app/Views/bag/waste_suibal_enterprise.php
Normal file
@@ -0,0 +1,446 @@
|
||||
<!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 data-purpose="base-typography">
|
||||
body {
|
||||
font-family: 'Malgun Gothic', 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
<style data-purpose="table-styling">
|
||||
.data-grid {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
min-width: 1200px;
|
||||
}
|
||||
.data-grid th, .data-grid td {
|
||||
border: 1px solid #d1d5db;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
.data-grid th {
|
||||
background-color: #f3f4f6;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.data-grid td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.data-grid tbody tr:hover {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
.col-number { text-align: right; }
|
||||
.col-center { text-align: center; }
|
||||
.data-grid thead th.border-b-0 { border-bottom: 1px solid #f3f4f6; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white flex flex-col min-h-screen">
|
||||
<header class="border-b border-gray-300 bg-white shadow-sm" 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-2 text-green-700 font-bold text-lg shrink-0">
|
||||
<i class="fa-solid fa-recycle text-xl"></i>
|
||||
<span>종량제 시스템</span>
|
||||
</div>
|
||||
<nav class="hidden lg:flex flex-wrap items-center gap-4 xl:gap-6 text-sm font-medium text-gray-700">
|
||||
<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="#"><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') ?>" class="text-xs text-gray-500 hover:text-blue-600 hidden sm:inline">대시보드</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-grow flex flex-col min-h-0" data-purpose="dashboard-content">
|
||||
<div class="bg-[#eff5fb] border-b border-gray-300 px-4 py-2 flex justify-between items-center text-sm font-semibold text-gray-800" data-purpose="page-title">
|
||||
<span><i class="fa-solid fa-table-list text-[#2b4c8c] mr-2"></i>봉투 수불 현황 <span class="text-xs font-normal text-gray-500">(기간·품목별)</span></span>
|
||||
<button type="button" class="text-gray-500 hover:text-gray-800" title="필터"><i class="fa-solid fa-filter"></i></button>
|
||||
</div>
|
||||
|
||||
<section class="p-2 border-b border-gray-300 bg-white" data-purpose="search-filters">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-3 flex-wrap">
|
||||
<div class="flex items-center gap-1">
|
||||
<label class="text-gray-600 font-medium whitespace-nowrap">조회기간</label>
|
||||
<input class="border border-gray-300 px-2 py-1 text-xs w-24 rounded shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500" type="text" value="2024.01.01" readonly>
|
||||
<span>~</span>
|
||||
<input class="border border-gray-300 px-2 py-1 text-xs w-24 rounded shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500" type="text" value="2025.12.12" readonly>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-gray-600 font-medium whitespace-nowrap">봉투구분</label>
|
||||
<select class="border border-gray-300 px-2 py-1 text-xs rounded shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 w-28">
|
||||
<option>전체</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-gray-600 font-medium whitespace-nowrap">봉투형식</label>
|
||||
<select class="border border-gray-300 px-2 py-1 text-xs rounded shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 w-32">
|
||||
<option>전체 봉투</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-gray-600 font-medium whitespace-nowrap">대행소</label>
|
||||
<select class="border border-gray-300 px-2 py-1 text-xs rounded shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 w-28">
|
||||
<option>북구</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" class="bg-[#2b4c8c] hover:bg-blue-800 text-white px-4 py-1.5 rounded text-sm font-medium shadow flex items-center gap-1">
|
||||
<i class="fa-solid fa-magnifying-glass"></i> 검색
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<button type="button" class="bg-[#3b8c45] hover:bg-green-700 text-white px-3 py-1.5 rounded text-sm font-medium shadow flex items-center gap-1">
|
||||
<i class="fa-solid fa-file-excel"></i> 엑셀 저장
|
||||
</button>
|
||||
<button type="button" class="bg-[#5a6268] hover:bg-gray-700 text-white px-3 py-1.5 rounded text-sm font-medium shadow flex items-center gap-1">
|
||||
<i class="fa-solid fa-print"></i> 인쇄
|
||||
</button>
|
||||
<button type="button" class="bg-[#dc3545] hover:bg-red-700 text-white px-3 py-1.5 rounded text-sm font-medium shadow flex items-center gap-1">
|
||||
<i class="fa-solid fa-power-off"></i> 닫기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="flex-grow overflow-auto bg-gray-50 p-2" data-purpose="data-table-container">
|
||||
<table class="data-grid bg-white shadow-sm" id="inventory-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">일자</th>
|
||||
<th rowspan="2">품목</th>
|
||||
<th rowspan="2">전월재고</th>
|
||||
<th class="border-b-0" colspan="3">입고</th>
|
||||
<th rowspan="2">입고<br><span class="text-xs font-normal text-gray-500">소계</span></th>
|
||||
<th rowspan="2">출고<br><span class="text-xs font-normal text-gray-500">소계</span></th>
|
||||
<th class="border-b-0" colspan="3">출고</th>
|
||||
<th rowspan="2">잔량</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>입고</th>
|
||||
<th>반품</th>
|
||||
<th>기타</th>
|
||||
<th>판매</th>
|
||||
<th>기타</th>
|
||||
<th>합계</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="col-center align-top bg-white font-medium" rowspan="18">2024.01.01</td>
|
||||
<td>일반형 5L</td>
|
||||
<td class="col-number">187,240</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">187,240</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 5L</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 10L</td>
|
||||
<td class="col-number">159,428</td>
|
||||
<td class="col-number">252,000</td>
|
||||
<td class="col-number">252,000</td>
|
||||
<td class="col-number">8,580</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">8,580</td>
|
||||
<td class="col-number">8,990</td>
|
||||
<td class="col-number font-bold bg-[#1cae9e] text-white">402,248</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 20L</td>
|
||||
<td class="col-number">212,082</td>
|
||||
<td class="col-number">201,000</td>
|
||||
<td class="col-number">201,600</td>
|
||||
<td class="col-number">11,320</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">11,320</td>
|
||||
<td class="col-number">11,320</td>
|
||||
<td class="col-number font-bold bg-[#1cae9e] text-white">402,365</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 50L</td>
|
||||
<td class="col-number">7,605</td>
|
||||
<td class="col-number">13,000</td>
|
||||
<td class="col-number">13,000</td>
|
||||
<td class="col-number">540</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">540</td>
|
||||
<td class="col-number">540</td>
|
||||
<td class="col-number font-bold bg-[#e87a27] text-white">20,065</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 75L</td>
|
||||
<td class="col-number">31,459</td>
|
||||
<td class="col-number">22,600</td>
|
||||
<td class="col-number">22,600</td>
|
||||
<td class="col-number">2,990</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">2,090</td>
|
||||
<td class="col-number">3,640</td>
|
||||
<td class="col-number font-bold bg-[#e87a27] text-white">86,240</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 100L</td>
|
||||
<td class="col-number">11</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">11</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>할인용 70L</td>
|
||||
<td class="col-number">77,400</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">1,000</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">1,000</td>
|
||||
<td class="col-number">1,000</td>
|
||||
<td class="col-number font-bold">76,400</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>공공용(매립) 20L</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>공공용(매립) 50L</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>재사용 백</td>
|
||||
<td class="col-number">58,540</td>
|
||||
<td class="col-number">27,000</td>
|
||||
<td class="col-number">27,000</td>
|
||||
<td class="col-number">560</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">560</td>
|
||||
<td class="col-number">560</td>
|
||||
<td class="col-number font-bold bg-[#1cae9e] text-white">84,990</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>음식물 2L</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>음식물 스티커 1L</td>
|
||||
<td class="col-number">376,758</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">100</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">100</td>
|
||||
<td class="col-number">180</td>
|
||||
<td class="col-number font-bold">376,658</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>음식물 스티커 2L</td>
|
||||
<td class="col-number">231,542</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">100</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">100</td>
|
||||
<td class="col-number">100</td>
|
||||
<td class="col-number font-bold">231,422</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>음식물 스티커 3L</td>
|
||||
<td class="col-number">529,938</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">1,200</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">1,200</td>
|
||||
<td class="col-number">1,200</td>
|
||||
<td class="col-number font-bold">529,738</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>음식물 스티커 70L</td>
|
||||
<td class="col-number">751,036</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">1,400</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">1,400</td>
|
||||
<td class="col-number">1,400</td>
|
||||
<td class="col-number font-bold">750,030</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>대형용 스티커 120L</td>
|
||||
<td class="col-number">209,743</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">80</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">80</td>
|
||||
<td class="col-number">80</td>
|
||||
<td class="col-number font-bold">209,663</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>폐기물 스티커 1,000원</td>
|
||||
<td class="col-number">161,676</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">300</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">300</td>
|
||||
<td class="col-number">300</td>
|
||||
<td class="col-number font-bold">161,376</td>
|
||||
</tr>
|
||||
<tr class="border-t-2 border-gray-400">
|
||||
<td class="col-center align-top bg-white font-medium" rowspan="4">2024.01.03</td>
|
||||
<td>폐기물 스티커 10,000원</td>
|
||||
<td class="col-number">44,860</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">44,860</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 5L</td>
|
||||
<td class="col-number">187,240</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">187,240</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 10L</td>
|
||||
<td class="col-number">402,848</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">402,848</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>일반형 20L</td>
|
||||
<td class="col-number">402,365</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number"></td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number">0</td>
|
||||
<td class="col-number font-bold">402,365</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</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>준비됨</div>
|
||||
<div class="flex gap-4">
|
||||
<span>북구</span>
|
||||
<span>Ver. 목업</span>
|
||||
<span><?= date('Y.m.d (D) g:i A') ?></span>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
7
app/Views/errors/cli/error_404.php
Normal file
7
app/Views/errors/cli/error_404.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
CLI::error('ERROR: ' . $code);
|
||||
CLI::write($message);
|
||||
CLI::newLine();
|
||||
65
app/Views/errors/cli/error_exception.php
Normal file
65
app/Views/errors/cli/error_exception.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
// The main Exception
|
||||
CLI::write('[' . $exception::class . ']', 'light_gray', 'red');
|
||||
CLI::write($message);
|
||||
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green'));
|
||||
CLI::newLine();
|
||||
|
||||
$last = $exception;
|
||||
|
||||
while ($prevException = $last->getPrevious()) {
|
||||
$last = $prevException;
|
||||
|
||||
CLI::write(' Caused by:');
|
||||
CLI::write(' [' . $prevException::class . ']', 'red');
|
||||
CLI::write(' ' . $prevException->getMessage());
|
||||
CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green'));
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
// The backtrace
|
||||
if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
|
||||
$backtraces = $last->getTrace();
|
||||
|
||||
if ($backtraces) {
|
||||
CLI::write('Backtrace:', 'green');
|
||||
}
|
||||
|
||||
foreach ($backtraces as $i => $error) {
|
||||
$padFile = ' '; // 4 spaces
|
||||
$padClass = ' '; // 7 spaces
|
||||
$c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT);
|
||||
|
||||
if (isset($error['file'])) {
|
||||
$filepath = clean_path($error['file']) . ':' . $error['line'];
|
||||
|
||||
CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
|
||||
} else {
|
||||
CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'));
|
||||
}
|
||||
|
||||
$function = '';
|
||||
|
||||
if (isset($error['class'])) {
|
||||
$type = ($error['type'] === '->') ? '()' . $error['type'] : $error['type'];
|
||||
$function .= $padClass . $error['class'] . $type . $error['function'];
|
||||
} elseif (! isset($error['class']) && isset($error['function'])) {
|
||||
$function .= $padClass . $error['function'];
|
||||
}
|
||||
|
||||
$args = implode(', ', array_map(static fn ($value): string => match (true) {
|
||||
is_object($value) => 'Object(' . $value::class . ')',
|
||||
is_array($value) => $value !== [] ? '[...]' : '[]',
|
||||
$value === null => 'null', // return the lowercased version
|
||||
default => var_export($value, true),
|
||||
}, array_values($error['args'] ?? [])));
|
||||
|
||||
$function .= '(' . $args . ')';
|
||||
|
||||
CLI::write($function);
|
||||
CLI::newLine();
|
||||
}
|
||||
}
|
||||
5
app/Views/errors/cli/production.php
Normal file
5
app/Views/errors/cli/production.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
// On the CLI, we still want errors in productions
|
||||
// so just use the exception template.
|
||||
include __DIR__ . '/error_exception.php';
|
||||
194
app/Views/errors/html/debug.css
Normal file
194
app/Views/errors/html/debug.css
Normal file
@@ -0,0 +1,194 @@
|
||||
:root {
|
||||
--main-bg-color: #fff;
|
||||
--main-text-color: #555;
|
||||
--dark-text-color: #222;
|
||||
--light-text-color: #c7c7c7;
|
||||
--brand-primary-color: #DC4814;
|
||||
--light-bg-color: #ededee;
|
||||
--dark-bg-color: #404040;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
background: var(--main-bg-color);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
color: var(--main-text-color);
|
||||
font-weight: 300;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
h1 {
|
||||
font-weight: lighter;
|
||||
font-size: 3rem;
|
||||
color: var(--dark-text-color);
|
||||
margin: 0;
|
||||
}
|
||||
h1.headline {
|
||||
margin-top: 20%;
|
||||
font-size: 5rem;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
p.lead {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
.container {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
.header {
|
||||
background: var(--light-bg-color);
|
||||
color: var(--dark-text-color);
|
||||
margin-top: 2.17rem;
|
||||
}
|
||||
.header .container {
|
||||
padding: 1rem;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.header p {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
line-height: 2.5;
|
||||
}
|
||||
.header a {
|
||||
color: var(--brand-primary-color);
|
||||
margin-left: 2rem;
|
||||
display: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.header:hover a {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.environment {
|
||||
background: var(--brand-primary-color);
|
||||
color: var(--main-bg-color);
|
||||
text-align: center;
|
||||
padding: calc(4px + 0.2083vw);
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.source {
|
||||
background: #343434;
|
||||
color: var(--light-text-color);
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 5px;
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.source span.line {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.source span.line .number {
|
||||
color: #666;
|
||||
}
|
||||
.source .line .highlight {
|
||||
display: block;
|
||||
background: var(--dark-text-color);
|
||||
color: var(--light-text-color);
|
||||
}
|
||||
.source span.highlight .number {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
list-style: none;
|
||||
list-style-position: inside;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.tabs li {
|
||||
display: inline;
|
||||
}
|
||||
.tabs a:link,
|
||||
.tabs a:visited {
|
||||
padding: 0 1rem;
|
||||
line-height: 2.7;
|
||||
text-decoration: none;
|
||||
color: var(--dark-text-color);
|
||||
background: var(--light-bg-color);
|
||||
border: 1px solid rgba(0,0,0,0.15);
|
||||
border-bottom: 0;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
.tabs a:hover {
|
||||
background: var(--light-bg-color);
|
||||
border-color: rgba(0,0,0,0.15);
|
||||
}
|
||||
.tabs a.active {
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
.tab-content {
|
||||
background: var(--main-bg-color);
|
||||
border: 1px solid rgba(0,0,0,0.15);
|
||||
}
|
||||
.content {
|
||||
padding: 1rem;
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-top: 2rem;
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 3.0;
|
||||
background: #d9edf7;
|
||||
border: 1px solid #bcdff1;
|
||||
border-radius: 5px;
|
||||
color: #31708f;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
td {
|
||||
padding: 0.2rem 0.5rem 0.2rem 0;
|
||||
}
|
||||
tr:hover td {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
td pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.trace a {
|
||||
color: inherit;
|
||||
}
|
||||
.trace table {
|
||||
width: auto;
|
||||
}
|
||||
.trace tr td:first-child {
|
||||
min-width: 5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.trace td {
|
||||
background: var(--light-bg-color);
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.trace td pre {
|
||||
margin: 0;
|
||||
}
|
||||
.args {
|
||||
display: none;
|
||||
}
|
||||
116
app/Views/errors/html/debug.js
Normal file
116
app/Views/errors/html/debug.js
Normal file
@@ -0,0 +1,116 @@
|
||||
var tabLinks = new Array();
|
||||
var contentDivs = new Array();
|
||||
|
||||
function init()
|
||||
{
|
||||
// Grab the tab links and content divs from the page
|
||||
var tabListItems = document.getElementById('tabs').childNodes;
|
||||
console.log(tabListItems);
|
||||
for (var i = 0; i < tabListItems.length; i ++)
|
||||
{
|
||||
if (tabListItems[i].nodeName == "LI")
|
||||
{
|
||||
var tabLink = getFirstChildWithTagName(tabListItems[i], 'A');
|
||||
var id = getHash(tabLink.getAttribute('href'));
|
||||
tabLinks[id] = tabLink;
|
||||
contentDivs[id] = document.getElementById(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign onclick events to the tab links, and
|
||||
// highlight the first tab
|
||||
var i = 0;
|
||||
|
||||
for (var id in tabLinks)
|
||||
{
|
||||
tabLinks[id].onclick = showTab;
|
||||
tabLinks[id].onfocus = function () {
|
||||
this.blur()
|
||||
};
|
||||
if (i == 0)
|
||||
{
|
||||
tabLinks[id].className = 'active';
|
||||
}
|
||||
i ++;
|
||||
}
|
||||
|
||||
// Hide all content divs except the first
|
||||
var i = 0;
|
||||
|
||||
for (var id in contentDivs)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
console.log(contentDivs[id]);
|
||||
contentDivs[id].className = 'content hide';
|
||||
}
|
||||
i ++;
|
||||
}
|
||||
}
|
||||
|
||||
function showTab()
|
||||
{
|
||||
var selectedId = getHash(this.getAttribute('href'));
|
||||
|
||||
// Highlight the selected tab, and dim all others.
|
||||
// Also show the selected content div, and hide all others.
|
||||
for (var id in contentDivs)
|
||||
{
|
||||
if (id == selectedId)
|
||||
{
|
||||
tabLinks[id].className = 'active';
|
||||
contentDivs[id].className = 'content';
|
||||
}
|
||||
else
|
||||
{
|
||||
tabLinks[id].className = '';
|
||||
contentDivs[id].className = 'content hide';
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the browser following the link
|
||||
return false;
|
||||
}
|
||||
|
||||
function getFirstChildWithTagName(element, tagName)
|
||||
{
|
||||
for (var i = 0; i < element.childNodes.length; i ++)
|
||||
{
|
||||
if (element.childNodes[i].nodeName == tagName)
|
||||
{
|
||||
return element.childNodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHash(url)
|
||||
{
|
||||
var hashPos = url.lastIndexOf('#');
|
||||
return url.substring(hashPos + 1);
|
||||
}
|
||||
|
||||
function toggle(elem)
|
||||
{
|
||||
elem = document.getElementById(elem);
|
||||
|
||||
if (elem.style && elem.style['display'])
|
||||
{
|
||||
// Only works with the "style" attr
|
||||
var disp = elem.style['display'];
|
||||
}
|
||||
else if (elem.currentStyle)
|
||||
{
|
||||
// For MSIE, naturally
|
||||
var disp = elem.currentStyle['display'];
|
||||
}
|
||||
else if (window.getComputedStyle)
|
||||
{
|
||||
// For most other browsers
|
||||
var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display');
|
||||
}
|
||||
|
||||
// Toggle the state of the "display" style
|
||||
elem.style.display = disp == 'block' ? 'none' : 'block';
|
||||
|
||||
return false;
|
||||
}
|
||||
84
app/Views/errors/html/error_400.php
Normal file
84
app/Views/errors/html/error_400.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= lang('Errors.badRequest') ?></title>
|
||||
|
||||
<style>
|
||||
div.logo {
|
||||
height: 200px;
|
||||
width: 155px;
|
||||
display: inline-block;
|
||||
opacity: 0.08;
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
left: 50%;
|
||||
margin-left: -73px;
|
||||
}
|
||||
body {
|
||||
height: 100%;
|
||||
background: #fafafa;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #777;
|
||||
font-weight: 300;
|
||||
}
|
||||
h1 {
|
||||
font-weight: lighter;
|
||||
letter-spacing: normal;
|
||||
font-size: 3rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: #222;
|
||||
}
|
||||
.wrap {
|
||||
max-width: 1024px;
|
||||
margin: 5rem auto;
|
||||
padding: 2rem;
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
border: 1px solid #efefef;
|
||||
border-radius: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
pre {
|
||||
white-space: normal;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
code {
|
||||
background: #fafafa;
|
||||
border: 1px solid #efefef;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
border-top: 1px solid #efefef;
|
||||
padding: 1em 2em 0 2em;
|
||||
font-size: 85%;
|
||||
color: #999;
|
||||
}
|
||||
a:active,
|
||||
a:link,
|
||||
a:visited {
|
||||
color: #dd4814;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>400</h1>
|
||||
|
||||
<p>
|
||||
<?php if (ENVIRONMENT !== 'production') : ?>
|
||||
<?= nl2br(esc($message)) ?>
|
||||
<?php else : ?>
|
||||
<?= lang('Errors.sorryBadRequest') ?>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
84
app/Views/errors/html/error_404.php
Normal file
84
app/Views/errors/html/error_404.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= lang('Errors.pageNotFound') ?></title>
|
||||
|
||||
<style>
|
||||
div.logo {
|
||||
height: 200px;
|
||||
width: 155px;
|
||||
display: inline-block;
|
||||
opacity: 0.08;
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
left: 50%;
|
||||
margin-left: -73px;
|
||||
}
|
||||
body {
|
||||
height: 100%;
|
||||
background: #fafafa;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #777;
|
||||
font-weight: 300;
|
||||
}
|
||||
h1 {
|
||||
font-weight: lighter;
|
||||
letter-spacing: normal;
|
||||
font-size: 3rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: #222;
|
||||
}
|
||||
.wrap {
|
||||
max-width: 1024px;
|
||||
margin: 5rem auto;
|
||||
padding: 2rem;
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
border: 1px solid #efefef;
|
||||
border-radius: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
pre {
|
||||
white-space: normal;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
code {
|
||||
background: #fafafa;
|
||||
border: 1px solid #efefef;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
border-top: 1px solid #efefef;
|
||||
padding: 1em 2em 0 2em;
|
||||
font-size: 85%;
|
||||
color: #999;
|
||||
}
|
||||
a:active,
|
||||
a:link,
|
||||
a:visited {
|
||||
color: #dd4814;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>404</h1>
|
||||
|
||||
<p>
|
||||
<?php if (ENVIRONMENT !== 'production') : ?>
|
||||
<?= nl2br(esc($message)) ?>
|
||||
<?php else : ?>
|
||||
<?= lang('Errors.sorryCannotFind') ?>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
429
app/Views/errors/html/error_exception.php
Normal file
429
app/Views/errors/html/error_exception.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
use CodeIgniter\HTTP\Header;
|
||||
use CodeIgniter\CodeIgniter;
|
||||
|
||||
$errorId = uniqid('error', true);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<title><?= esc($title) ?></title>
|
||||
<style>
|
||||
<?= preg_replace('#[\r\n\t ]+#', ' ', file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')) ?>
|
||||
</style>
|
||||
|
||||
<script>
|
||||
<?= file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.js') ?>
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
<div class="environment">
|
||||
Displayed at <?= esc(date('H:i:s')) ?> —
|
||||
PHP: <?= esc(PHP_VERSION) ?> —
|
||||
CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?> --
|
||||
Environment: <?= ENVIRONMENT ?>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1><?= esc($title), esc($exception->getCode() ? ' #' . $exception->getCode() : '') ?></h1>
|
||||
<p>
|
||||
<?= nl2br(esc($exception->getMessage())) ?>
|
||||
<a href="https://www.duckduckgo.com/?q=<?= urlencode($title . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $exception->getMessage())) ?>"
|
||||
rel="noreferrer" target="_blank">search →</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Source -->
|
||||
<div class="container">
|
||||
<p><b><?= esc(clean_path($file)) ?></b> at line <b><?= esc($line) ?></b></p>
|
||||
|
||||
<?php if (is_file($file)) : ?>
|
||||
<div class="source">
|
||||
<?= static::highlightFile($file, $line, 15); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<?php
|
||||
$last = $exception;
|
||||
|
||||
while ($prevException = $last->getPrevious()) {
|
||||
$last = $prevException;
|
||||
?>
|
||||
|
||||
<pre>
|
||||
Caused by:
|
||||
<?= esc($prevException::class), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
|
||||
|
||||
<?= nl2br(esc($prevException->getMessage())) ?>
|
||||
<a href="https://www.duckduckgo.com/?q=<?= urlencode($prevException::class . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $prevException->getMessage())) ?>"
|
||||
rel="noreferrer" target="_blank">search →</a>
|
||||
<?= esc(clean_path($prevException->getFile()) . ':' . $prevException->getLine()) ?>
|
||||
</pre>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) : ?>
|
||||
<div class="container">
|
||||
|
||||
<ul class="tabs" id="tabs">
|
||||
<li><a href="#backtrace">Backtrace</a></li>
|
||||
<li><a href="#server">Server</a></li>
|
||||
<li><a href="#request">Request</a></li>
|
||||
<li><a href="#response">Response</a></li>
|
||||
<li><a href="#files">Files</a></li>
|
||||
<li><a href="#memory">Memory</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- Backtrace -->
|
||||
<div class="content" id="backtrace">
|
||||
|
||||
<ol class="trace">
|
||||
<?php foreach ($trace as $index => $row) : ?>
|
||||
|
||||
<li>
|
||||
<p>
|
||||
<!-- Trace info -->
|
||||
<?php if (isset($row['file']) && is_file($row['file'])) : ?>
|
||||
<?php
|
||||
if (isset($row['function']) && in_array($row['function'], ['include', 'include_once', 'require', 'require_once'], true)) {
|
||||
echo esc($row['function'] . ' ' . clean_path($row['file']));
|
||||
} else {
|
||||
echo esc(clean_path($row['file']) . ' : ' . $row['line']);
|
||||
}
|
||||
?>
|
||||
<?php else: ?>
|
||||
{PHP internal code}
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Class/Method -->
|
||||
<?php if (isset($row['class'])) : ?>
|
||||
— <?= esc($row['class'] . $row['type'] . $row['function']) ?>
|
||||
<?php if (! empty($row['args'])) : ?>
|
||||
<?php $argsId = $errorId . 'args' . $index ?>
|
||||
( <a href="#" onclick="return toggle('<?= esc($argsId, 'attr') ?>');">arguments</a> )
|
||||
<div class="args" id="<?= esc($argsId, 'attr') ?>">
|
||||
<table cellspacing="0">
|
||||
|
||||
<?php
|
||||
$params = null;
|
||||
// Reflection by name is not available for closure function
|
||||
if (! str_ends_with($row['function'], '}')) {
|
||||
$mirror = isset($row['class']) ? new ReflectionMethod($row['class'], $row['function']) : new ReflectionFunction($row['function']);
|
||||
$params = $mirror->getParameters();
|
||||
}
|
||||
|
||||
foreach ($row['args'] as $key => $value) : ?>
|
||||
<tr>
|
||||
<td><code><?= esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?></code></td>
|
||||
<td><pre><?= esc(print_r($value, true)) ?></pre></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
()
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (! isset($row['class']) && isset($row['function'])) : ?>
|
||||
— <?= esc($row['function']) ?>()
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<!-- Source? -->
|
||||
<?php if (isset($row['file']) && is_file($row['file']) && isset($row['class'])) : ?>
|
||||
<div class="source">
|
||||
<?= static::highlightFile($row['file'], $row['line']) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Server -->
|
||||
<div class="content" id="server">
|
||||
<?php foreach (['_SERVER', '_SESSION'] as $var) : ?>
|
||||
<?php
|
||||
if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
|
||||
continue;
|
||||
} ?>
|
||||
|
||||
<h3>$<?= esc($var) ?></h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($GLOBALS[$var] as $key => $value) : ?>
|
||||
<tr>
|
||||
<td><?= esc($key) ?></td>
|
||||
<td>
|
||||
<?php if (is_string($value)) : ?>
|
||||
<?= esc($value) ?>
|
||||
<?php else: ?>
|
||||
<pre><?= esc(print_r($value, true)) ?></pre>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php endforeach ?>
|
||||
|
||||
<!-- Constants -->
|
||||
<?php $constants = get_defined_constants(true); ?>
|
||||
<?php if (! empty($constants['user'])) : ?>
|
||||
<h3>Constants</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($constants['user'] as $key => $value) : ?>
|
||||
<tr>
|
||||
<td><?= esc($key) ?></td>
|
||||
<td>
|
||||
<?php if (is_string($value)) : ?>
|
||||
<?= esc($value) ?>
|
||||
<?php else: ?>
|
||||
<pre><?= esc(print_r($value, true)) ?></pre>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Request -->
|
||||
<div class="content" id="request">
|
||||
<?php $request = service('request'); ?>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 10em">Path</td>
|
||||
<td><?= esc($request->getUri()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HTTP Method</td>
|
||||
<td><?= esc($request->getMethod()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP Address</td>
|
||||
<td><?= esc($request->getIPAddress()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 10em">Is AJAX Request?</td>
|
||||
<td><?= $request->isAJAX() ? 'yes' : 'no' ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Is CLI Request?</td>
|
||||
<td><?= $request->isCLI() ? 'yes' : 'no' ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Is Secure Request?</td>
|
||||
<td><?= $request->isSecure() ? 'yes' : 'no' ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User Agent</td>
|
||||
<td><?= esc($request->getUserAgent()->getAgentString()) ?></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<?php $empty = true; ?>
|
||||
<?php foreach (['_GET', '_POST', '_COOKIE'] as $var) : ?>
|
||||
<?php
|
||||
if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
|
||||
continue;
|
||||
} ?>
|
||||
|
||||
<?php $empty = false; ?>
|
||||
|
||||
<h3>$<?= esc($var) ?></h3>
|
||||
|
||||
<table style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($GLOBALS[$var] as $key => $value) : ?>
|
||||
<tr>
|
||||
<td><?= esc($key) ?></td>
|
||||
<td>
|
||||
<?php if (is_string($value)) : ?>
|
||||
<?= esc($value) ?>
|
||||
<?php else: ?>
|
||||
<pre><?= esc(print_r($value, true)) ?></pre>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if ($empty) : ?>
|
||||
|
||||
<div class="alert">
|
||||
No $_GET, $_POST, or $_COOKIE Information to show.
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $headers = $request->headers(); ?>
|
||||
<?php if (! empty($headers)) : ?>
|
||||
|
||||
<h3>Headers</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($headers as $name => $value) : ?>
|
||||
<tr>
|
||||
<td><?= esc($name, 'html') ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ($value instanceof Header) {
|
||||
echo esc($value->getValueLine(), 'html');
|
||||
} else {
|
||||
foreach ($value as $i => $header) {
|
||||
echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
|
||||
}
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Response -->
|
||||
<?php
|
||||
$response = service('response');
|
||||
$response->setStatusCode(http_response_code());
|
||||
?>
|
||||
<div class="content" id="response">
|
||||
<table>
|
||||
<tr>
|
||||
<td style="width: 15em">Response Status</td>
|
||||
<td><?= esc($response->getStatusCode() . ' - ' . $response->getReasonPhrase()) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php $headers = $response->headers(); ?>
|
||||
<?php if (! empty($headers)) : ?>
|
||||
<h3>Headers</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($headers as $name => $value) : ?>
|
||||
<tr>
|
||||
<td><?= esc($name, 'html') ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ($value instanceof Header) {
|
||||
echo esc($response->getHeaderLine($name), 'html');
|
||||
} else {
|
||||
foreach ($value as $i => $header) {
|
||||
echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
|
||||
}
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Files -->
|
||||
<div class="content" id="files">
|
||||
<?php $files = get_included_files(); ?>
|
||||
|
||||
<ol>
|
||||
<?php foreach ($files as $file) :?>
|
||||
<li><?= esc(clean_path($file)) ?></li>
|
||||
<?php endforeach ?>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- Memory -->
|
||||
<div class="content" id="memory">
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Memory Usage</td>
|
||||
<td><?= esc(static::describeMemory(memory_get_usage(true))) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 12em">Peak Memory Usage:</td>
|
||||
<td><?= esc(static::describeMemory(memory_get_peak_usage(true))) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Memory Limit:</td>
|
||||
<td><?= esc(ini_get('memory_limit')) ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div> <!-- /tab-content -->
|
||||
|
||||
</div> <!-- /container -->
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
25
app/Views/errors/html/production.php
Normal file
25
app/Views/errors/html/production.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<title><?= lang('Errors.whoops') ?></title>
|
||||
|
||||
<style>
|
||||
<?= preg_replace('#[\r\n\t ]+#', ' ', file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')) ?>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container text-center">
|
||||
|
||||
<h1 class="headline"><?= lang('Errors.whoops') ?></h1>
|
||||
|
||||
<p class="lead"><?= lang('Errors.weHitASnag') ?></p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
56
app/Views/home/dashboard.php
Normal file
56
app/Views/home/dashboard.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>홈 - 쓰레기봉투 물류시스템</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"/>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: { sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
|
||||
colors: {
|
||||
'title-bar': '#2c3e50',
|
||||
'control-panel': '#f8f9fa',
|
||||
'btn-search': '#1c4e80',
|
||||
'btn-exit': '#d9534f',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased">
|
||||
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 flex items-center justify-center shrink-0">
|
||||
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
|
||||
</div>
|
||||
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-red-600 transition-colors inline-block p-1 rounded hover:bg-red-50" title="로그아웃">
|
||||
<svg class="h-5 w-5 inline" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/></svg> 종료
|
||||
</a>
|
||||
</header>
|
||||
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
|
||||
홈
|
||||
</div>
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="mx-4 mt-2 p-3 rounded-lg bg-green-50 text-green-700 text-sm" role="alert"><?= esc(session()->getFlashdata('success')) ?></div>
|
||||
<?php endif; ?>
|
||||
<main class="flex-grow bg-control-panel border-b border-gray-300 p-6">
|
||||
<section class="max-w-md bg-white border border-gray-300 rounded shadow-sm p-6">
|
||||
<p class="text-sm font-bold text-gray-700 mb-2"><?= esc(session()->get('mb_name')) ?>님, 환영합니다.</p>
|
||||
<p class="text-sm text-gray-600 mb-4">아이디: <?= esc(session()->get('mb_id')) ?></p>
|
||||
<p class="text-sm text-gray-600 mb-4">역할: <?= esc(config('Roles')->getLevelName((int) session()->get('mb_level'))) ?></p>
|
||||
<a href="<?= base_url('logout') ?>" class="inline-block bg-btn-exit text-white px-4 py-2 rounded-sm text-sm shadow hover:bg-red-700 transition">로그아웃</a>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 shrink-0">쓰레기봉투 물류시스템</footer>
|
||||
</body>
|
||||
</html>
|
||||
56
app/Views/welcome_message.php
Normal file
56
app/Views/welcome_message.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>쓰레기봉투 물류시스템</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"/>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: { sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
|
||||
colors: {
|
||||
'system-header': '#ffffff',
|
||||
'title-bar': '#2c3e50',
|
||||
'control-panel': '#f8f9fa',
|
||||
'btn-search': '#1c4e80',
|
||||
'btn-exit': '#d9534f',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased">
|
||||
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 flex items-center justify-center shrink-0">
|
||||
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
|
||||
</div>
|
||||
<nav class="flex gap-4 text-sm font-medium text-gray-600">
|
||||
<a class="hover:text-blue-600" href="<?= base_url('login') ?>">로그인</a>
|
||||
<a class="hover:text-blue-600" href="<?= base_url('register') ?>">회원가입</a>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
|
||||
쓰레기봉투 물류시스템
|
||||
</div>
|
||||
<main class="flex-grow bg-control-panel border-b border-gray-300 p-6 flex items-center justify-center">
|
||||
<section class="text-center max-w-lg">
|
||||
<p class="text-gray-700 mb-6">로그인 후 이용해 주세요.</p>
|
||||
<div class="flex gap-2 justify-center">
|
||||
<a href="<?= base_url('login') ?>" class="bg-btn-search text-white px-4 py-2 rounded-sm text-sm font-medium shadow hover:opacity-90 transition">로그인</a>
|
||||
<a href="<?= base_url('register') ?>" class="bg-white text-gray-700 border border-gray-300 px-4 py-2 rounded-sm text-sm shadow hover:bg-gray-50 transition">회원가입</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 shrink-0">쓰레기봉투 물류시스템</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user