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>
|
||||
Reference in New Issue
Block a user