5 Commits

Author SHA1 Message Date
taekyoungc
71534fb198 feat: 인쇄 A4 맞춤 + 재고현황 실사버튼 제거
- 회원관리·승인대기 표를 인쇄 시 A4 너비에 맞게(줄바꿈·관리 열 제외)
- 재고현황의 '실사 선별 조회' 버튼 제거(소메뉴로 이동 가능)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:31:56 +09:00
taekyoungc
e6a49e379a style: 표 카드 모서리 둥글게(overflow-hidden) 일괄 적용
헤더 바(bg-gray-50)가 둥근 테두리를 뚫고 나오던 카드 컨테이너에 overflow-hidden 추가(12개 파일).
order_change: 중복 플래시 출력도 함께 제거.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:31:46 +09:00
taekyoungc
77c7d3e119 fix: 플래시 메시지 중복 표시 제거
레이아웃(embed/portal)에서 이미 success/error 를 표시하므로 뷰 안의 중복 출력 제거.
일괄입고·스캐너입고·전화주문 화면에서 메시지가 한 번만 표시됨.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:31:38 +09:00
taekyoungc
b9dd24082c feat: 화면설명 소제목 스크롤·강조 + 글씨크기 메뉴 확대 + 드로어 개선
- screenHelp 앵커(?hl=)로 '이 화면 설명' 클릭 시 해당 소제목으로 스크롤·강조, 재오픈 시 재강조(postMessage)
- 글씨 크기(A−/A+)가 상단 대메뉴·좌측 사이드바까지 확대, 관리자 페이지에도 조절 기능 추가
- 화면 설명 드로어 양방향 리사이즈(좁히기 가능) + 기본 너비 2배

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:31:31 +09:00
taekyoungc
56dadb3478 fix: 워크스페이스 탭 중첩 셸 방지 + 세션 만료 시 로그인 리다이렉트
- EmbedRedirectFilter 추가: 임베드(embed=1) 요청의 리다이렉트 Location에 embed 유지(중첩 셸 방지)
- bag/* 전체에 loginAuth 적용, 임베드 대시보드 로그아웃 시 로그인으로 이동
- 기본코드 종류 선택 시 embed 유지, 일괄입고 오류 복귀를 명시 URL로(back() 제거)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:31:22 +09:00
30 changed files with 296 additions and 117 deletions

View File

@@ -27,6 +27,7 @@ class Filters extends BaseFilters
public array $aliases = [ public array $aliases = [
'adminAuth' => \App\Filters\AdminAuthFilter::class, 'adminAuth' => \App\Filters\AdminAuthFilter::class,
'loginAuth' => \App\Filters\LoginAuthFilter::class, 'loginAuth' => \App\Filters\LoginAuthFilter::class,
'embedRedirect' => \App\Filters\EmbedRedirectFilter::class,
'csrf' => CSRF::class, 'csrf' => CSRF::class,
'toolbar' => DebugToolbar::class, 'toolbar' => DebugToolbar::class,
'honeypot' => Honeypot::class, 'honeypot' => Honeypot::class,
@@ -81,6 +82,7 @@ class Filters extends BaseFilters
'after' => [ 'after' => [
// 'honeypot', // 'honeypot',
// 'secureheaders', // 'secureheaders',
'embedRedirect', // 임베드(탭) 리다이렉트에 embed=1 유지 → 중첩 셸 방지
], ],
]; ];
@@ -108,5 +110,10 @@ class Filters extends BaseFilters
* *
* @var array<string, array<string, list<string>>> * @var array<string, array<string, list<string>>>
*/ */
public array $filters = []; public array $filters = [
// 모든 업무(bag) 화면은 로그인 필요. 세션 만료 후 어떤 버튼을 눌러도
// 깨진 화면 대신 로그인으로 리다이렉트되도록 일괄 보호한다.
// (login/logout/register 는 bag/* 가 아니므로 영향 없음. 관리자 화면은 adminAuth 가 별도 처리)
'loginAuth' => ['before' => ['bag', 'bag/*']],
];
} }

View File

@@ -40,34 +40,37 @@ class Manual extends BaseConfig
* "이 화면 설명" 버튼이 현재 경로로 알맞은 매뉴얼 페이지를 연다. * "이 화면 설명" 버튼이 현재 경로로 알맞은 매뉴얼 페이지를 연다.
* 더 긴(구체적) 접두가 우선하도록 길이 내림차순으로 매칭한다. * 더 긴(구체적) 접두가 우선하도록 길이 내림차순으로 매칭한다.
* *
* 값에 "slug#소제목힌트" 형식으로 적으면, 매뉴얼 창이 열릴 때 해당 소제목으로
* 자동 스크롤하고 잠시 강조 표시한다(힌트는 그 페이지의 H2/H3 텍스트 일부와 일치).
*
* @var array<string, string> * @var array<string, string>
*/ */
public array $screenHelp = [ public array $screenHelp = [
'bag/order/phone' => 'sales', 'bag/order/phone' => 'sales#전화 주문 접수',
'bag/order' => 'order', 'bag/order' => 'order#발주 등록',
'bag/bag-orders' => 'order', 'bag/bag-orders' => 'order#발주 현황',
'bag/receiving' => 'order', 'bag/receiving' => 'order#입고 처리',
'bag/bag-receivings' => 'order', 'bag/bag-receivings' => 'order#입고 현황',
'bag/inventory' => 'inventory', 'bag/inventory' => 'inventory#재고 현황',
'bag/sale' => 'sales', 'bag/sale' => 'sales#지정판매소 판매',
'bag/sales' => 'sales', 'bag/sales' => 'sales#지정판매소 판매',
'bag/issue' => 'sales', 'bag/issue' => 'sales#무료용 불출 처리',
'bag/bag-issues' => 'sales', 'bag/bag-issues' => 'sales#무료용 불출 처리',
'bag/bag-sales' => 'sales', 'bag/bag-sales' => 'sales#판매/반품 현황',
'bag/shop-orders' => 'sales', 'bag/shop-orders' => 'sales#전화 주문 접수',
'bag/flow' => 'reports', 'bag/flow' => 'reports',
'bag/reports' => 'reports', 'bag/reports' => 'reports#일계표',
'bag/analytics' => 'reports', 'bag/analytics' => 'reports',
'bag/designated-shops' => 'basic', 'bag/designated-shops' => 'basic#지정판매소 관리',
'bag/bag-prices' => 'basic', 'bag/bag-prices' => 'basic#단가 관리',
'bag/prices' => 'basic', 'bag/prices' => 'basic#단가 관리',
'bag/packaging-units' => 'basic', 'bag/packaging-units' => 'basic#포장 단위 관리',
'bag/code-kinds' => 'basic', 'bag/code-kinds' => 'basic#기본코드 관리',
'bag/code-details' => 'basic', 'bag/code-details' => 'basic#기본코드 관리',
'bag/managers' => 'basic', 'bag/managers' => 'basic#그 밖의 기본정보',
'bag/companies' => 'basic', 'bag/companies' => 'basic#그 밖의 기본정보',
'bag/sales-agencies' => 'basic', 'bag/sales-agencies' => 'basic#그 밖의 기본정보',
'bag/free-recipients' => 'basic', 'bag/free-recipients' => 'basic#그 밖의 기본정보',
'bag/number-lookup' => 'codes', 'bag/number-lookup' => 'codes',
]; ];
} }

View File

@@ -592,6 +592,7 @@ class Bag extends BaseController
'selectedKind' => $selectedKind, 'selectedKind' => $selectedKind,
'detailList' => $detailList, 'detailList' => $detailList,
'rowCanEdit' => $rowCanEdit, 'rowCanEdit' => $rowCanEdit,
'isEmbed' => $this->isEmbeddedRequest(), // 행 클릭 시 embed 유지(중첩 셸 방지)
], true); // 본문이 이미 카드 2개라 바깥 래퍼 생략 ], true); // 본문이 이미 카드 2개라 바깥 래퍼 생략
} }
@@ -4768,14 +4769,18 @@ SQL);
$receiverIdx = $this->parseReceiverRefToStoredIdx($lgIdx, $receiverRef); $receiverIdx = $this->parseReceiverRefToStoredIdx($lgIdx, $receiverRef);
$senderIdx = (int) ($this->request->getPost('br_sender_idx') ?? 0); $senderIdx = (int) ($this->request->getPost('br_sender_idx') ?? 0);
// 오류 시 명시적으로 일괄 입고 화면으로 복귀(back()은 도움말 드로어 등이 남긴
// previous_url 로 잘못 이동할 수 있어 사용하지 않는다)
$batchUrl = site_url('bag/receiving/batch?company_idx=' . $companyIdx . '&bag_code=' . rawurlencode($bagCode));
if (empty($selected)) { if (empty($selected)) {
return redirect()->back()->withInput()->with('error', '일괄 입고할 행을 선택해 주세요.'); return redirect()->to($batchUrl)->withInput()->with('error', '일괄 입고할 행을 선택해 주세요.');
} }
if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $receiveDate)) { if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $receiveDate)) {
return redirect()->back()->withInput()->with('error', '입고일 형식을 확인해 주세요.'); return redirect()->to($batchUrl)->withInput()->with('error', '입고일 형식을 확인해 주세요.');
} }
if ($receiverIdx <= 0) { if ($receiverIdx <= 0) {
return redirect()->back()->withInput()->with('error', '인수자를 선택해 주세요.'); return redirect()->to($batchUrl)->withInput()->with('error', '인수자를 선택해 주세요.');
} }
$senderResolved = $this->resolveCompanySenderName($lgIdx, $senderIdx); $senderResolved = $this->resolveCompanySenderName($lgIdx, $senderIdx);
@@ -4816,7 +4821,7 @@ SQL);
} }
if (empty($insertRows)) { if (empty($insertRows)) {
return redirect()->back()->withInput()->with('error', '선택한 행에 입고할 수량이 없습니다.'); return redirect()->to($batchUrl)->withInput()->with('error', '선택한 행에 입고할 수량이 없습니다.');
} }
$recvModel = model(BagReceivingModel::class); $recvModel = model(BagReceivingModel::class);
@@ -4846,7 +4851,7 @@ SQL);
$db->transComplete(); $db->transComplete();
if (! $db->transStatus()) { if (! $db->transStatus()) {
return redirect()->back()->withInput()->with('error', '일괄 입고 처리 중 오류가 발생했습니다.'); return redirect()->to($batchUrl)->withInput()->with('error', '일괄 입고 처리 중 오류가 발생했습니다.');
} }
return redirect()->to(site_url('bag/receiving/batch?company_idx=' . $companyIdx . '&bag_code=' . rawurlencode($bagCode))) return redirect()->to(site_url('bag/receiving/batch?company_idx=' . $companyIdx . '&bag_code=' . rawurlencode($bagCode)))

View File

@@ -25,6 +25,12 @@ class Home extends BaseController
return view('bag/layout/workspace'); return view('bag/layout/workspace');
} }
// 워크스페이스 탭(iframe) 안에서 세션이 만료된 경우: 공개 랜딩 대신 로그인으로 보내
// 로그인 페이지의 프레임 이탈 스크립트가 상위 창 전체를 로그인으로 전환하게 한다.
if ($this->isEmbeddedRequest()) {
return redirect()->to(base_url('login'));
}
return view('welcome_message'); return view('welcome_message');
} }

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
/**
* 워크스페이스 탭(iframe, embed=1) 안에서 발생한 리다이렉트가 embed 파라미터를 잃지 않도록
* 응답 Location 헤더에 embed=1 을 유지시킨다.
*
* 배경: 폼 전송 후 redirect()->to(...) 하면 Location 에 embed 가 빠지고, iframe 안에서
* 그 주소로 다시 GET 하면(특히 HTTP 환경에서 Sec-Fetch-Dest 미전송) 전체 셸(헤더·사이드바)이
* 한 번 더 렌더되어 "화면 안에 전체 화면이 또" 생긴다. 이를 전역에서 한 번에 방지한다.
*/
class EmbedRedirectFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
// no-op
}
/**
* @param array<string>|null $arguments
*/
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// 임베드 요청에서 시작된 리다이렉트만 처리
if ($request->getGet('embed') === null) {
return;
}
$location = $response->getHeaderLine('Location');
if ($location === '') {
return; // 리다이렉트 응답이 아님
}
// 이미 embed 파라미터가 있으면 그대로 둔다
if (preg_match('/[?&]embed=/', $location) === 1) {
return;
}
// 인증 흐름(로그인/로그아웃/회원가입)은 상위 프레임에서 처리하므로 embed 를 붙이지 않는다
if (preg_match('#/(login|logout|register)(/|\?|$)#', $location) === 1) {
return;
}
$sep = strpos($location, '?') !== false ? '&' : '?';
$response->setHeader('Location', $location . $sep . 'embed=1');
}
}

View File

@@ -950,18 +950,28 @@ if (! function_exists('manual_help_url_for_path')) {
return ''; return '';
} }
$map = config(\Config\Manual::class)->screenHelp ?? []; $map = config(\Config\Manual::class)->screenHelp ?? [];
$bestSlug = ''; $bestVal = '';
$bestLen = -1; $bestLen = -1;
foreach ($map as $prefix => $slug) { foreach ($map as $prefix => $val) {
$p = strtolower((string) $prefix); $p = strtolower((string) $prefix);
if ($path === $p || str_starts_with($path . '/', $p . '/')) { if ($path === $p || str_starts_with($path . '/', $p . '/')) {
if (strlen($p) > $bestLen) { if (strlen($p) > $bestLen) {
$bestLen = strlen($p); $bestLen = strlen($p);
$bestSlug = (string) $slug; $bestVal = (string) $val;
} }
} }
} }
if ($bestVal === '') {
return '';
}
return $bestSlug !== '' ? base_url('bag/manual/' . $bestSlug) : ''; // 값은 "slug" 또는 "slug#소제목힌트" 형식. 힌트가 있으면 ?hl= 로 전달해 해당 소제목으로 스크롤·강조.
[$slug, $hint] = array_pad(explode('#', $bestVal, 2), 2, null);
$url = base_url('bag/manual/' . $slug);
if ($hint !== null && trim($hint) !== '') {
$url .= '?hl=' . rawurlencode(trim($hint));
}
return $url;
} }
} }

View File

@@ -5,8 +5,26 @@
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button> <button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
</div> </div>
</section> </section>
<div class="border border-gray-300 rounded-lg p-4 mt-2"> <style>
<form method="get" action="<?= base_url('admin/access/approvals') ?>" class="mb-4 flex flex-wrap items-center gap-2 text-sm"> /* 인쇄 시 승인 대기 표를 A4 너비에 맞춤(잘림 방지) */
@media print {
@page { size: A4 portrait; margin: 10mm; }
.approval-print-wrap { border: 0 !important; padding: 0 !important; overflow: visible !important; }
.approval-print-wrap table.data-table { width: 100% !important; table-layout: fixed; font-size: 9px; }
.approval-print-wrap .data-table th,
.approval-print-wrap .data-table td {
white-space: normal !important; /* nowrap 해제 → 줄바꿈 허용 */
word-break: break-word;
overflow-wrap: anywhere;
padding: 2px 3px !important;
vertical-align: top;
}
/* '관리'(승인/반려 버튼) 열은 인쇄에 불필요 → 제외해 가로 폭 확보 */
.approval-print-wrap .col-actions { display: none !important; }
}
</style>
<div class="approval-print-wrap border border-gray-300 rounded-lg p-4 mt-2">
<form method="get" action="<?= base_url('admin/access/approvals') ?>" class="no-print mb-4 flex flex-wrap items-center gap-2 text-sm">
<label for="status" class="font-bold text-gray-700 shrink-0">상태</label> <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"> <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="pending" <?= ($status ?? 'pending') === 'pending' ? 'selected' : '' ?>>승인 대기</option>
@@ -25,7 +43,7 @@
<th class="text-center">요청 역할</th> <th class="text-center">요청 역할</th>
<th class="text-center">상태</th> <th class="text-center">상태</th>
<th class="text-center">처리일</th> <th class="text-center">처리일</th>
<th class="text-center">관리</th> <th class="text-center col-actions">관리</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -45,7 +63,7 @@
<?php if (($row->mar_status ?? '') === 'rejected'): ?>반려<?php endif; ?> <?php if (($row->mar_status ?? '') === 'rejected'): ?>반려<?php endif; ?>
</td> </td>
<td class="text-center"><?= esc($row->mar_processed_at ?? '-') ?></td> <td class="text-center"><?= esc($row->mar_processed_at ?? '-') ?></td>
<td class="text-center"> <td class="text-center col-actions">
<?php if (($row->mar_status ?? '') === 'pending'): ?> <?php if (($row->mar_status ?? '') === 'pending'): ?>
<div class="flex items-center justify-center gap-1"> <div class="flex items-center justify-center gap-1">
<form action="<?= base_url('admin/access/approve/' . $row->mar_idx) ?>" method="post" class="inline"> <form action="<?= base_url('admin/access/approve/' . $row->mar_idx) ?>" method="post" class="inline">

View File

@@ -105,6 +105,11 @@ tailwind.config = {
<?= view('home/_dashboard_gov_portal_brand', ['brandHref' => base_url('/')]) ?> <?= view('home/_dashboard_gov_portal_brand', ['brandHref' => base_url('/')]) ?>
<?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?> <?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?>
<div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;"> <div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;">
<div class="ws-fontctl" title="글씨 크기 조절" style="display:inline-flex;align-items:center;gap:2px;background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);border-radius:6px;padding:1px;">
<button type="button" id="wsFontMinus" title="글씨 작게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:11px;line-height:1;border-radius:5px;">A</button>
<span id="wsFontPct" style="min-width:34px;text-align:center;color:#fff;font-size:.68rem;font-weight:600;">100%</span>
<button type="button" id="wsFontPlus" title="글씨 크게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:14px;line-height:1;border-radius:5px;">A+</button>
</div>
<span class="user-line"> <span class="user-line">
<?php if ($effectiveLgName !== ''): ?><strong><?= esc($effectiveLgName) ?></strong> · <?php endif; ?> <?php if ($effectiveLgName !== ''): ?><strong><?= esc($effectiveLgName) ?></strong> · <?php endif; ?>
<?= esc($levelName) ?> · <?= esc($mbName) ?>님 <?= esc($levelName) ?> · <?= esc($mbName) ?>님
@@ -161,6 +166,25 @@ tailwind.config = {
window.addEventListener('pageshow', function (e) { if (e.persisted) closeStuckOverlays(); }); window.addEventListener('pageshow', function (e) { if (e.persisted) closeStuckOverlays(); });
window.addEventListener('pagehide', closeStuckOverlays); window.addEventListener('pagehide', closeStuckOverlays);
})(); })();
// 글씨 크기 조절(A/A+) — 본문 + 상단 대메뉴 + 좌측 사이드바에 zoom 적용. 사이트/워크스페이스와 배율 공유.
(function () {
var FONT_KEY = 'jrj_font_scale';
var scaleSelectors = ['.portal-header', '.sidebar', '.work-main'];
function curScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; }
function applyScale(s) {
s = Math.min(150, Math.max(70, s));
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
var z = s / 100;
scaleSelectors.forEach(function (sel) { var el = document.querySelector(sel); if (el) el.style.zoom = z; });
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
}
applyScale(curScale());
var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus');
if (plus) plus.addEventListener('click', function () { applyScale(curScale() + 10); });
if (minus) minus.addEventListener('click', function () { applyScale(curScale() - 10); });
window.addEventListener('storage', function (e) { if (e.key === FONT_KEY) applyScale(curScale()); });
})();
</script> </script>
</body> </body>
</html> </html>

View File

@@ -8,7 +8,25 @@
</div> </div>
</div> </div>
</section> </section>
<div class="border border-gray-300 rounded-lg p-4 overflow-auto mt-2"> <style>
/* 인쇄 시 회원 목록 표를 A4 너비에 맞춤(잘림 방지) */
@media print {
@page { size: A4 portrait; margin: 10mm; }
.user-print-wrap { border: 0 !important; padding: 0 !important; overflow: visible !important; }
.user-print-wrap table.data-table { width: 100% !important; table-layout: fixed; font-size: 9px; }
.user-print-wrap .data-table th,
.user-print-wrap .data-table td {
white-space: normal !important; /* nowrap 해제 → 줄바꿈 허용 */
word-break: break-word;
overflow-wrap: anywhere;
padding: 2px 3px !important;
vertical-align: top;
}
/* '관리'(작업 버튼) 열은 인쇄에 불필요 → 제외해 가로 폭 확보 */
.user-print-wrap .col-actions { display: none !important; }
}
</style>
<div class="user-print-wrap border border-gray-300 rounded-lg p-4 overflow-auto mt-2">
<table class="w-full data-table"> <table class="w-full data-table">
<thead> <thead>
<tr> <tr>
@@ -20,7 +38,7 @@
<th class="text-center">상태</th> <th class="text-center">상태</th>
<th class="text-left">로그인 잠금</th> <th class="text-left">로그인 잠금</th>
<th class="text-left">가입일</th> <th class="text-left">가입일</th>
<th class="text-center">관리</th> <th class="text-center col-actions">관리</th>
</tr> </tr>
</thead> </thead>
<tbody class="text-right"> <tbody class="text-right">
@@ -56,7 +74,7 @@
?> ?>
</td> </td>
<td class="text-left pl-2"><?= esc($row->mb_regdate ?? '') ?></td> <td class="text-left pl-2"><?= esc($row->mb_regdate ?? '') ?></td>
<td class="text-center"> <td class="text-center col-actions">
<?php if ((int) $row->mb_state !== 0): ?> <?php if ((int) $row->mb_state !== 0): ?>
<?php if ($loginLocked): ?> <?php if ($loginLocked): ?>
<form action="<?= base_url('admin/users/unlock-login/' . $row->mb_idx) ?>" method="POST" class="inline mr-1" onsubmit="return confirm('로그인 잠금을 해제할까요?');"> <form action="<?= base_url('admin/users/unlock-login/' . $row->mb_idx) ?>" method="POST" class="inline mr-1" onsubmit="return confirm('로그인 잠금을 해제할까요?');">

View File

@@ -12,6 +12,7 @@ $showKindActions = $canManageKinds;
$selectedKindId = (int) ($selectedKind->ck_idx ?? 0); $selectedKindId = (int) ($selectedKind->ck_idx ?? 0);
$colCount = 6 + ($showKindActions ? 1 : 0); $colCount = 6 + ($showKindActions ? 1 : 0);
$detailColCount = 7 + ($canManageDetails ? 1 : 0); $detailColCount = 7 + ($canManageDetails ? 1 : 0);
$embedQs = ! empty($isEmbed) ? '&embed=1' : ''; // 워크스페이스 탭 안에서는 embed 유지(중첩 셸 방지)
/** 상태 배지 (업무현황 스타일의 가벼운 pill) */ /** 상태 배지 (업무현황 스타일의 가벼운 pill) */
$stateBadge = static function (int $state): string { $stateBadge = static function (int $state): string {
@@ -51,7 +52,7 @@ $stateBadge = static function (int $state): string {
<?php $i = 0; foreach ($codeKinds as $row): $i++; ?> <?php $i = 0; foreach ($codeKinds as $row): $i++; ?>
<?php <?php
$isSelected = (int) $row->ck_idx === $selectedKindId; $isSelected = (int) $row->ck_idx === $selectedKindId;
$detailUrl = base_url('bag/code-kinds?ck_idx=' . (int) $row->ck_idx); $detailUrl = base_url('bag/code-kinds?ck_idx=' . (int) $row->ck_idx . $embedQs);
?> ?>
<tr class="border-b border-gray-200 last:border-0 cursor-pointer hover:bg-blue-50/60 <?= $isSelected ? 'bg-blue-50' : '' ?>" <tr class="border-b border-gray-200 last:border-0 cursor-pointer hover:bg-blue-50/60 <?= $isSelected ? 'bg-blue-50' : '' ?>"
onclick="window.location.href='<?= esc($detailUrl, 'attr') ?>'"> onclick="window.location.href='<?= esc($detailUrl, 'attr') ?>'">

View File

@@ -144,7 +144,7 @@ if ($initialRows === []) {
</div> </div>
<div class="grid grid-cols-1 xl:grid-cols-2 gap-2 mt-2"> <div class="grid grid-cols-1 xl:grid-cols-2 gap-2 mt-2">
<section class="border border-gray-300 rounded-lg bg-white"> <section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 가능 봉투(현재 재고)</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 가능 봉투(현재 재고)</div>
<div class="overflow-auto max-h-[300px]"> <div class="overflow-auto max-h-[300px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -180,7 +180,7 @@ if ($initialRows === []) {
</div> </div>
</section> </section>
<section class="border border-gray-300 rounded-lg bg-white"> <section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">최근 불출 내역</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">최근 불출 내역</div>
<div class="overflow-auto max-h-[300px]"> <div class="overflow-auto max-h-[300px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -94,7 +94,7 @@
<span class="text-gray-600 ml-2">왼쪽 목록에서 삭제할 발주를 선택한 뒤 「삭제 실행」을 누르세요.</span> <span class="text-gray-600 ml-2">왼쪽 목록에서 삭제할 발주를 선택한 뒤 「삭제 실행」을 누르세요.</span>
</div> </div>
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2"> <div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
<section class="xl:col-span-5 border border-gray-300 rounded-lg bg-white"> <section class="xl:col-span-5 border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
<div class="overflow-auto max-h-[410px]"> <div class="overflow-auto max-h-[410px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -189,7 +189,7 @@
</div> </div>
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2"> <div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
<section class="xl:col-span-5 border border-gray-300 rounded-lg bg-white"> <section class="xl:col-span-5 border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
<div class="overflow-auto max-h-[410px]"> <div class="overflow-auto max-h-[410px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -222,7 +222,7 @@
</div> </div>
</section> </section>
<section class="xl:col-span-7 border border-gray-300 rounded-lg bg-white"> <section class="xl:col-span-7 border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700"><?= $editMode ? '발주 변경 수정' : '발주 Form' ?></div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700"><?= $editMode ? '발주 변경 수정' : '발주 Form' ?></div>
<div class="p-2 space-y-2"> <div class="p-2 space-y-2">
<div class="grid grid-cols-1 md:grid-cols-2 gap-2"> <div class="grid grid-cols-1 md:grid-cols-2 gap-2">
@@ -331,7 +331,7 @@
</section> </section>
</div> </div>
<section class="border border-gray-300 rounded-lg bg-white"> <section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 등록 종류</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 등록 종류</div>
<div class="overflow-auto"> <div class="overflow-auto">
<table class="w-full data-table text-sm order-reference-table"> <table class="w-full data-table text-sm order-reference-table">

View File

@@ -26,7 +26,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
</form> </form>
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3"> <div class="grid grid-cols-1 xl:grid-cols-5 gap-3">
<section class="xl:col-span-2 border border-gray-300 rounded-lg"> <section class="xl:col-span-2 border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 리스트</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 리스트</div>
<div class="max-h-64 overflow-auto"> <div class="max-h-64 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -52,7 +52,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
</div> </div>
</section> </section>
<section class="xl:col-span-3 border border-gray-300 rounded-lg"> <section class="xl:col-span-3 border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 취소 리스트</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 취소 리스트</div>
<div class="max-h-64 overflow-auto"> <div class="max-h-64 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -85,7 +85,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
</section> </section>
</div> </div>
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 취소 봉투 코드</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 취소 봉투 코드</div>
<div class="max-h-72 overflow-auto"> <div class="max-h-72 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -22,7 +22,7 @@ $returns = is_array($returns ?? null) ? $returns : [];
</form> </form>
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3"> <div class="grid grid-cols-1 xl:grid-cols-5 gap-3">
<section class="xl:col-span-2 border border-gray-300 rounded-lg"> <section class="xl:col-span-2 border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 리스트</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 리스트</div>
<div class="max-h-64 overflow-auto"> <div class="max-h-64 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -50,7 +50,7 @@ $returns = is_array($returns ?? null) ? $returns : [];
</div> </div>
</section> </section>
<section class="xl:col-span-3 border border-gray-300 rounded-lg"> <section class="xl:col-span-3 border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 리스트</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 리스트</div>
<div class="max-h-64 overflow-auto"> <div class="max-h-64 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -79,7 +79,7 @@ $returns = is_array($returns ?? null) ? $returns : [];
</section> </section>
</div> </div>
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 취소 대상 봉투 코드</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 취소 대상 봉투 코드</div>
<div class="max-h-72 overflow-auto"> <div class="max-h-72 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -35,7 +35,7 @@
<div id="scan-message" class="text-sm text-gray-600"></div> <div id="scan-message" class="text-sm text-gray-600"></div>
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3"> <div class="grid grid-cols-1 xl:grid-cols-5 gap-3">
<section class="xl:col-span-2 border border-gray-300 rounded-lg"> <section class="xl:col-span-2 border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 정보</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 정보</div>
<table class="w-full text-sm"> <table class="w-full text-sm">
<tr><th class="text-left px-3 py-1.5 w-28">판매소 코드</th><td id="shop-info-code" class="px-3 py-1.5">-</td></tr> <tr><th class="text-left px-3 py-1.5 w-28">판매소 코드</th><td id="shop-info-code" class="px-3 py-1.5">-</td></tr>
@@ -46,7 +46,7 @@
</table> </table>
</section> </section>
<section class="xl:col-span-3 border border-gray-300 rounded-lg"> <section class="xl:col-span-3 border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">주문 접수 리스트</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">주문 접수 리스트</div>
<div class="max-h-56 overflow-auto"> <div class="max-h-56 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -97,7 +97,7 @@
</section> </section>
<div class="grid grid-cols-1 xl:grid-cols-2 gap-3"> <div class="grid grid-cols-1 xl:grid-cols-2 gap-3">
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 내역</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 내역</div>
<div class="max-h-72 overflow-auto"> <div class="max-h-72 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -118,7 +118,7 @@
</div> </div>
</section> </section>
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 상세 내역</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">판매 상세 내역</div>
<div class="max-h-72 overflow-auto"> <div class="max-h-72 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -28,7 +28,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
<div id="scan-message" class="text-sm text-gray-600"></div> <div id="scan-message" class="text-sm text-gray-600"></div>
<div class="grid grid-cols-1 xl:grid-cols-2 gap-3"> <div class="grid grid-cols-1 xl:grid-cols-2 gap-3">
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 정보</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">지정판매소 정보</div>
<table class="w-full text-sm"> <table class="w-full text-sm">
<tr><th class="text-left px-3 py-1.5 w-28">판매소 코드</th><td id="shop-info-code" class="px-3 py-1.5">-</td></tr> <tr><th class="text-left px-3 py-1.5 w-28">판매소 코드</th><td id="shop-info-code" class="px-3 py-1.5">-</td></tr>
@@ -39,7 +39,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
</table> </table>
</section> </section>
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 리스트</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 리스트</div>
<div class="max-h-64 overflow-auto"> <div class="max-h-64 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -70,7 +70,7 @@ $devSoldScans = is_array($devSoldScans ?? null) ? $devSoldScans : [];
</section> </section>
</div> </div>
<section class="border border-gray-300 rounded-lg"> <section class="border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 봉투 코드</div> <div class="px-3 py-2 border-b bg-gray-50 text-sm font-semibold text-gray-700">반품 봉투 코드</div>
<div class="max-h-72 overflow-auto"> <div class="max-h-72 overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -42,7 +42,6 @@ foreach ($subtotals as $subtotal) {
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button> <button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
<a href="<?= base_url('bag/inventory/export?' . http_build_query(['base_date' => $baseDate, 'agency_idx' => $agencyIdx])) ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a> <a href="<?= base_url('bag/inventory/export?' . http_build_query(['base_date' => $baseDate, 'agency_idx' => $agencyIdx])) ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
<button type="button" onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button> <button type="button" onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
<a href="<?= base_url('bag/inventory/inspection-select') ?>" class="no-print border border-blue-300 text-blue-700 px-3 py-1 rounded-sm text-sm hover:bg-blue-50 transition">실사 선별 조회</a>
</div> </div>
</form> </form>
</section> </section>

View File

@@ -74,7 +74,7 @@ $sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
</section> </section>
<section class="grid grid-cols-1 xl:grid-cols-2 gap-2"> <section class="grid grid-cols-1 xl:grid-cols-2 gap-2">
<div class="border border-gray-300 rounded-lg bg-white"> <div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div>
<div class="overflow-auto max-h-[500px]"> <div class="overflow-auto max-h-[500px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -134,7 +134,7 @@ $sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<div class="border border-gray-300 rounded-lg bg-white"> <div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(선택 박스 조회)</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(선택 박스 조회)</div>
<form method="post" action="<?= base_url('bag/inventory/inspection-select/save') ?>" id="inspection-save-form"> <form method="post" action="<?= base_url('bag/inventory/inspection-select/save') ?>" id="inspection-save-form">
<?= csrf_field() ?> <?= csrf_field() ?>
@@ -224,7 +224,7 @@ $sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
</form> </form>
</div> </div>
<div class="border border-gray-300 rounded-lg bg-white"> <div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(선택 낱장 코드 조회)</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(선택 낱장 코드 조회)</div>
<div class="overflow-auto max-h-[240px]"> <div class="overflow-auto max-h-[240px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -61,7 +61,7 @@ foreach ($sheetRows as $row) {
</section> </section>
<section class="grid grid-cols-1 xl:grid-cols-2 gap-2"> <section class="grid grid-cols-1 xl:grid-cols-2 gap-2">
<div class="border border-gray-300 rounded-lg bg-white"> <div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div>
<div class="overflow-auto max-h-[500px]"> <div class="overflow-auto max-h-[500px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -114,7 +114,7 @@ foreach ($sheetRows as $row) {
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<div class="border border-gray-300 rounded-lg bg-white"> <div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(읽기 전용)</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(읽기 전용)</div>
<div class="overflow-auto max-h-[260px]"> <div class="overflow-auto max-h-[260px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -169,7 +169,7 @@ foreach ($sheetRows as $row) {
</div> </div>
</div> </div>
<div class="border border-gray-300 rounded-lg bg-white"> <div class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(읽기 전용)</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(읽기 전용)</div>
<div class="overflow-auto max-h-[240px]"> <div class="overflow-auto max-h-[240px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -73,7 +73,7 @@ $selectedBagCode = (string) ($selectedBagCode ?? '');
<input type="hidden" name="sel_bag_code" value="<?= esc($selectedBagCode) ?>"/> <input type="hidden" name="sel_bag_code" value="<?= esc($selectedBagCode) ?>"/>
<div class="grid grid-cols-1 xl:grid-cols-4 gap-2"> <div class="grid grid-cols-1 xl:grid-cols-4 gap-2">
<section class="border border-gray-300 rounded-lg bg-white xl:col-span-1"> <section class="border border-gray-300 rounded-lg overflow-hidden bg-white xl:col-span-1">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 리스트</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 리스트</div>
<div class="overflow-auto max-h-[560px]"> <div class="overflow-auto max-h-[560px]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -120,7 +120,7 @@ $selectedBagCode = (string) ($selectedBagCode ?? '');
</section> </section>
<div class="xl:col-span-3 grid grid-cols-1 gap-2"> <div class="xl:col-span-3 grid grid-cols-1 gap-2">
<section class="border border-gray-300 rounded-lg bg-white"> <section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 품목 내역</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 품목 내역</div>
<div class="overflow-auto max-h-[270px]"> <div class="overflow-auto max-h-[270px]">
<table class="w-full data-table text-sm" id="detail-table"> <table class="w-full data-table text-sm" id="detail-table">
@@ -196,7 +196,7 @@ $selectedBagCode = (string) ($selectedBagCode ?? '');
</div> </div>
</section> </section>
<section class="border border-gray-300 rounded-lg bg-white"> <section class="border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 품목 코드</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">불출 품목 코드</div>
<div class="overflow-auto max-h-[250px]"> <div class="overflow-auto max-h-[250px]">
<table class="w-full data-table text-sm" id="code-table"> <table class="w-full data-table text-sm" id="code-table">

View File

@@ -51,7 +51,7 @@ tailwind.config = {
.data-table tbody tr:hover td { background-color: #f9fafb; } .data-table tbody tr:hover td { background-color: #f9fafb; }
@media print { .no-print { display: none !important; } .embed-titlebar { display: none; } } @media print { .no-print { display: none !important; } .embed-titlebar { display: none; } }
/* 화면 설명 드로어(팝업) — 현재 화면 위 오른쪽에 겹쳐 띄움 */ /* 화면 설명 드로어(팝업) — 현재 화면 위 오른쪽에 겹쳐 띄움 */
.help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; } .help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(920px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; }
.help-drawer.open { display: flex; } .help-drawer.open { display: flex; }
.help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; } .help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; }
.help-drawer-head .hd-btns { display: flex; gap: 4px; } .help-drawer-head .hd-btns { display: flex; gap: 4px; }
@@ -137,7 +137,13 @@ tailwind.config = {
function withEmbedUrl(url) { try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); return x.href; } catch (e) { return url; } } function withEmbedUrl(url) { try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); return x.href; } catch (e) { return url; } }
function openHelp(url) { function openHelp(url) {
var u = withEmbedUrl(url); var u = withEmbedUrl(url);
if (dFrame.getAttribute('data-src') !== u) { dFrame.src = u; dFrame.setAttribute('data-src', u); } if (dFrame.getAttribute('data-src') !== u) {
// 새 URL → 재로드(로드 후 매뉴얼 자체 스크립트가 강조 실행)
dFrame.src = u; dFrame.setAttribute('data-src', u);
} else {
// 같은 URL → 재로드 안 함. 매뉴얼에 다시 강조하라고 알림(껐다 켜도 강조되도록)
try { dFrame.contentWindow.postMessage({ type: 'manual-hl' }, location.origin); } catch (e) {}
}
var tab = document.getElementById('helpDrawerTab'); if (tab) tab.setAttribute('href', url); var tab = document.getElementById('helpDrawerTab'); if (tab) tab.setAttribute('href', url);
drawer.classList.add('open'); drawer.classList.add('open');
} }
@@ -160,9 +166,13 @@ tailwind.config = {
// 드로어 폭 드래그 조절 // 드로어 폭 드래그 조절
(function () { (function () {
var grip = document.getElementById('helpDrawerGrip'), dragging = false; var grip = document.getElementById('helpDrawerGrip'), dragging = false;
grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; document.body.style.userSelect = 'none'; }); // 드래그 중 iframe 이 마우스 이벤트를 가로채면(특히 좁힐 때) 멈추므로, 화면 전체 투명 오버레이로 이벤트를 가로챈다.
var ov = document.createElement('div');
ov.style.cssText = 'position:fixed;inset:0;z-index:10000;cursor:col-resize;display:none;';
document.body.appendChild(ov);
grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; ov.style.display = 'block'; document.body.style.userSelect = 'none'; });
document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; }); document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; });
document.addEventListener('mouseup', function () { dragging = false; document.body.style.userSelect = ''; }); document.addEventListener('mouseup', function () { if (!dragging) return; dragging = false; ov.style.display = 'none'; document.body.style.userSelect = ''; });
})(); })();
// 글씨 크기(zoom) — 상단바에서 조절한 값을 적용. localStorage 공유 + storage 이벤트로 실시간 반영. // 글씨 크기(zoom) — 상단바에서 조절한 값을 적용. localStorage 공유 + storage 이벤트로 실시간 반영.

View File

@@ -217,7 +217,7 @@ tailwind.config = {
<!-- 화면 설명 드로어(팝업) --> <!-- 화면 설명 드로어(팝업) -->
<style> <style>
.help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; } .help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(920px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; }
.help-drawer.open { display: flex; } .help-drawer.open { display: flex; }
.help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; } .help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; }
.help-drawer-head .hd-btn { color: #fff; background: rgba(255,255,255,.14); border: 0; width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; margin-left: 4px; } .help-drawer-head .hd-btn { color: #fff; background: rgba(255,255,255,.14); border: 0; width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; margin-left: 4px; }
@@ -238,7 +238,12 @@ tailwind.config = {
var drawer = document.getElementById('helpDrawer'), dFrame = document.getElementById('helpDrawerFrame'); var drawer = document.getElementById('helpDrawer'), dFrame = document.getElementById('helpDrawerFrame');
function openHelp(url) { function openHelp(url) {
var u = url; try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); u = x.href; } catch (e) {} var u = url; try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); u = x.href; } catch (e) {}
if (dFrame.getAttribute('data-src') !== u) { dFrame.src = u; dFrame.setAttribute('data-src', u); } if (dFrame.getAttribute('data-src') !== u) {
dFrame.src = u; dFrame.setAttribute('data-src', u);
} else {
// 같은 URL → 재로드 안 함. 매뉴얼에 다시 강조하라고 알림(껐다 켜도 강조되도록)
try { dFrame.contentWindow.postMessage({ type: 'manual-hl' }, location.origin); } catch (e) {}
}
drawer.classList.add('open'); drawer.classList.add('open');
} }
function closeHelp() { drawer.classList.remove('open'); } function closeHelp() { drawer.classList.remove('open'); }
@@ -251,18 +256,23 @@ tailwind.config = {
document.getElementById('helpDrawerClose').addEventListener('click', closeHelp); document.getElementById('helpDrawerClose').addEventListener('click', closeHelp);
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeHelp(); }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeHelp(); });
var grip = document.getElementById('helpDrawerGrip'), dragging = false; var grip = document.getElementById('helpDrawerGrip'), dragging = false;
grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; document.body.style.userSelect = 'none'; }); // 드래그 중 iframe 이 마우스 이벤트를 가로채면(특히 좁힐 때) 멈추므로, 화면 전체 투명 오버레이로 이벤트를 가로챈다.
var ov = document.createElement('div');
ov.style.cssText = 'position:fixed;inset:0;z-index:10000;cursor:col-resize;display:none;';
document.body.appendChild(ov);
grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; ov.style.display = 'block'; document.body.style.userSelect = 'none'; });
document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; }); document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; });
document.addEventListener('mouseup', function () { dragging = false; document.body.style.userSelect = ''; }); document.addEventListener('mouseup', function () { if (!dragging) return; dragging = false; ov.style.display = 'none'; document.body.style.userSelect = ''; });
// 글씨 크기 조절 — 본문(.work-main) 영역에 zoom 적용. 헤더/사이드바는 그대로. // 글씨 크기 조절 — 본문 + 상단 대메뉴 + 좌측 사이드바(메뉴)에 zoom 적용.
var FONT_KEY = 'jrj_font_scale'; var FONT_KEY = 'jrj_font_scale';
var target = document.querySelector('.work-main') || document.body; var scaleSelectors = ['.portal-header', '.sidebar', '.work-main'];
function curScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; } function curScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; }
function applyScale(s) { function applyScale(s) {
s = Math.min(150, Math.max(70, s)); s = Math.min(150, Math.max(70, s));
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {} try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
target.style.zoom = (s / 100); var z = s / 100;
scaleSelectors.forEach(function (sel) { var el = document.querySelector(sel); if (el) el.style.zoom = z; });
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%'; var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
} }
applyScale(curScale()); applyScale(curScale());

View File

@@ -486,11 +486,17 @@ if ($effectiveLgIdx) {
function setFontScale(s) { function setFontScale(s) {
s = Math.min(150, Math.max(70, s)); s = Math.min(150, Math.max(70, s));
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {} try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
var z = s / 100;
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%'; var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
Object.keys(tabs).forEach(function (k) { try { tabs[k].frame.contentDocument.documentElement.style.zoom = (s / 100); } catch (e) {} }); // 탭(iframe) 내용
Object.keys(tabs).forEach(function (k) { try { tabs[k].frame.contentDocument.documentElement.style.zoom = z; } catch (e) {} });
// 셸 메뉴(상단 대메뉴 + 좌측 사이드바)도 함께 확대
['.portal-header', '.sidebar'].forEach(function (sel) {
var el = document.querySelector(sel); if (el) el.style.zoom = z;
});
} }
(function () { (function () {
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = curFontScale() + '%'; setFontScale(curFontScale()); // 저장된 배율을 셸 메뉴에도 적용(초기 로드)
var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus'); var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus');
if (plus) plus.addEventListener('click', function () { setFontScale(curFontScale() + 10); }); if (plus) plus.addEventListener('click', function () { setFontScale(curFontScale() + 10); });
if (minus) minus.addEventListener('click', function () { setFontScale(curFontScale() - 10); }); if (minus) minus.addEventListener('click', function () { setFontScale(curFontScale() - 10); });

View File

@@ -44,6 +44,9 @@ $searchQ = (string) (service('request')->getGet('q') ?? '');
.manual-prose tbody tr:nth-child(even) td { background: #f9fafb; } .manual-prose tbody tr:nth-child(even) td { background: #f9fafb; }
.manual-prose hr { margin: 1.6rem 0; border: 0; border-top: 1px solid #e5e7eb; } .manual-prose hr { margin: 1.6rem 0; border: 0; border-top: 1px solid #e5e7eb; }
.manual-toc a.active { background: #1a2b4b; color: #fff; font-weight: 700; } .manual-toc a.active { background: #1a2b4b; color: #fff; font-weight: 700; }
/* "이 화면 설명"으로 들어왔을 때 해당 소제목 강조 */
.manual-prose h2.hl-flash, .manual-prose h3.hl-flash { background: #fef9c3; box-shadow: -6px 0 0 #fef9c3, 6px 0 0 #fef9c3; border-radius: 4px; animation: hlFlash 2.6s ease-out 1; }
@keyframes hlFlash { 0%, 45% { background: #fde047; box-shadow: -6px 0 0 #fde047, 6px 0 0 #fde047; } 100% { background: transparent; box-shadow: -6px 0 0 transparent, 6px 0 0 transparent; } }
@media print { @media print {
.manual-toc, .manual-actions, .manual-nav { display: none !important; } .manual-toc, .manual-actions, .manual-nav { display: none !important; }
.manual-layout { display: block !important; } .manual-layout { display: block !important; }
@@ -108,6 +111,35 @@ $searchQ = (string) (service('request')->getGet('q') ?? '');
var SEARCH_URL = location.origin + <?= json_encode((string) parse_url(base_url('bag/manual/search'), PHP_URL_PATH), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>; var SEARCH_URL = location.origin + <?= json_encode((string) parse_url(base_url('bag/manual/search'), PHP_URL_PATH), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var BASE = location.origin + <?= json_encode((string) parse_url(base_url('bag/manual/'), PHP_URL_PATH), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>; var BASE = location.origin + <?= json_encode((string) parse_url(base_url('bag/manual/'), PHP_URL_PATH), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var Q0 = <?= json_encode($searchQ, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS) ?>; var Q0 = <?= json_encode($searchQ, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS) ?>;
// "이 화면 설명"으로 들어온 경우: 해당 소제목으로 스크롤 + 강조.
// 드로어를 껐다 다시 켜도(같은 URL이라 iframe 재로드 안 됨) 부모가 보낸 메시지로 다시 강조한다.
var hlFlashTimer = null;
function runHl() {
var hl = '';
try { hl = new URLSearchParams(location.search).get('hl') || ''; } catch (e) {}
hl = hl.trim();
if (!hl) return;
var norm = function (s) { return String(s || '').replace(/\s+/g, ' ').trim().toLowerCase(); };
var needle = norm(hl);
var prose = document.querySelector('.manual-prose');
if (!prose) return;
var heads = prose.querySelectorAll('h2, h3'), target = null;
for (var i = 0; i < heads.length; i++) {
if (norm(heads[i].textContent).indexOf(needle) >= 0) { target = heads[i]; break; }
}
if (!target) return;
clearTimeout(hlFlashTimer);
target.classList.remove('hl-flash');
void target.offsetWidth; // 리플로우 → 애니메이션 재시작
target.classList.add('hl-flash');
try { target.scrollIntoView({ block: 'start', behavior: 'smooth' }); } catch (e) { target.scrollIntoView(); }
hlFlashTimer = setTimeout(function () { target.classList.remove('hl-flash'); }, 2700);
}
runHl();
window.addEventListener('message', function (e) {
if (e.origin !== location.origin) return;
if (e.data && e.data.type === 'manual-hl') runHl();
});
var input = document.getElementById('manualSearchInput'); var input = document.getElementById('manualSearchInput');
var box = document.getElementById('manualSearchResults'); var box = document.getElementById('manualSearchResults');

View File

@@ -18,12 +18,7 @@
</section> </section>
<div class="mt-2 space-y-3"> <div class="mt-2 space-y-3">
<?php if (session()->getFlashdata('success')): ?> <?php /* 플래시 메시지는 레이아웃(embed/portal)에서 한 번만 표시 — 중복 출력 방지 */ ?>
<div class="border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<form method="get" action="<?= base_url('bag/order/change') ?>" class="border border-gray-300 rounded-lg bg-white p-3 flex flex-wrap items-end gap-4"> <form method="get" action="<?= base_url('bag/order/change') ?>" class="border border-gray-300 rounded-lg bg-white p-3 flex flex-wrap items-end gap-4">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@@ -55,7 +50,7 @@
</form> </form>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-2"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-2">
<section class="lg:col-span-5 border border-gray-300 rounded-lg bg-white"> <section class="lg:col-span-5 border border-gray-300 rounded-lg overflow-hidden bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 목록 (발주일 · 제작업체)</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 목록 (발주일 · 제작업체)</div>
<div class="overflow-auto max-h-[min(420px,60vh)]"> <div class="overflow-auto max-h-[min(420px,60vh)]">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -65,7 +65,7 @@ for ($year = $baseYear - 2; $year <= $baseYear + 2; $year++) {
</form> </form>
</section> </section>
<section class="border border-gray-300 rounded-lg mt-2 bg-white"> <section class="border border-gray-300 rounded-lg overflow-hidden mt-2 bg-white">
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주목록</div> <div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주목록</div>
<div class="overflow-auto"> <div class="overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">

View File

@@ -3,16 +3,7 @@
</section> </section>
<div class="border border-gray-300 rounded-lg p-4 mt-2 bg-white"> <div class="border border-gray-300 rounded-lg p-4 mt-2 bg-white">
<?php if (session()->getFlashdata('success')): ?> <?php /* success·error 플래시는 레이아웃(embed/portal)에서 한 번만 표시 — 중복 방지. (검증 errors 목록만 여기서 표시) */ ?>
<div class="mb-3 border border-emerald-300 bg-emerald-50 text-emerald-800 px-3 py-2 rounded-sm text-sm">
<?= esc(session()->getFlashdata('success')) ?>
</div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="mb-3 border border-red-300 bg-red-50 text-red-700 px-3 py-2 rounded-sm text-sm">
<?= esc(session()->getFlashdata('error')) ?>
</div>
<?php endif; ?>
<?php $flashErrors = session()->getFlashdata('errors'); ?> <?php $flashErrors = session()->getFlashdata('errors'); ?>
<?php if (is_array($flashErrors) && $flashErrors !== []): ?> <?php if (is_array($flashErrors) && $flashErrors !== []): ?>
<div class="mb-3 border border-red-300 bg-red-50 text-red-700 px-3 py-2 rounded-sm text-sm"> <div class="mb-3 border border-red-300 bg-red-50 text-red-700 px-3 py-2 rounded-sm text-sm">

View File

@@ -6,7 +6,7 @@
</section> </section>
<div class="grid grid-cols-1 xl:grid-cols-5 gap-3 mt-2"> <div class="grid grid-cols-1 xl:grid-cols-5 gap-3 mt-2">
<section class="xl:col-span-2 border border-gray-300 rounded-lg bg-white"> <section class="xl:col-span-2 border border-gray-300 rounded-lg bg-white overflow-hidden">
<div class="px-3 py-2 border-b border-gray-200 bg-gray-50 text-sm font-semibold text-gray-700">접수 리스트(전화)</div> <div class="px-3 py-2 border-b border-gray-200 bg-gray-50 text-sm font-semibold text-gray-700">접수 리스트(전화)</div>
<div class="max-h-[72vh] overflow-auto"> <div class="max-h-[72vh] overflow-auto">
<table class="w-full data-table text-sm"> <table class="w-full data-table text-sm">
@@ -38,7 +38,7 @@
</div> </div>
</section> </section>
<section class="xl:col-span-3 border border-gray-300 rounded-lg bg-white"> <section class="xl:col-span-3 border border-gray-300 rounded-lg bg-white overflow-hidden">
<div class="px-3 py-2 border-b border-gray-200 bg-gray-50 text-sm font-semibold text-gray-700">상세 정보</div> <div class="px-3 py-2 border-b border-gray-200 bg-gray-50 text-sm font-semibold text-gray-700">상세 정보</div>
<form id="order-detail-form" action="<?= base_url('bag/order/phone/manage/update') ?>" method="POST" class="p-3 space-y-3"> <form id="order-detail-form" action="<?= base_url('bag/order/phone/manage/update') ?>" method="POST" class="p-3 space-y-3">

View File

@@ -6,12 +6,7 @@
</div> </div>
</section> </section>
<?php if (session()->getFlashdata('success')): ?> <?php /* 플래시 메시지는 레이아웃(embed/portal)에서 한 번만 표시한다 — 중복 출력 방지 */ ?>
<div class="mt-2 border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="mt-2 border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<section class="p-2 bg-white border border-gray-300 rounded-lg mt-2"> <section class="p-2 bg-white border border-gray-300 rounded-lg mt-2">
<form method="get" action="<?= base_url('bag/receiving/batch') ?>" class="flex flex-wrap items-center gap-2"> <form method="get" action="<?= base_url('bag/receiving/batch') ?>" class="flex flex-wrap items-center gap-2">

View File

@@ -6,12 +6,7 @@
</div> </div>
</section> </section>
<?php if (session()->getFlashdata('success')): ?> <?php /* 플래시 메시지는 레이아웃(embed/portal)에서 한 번만 표시 — 중복 출력 방지 */ ?>
<div class="mt-2 border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="mt-2 border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<section class="p-2 bg-white border border-gray-300 rounded-lg mt-2"> <section class="p-2 bg-white border border-gray-300 rounded-lg mt-2">
<form method="get" action="<?= base_url('bag/receiving/scanner') ?>" class="flex flex-wrap items-end gap-2"> <form method="get" action="<?= base_url('bag/receiving/scanner') ?>" class="flex flex-wrap items-end gap-2">