Files
sam-api/public/tenant/production/screen_progress.php
hskwon cc206fdbed style: Laravel Pint 코드 포맷팅 적용
- PSR-12 스타일 가이드 준수
- 302개 파일 스타일 이슈 자동 수정
- 코드 로직 변경 없음 (포맷팅만)
2025-11-06 17:45:49 +09:00

276 lines
12 KiB
PHP

<!-- SAM RULES: include=../inc/header.php; base=/tenant; width=1280; js=jQuery+BS5 -->
<?php
$CURRENT_SECTION = 'process';
include '../inc/header.php';
?>
<div class="container" style="max-width:1280px; margin-top:18px;">
<!-- 탭 (좌측) + 검색 (우측) -->
<div class="d-flex align-items-center justify-content-between mb-2">
<ul class="nav nav-pills gap-2">
<li class="nav-item">
<a class="nav-link" href="/tenant/production/screen_work.php">작업지시현황</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/tenant/production/screen_progress.php">공정진행현황</a>
</li>
</ul>
<!-- 기본 검색폼 (키워드 + 작업자 + 버튼) -->
<form id="searchForm" class="d-flex gap-2">
<div class="search-form d-flex gap-2">
<input type="text" id="keyword" class="form-control" placeholder="현장명/발주처/제품명 등">
<input type="text" id="worker" class="form-control" placeholder="작업자">
<button type="submit" class="btn btn-primary">검색</button>
</div>
</form>
</div>
<div class="border rounded p-2">
<div class="table-responsive">
<table class="table table-hover align-middle m-0" id="jobTable">
<thead class="table-light">
<tr>
<th style="width:64px;">NO</th>
<th style="width:90px;">작업순위</th>
<th style="width:120px;">수주계약일</th>
<th style="width:140px;">발주처</th>
<th>현장명</th>
<th style="width:110px;">제품명</th>
<th style="width:90px;">수량(틀)</th>
<th style="width:130px;">출고요청일</th>
<th style="width:150px;">작업자</th> <!-- 추가 -->
<th style="width:140px;">공정진행현황</th> <!-- 라벨 변경 -->
</tr>
</thead>
<tbody id="jobTbody"><!-- JS로 렌더 --></tbody>
</table>
</div>
<!-- 페이지네이션 -->
<div class="d-flex justify-content-center py-2" id="pager"></div>
</div>
</div>
<!-- 작업지시 모달 (본문 Ajax 로드 그대로 사용) -->
<div class="modal fade" id="workInstructionModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">작업 지시</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="닫기"></button>
</div>
<div class="modal-body p-0" id="wiBody"><!-- /tenant/production/work_instruction.php 로드 --></div>
</div>
</div>
</div>
<!-- 모바일 카드 뷰 -->
<div id="jobCards" class="mt-2"></div>
<style>
/* 헤더/셀 간격 + 한 줄 유지 */
#jobTable th{ vertical-align:middle; text-align:center; white-space:nowrap; padding:.55rem .6rem; }
#jobTable td{ vertical-align:middle; padding:.55rem .6rem; }
/* 정렬: 번호/순위/수량/마지막 2개 */
#jobTable td:nth-child(1),
#jobTable td:nth-child(2),
#jobTable td:nth-child(7),
#jobTable td:nth-last-child(1),
#jobTable td:nth-last-child(2){ text-align:center; }
/* 현장명: 고정폭 + 말줄임 */
#jobTable td:nth-child(5){
max-width: 280px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 상태 뱃지 */
.badge-state{ font-weight:600; }
.badge-wait{ background:#fff1a8; color:#111; } /* 대기 */
.badge-miss{ background:#86b7fe; color:#052c65; } /* 미싱 */
.badge-check{ background:#d1e7dd; color:#0f5132; } /* 중간검사 */
.badge-pack{ background:#cfe2ff; color:#052c65; } /* 포장 */
.badge-done{ background:#adb5bd; }
.badge-cancel{ background:#ffb3b3; }
/* 대기 행 하이라이트(다른 행은 hover 때만) */
.row-waiting{ background:#fff8cc; }
/* 페이지네이션 */
.pagination .page-link{ padding:.35rem .6rem; }
.pagination .page-item.active .page-link{ background:#2c4a85; border-color:#2c4a85; }
/* 모바일: 테이블 → 카드 */
@media (max-width: 767.98px){
.table-responsive{ display:none; }
#jobCards{ display:block; }
}
@media (min-width: 768px){
#jobCards{ display:none; }
}
.job-card .title{ font-weight:700; }
.job-card .meta{ font-size:.9rem; color:#555; }
.job-card.waiting{ background:#fff8cc; }
.search-form .btn{
height:38px; line-height:1.5; padding:0 16px; white-space:nowrap;
}
</style>
<script>
$(function(){
// ------- 샘플 데이터 (공정단계 + 작업자 포함) -------
const rows = [
{no:13, pri:0, od:'2025-07-25', buyer:'명보에스티', site:'일산 동국대병원 3층', prod:'KSS01', qty:10, ship:'2025-08-01', workers:['이강민','김소연'], stage:'대기'},
{no:12, pri:0, od:'2025-07-29', buyer:'명보에스티', site:'일산 동국대병원 2층', prod:'KSS01', qty:10, ship:'2025-08-01', workers:['김소연'], stage:'미싱'},
{no:11, pri:1, od:'2025-07-31', buyer:'이에스디', site:'용인 기흥', prod:'KSS02', qty:2, ship:'2025-08-03', workers:['홍길동'], stage:'중간검사'},
{no:10, pri:2, od:'2025-06-24', buyer:'대성산업', site:'횡덕중학교', prod:'KSS02', qty:9, ship:'2025-08-03', workers:['김철수'], stage:'포장'},
{no:9, pri:3, od:'2025-07-14', buyer:'이엘도어', site:'파주아르디움 2층', prod:'KSS01', qty:5, ship:'2025-08-03', workers:['박지훈'], stage:'포장'},
{no:8, pri:4, od:'2025-07-26', buyer:'주월기업', site:'여의도IFC 1층', prod:'KSS01', qty:5, ship:'2025-08-05', workers:['이강민','유영수'], stage:'중간검사'},
{no:7, pri:5, od:'2025-08-01', buyer:'명보에스티', site:'일산 동국대병원 1층', prod:'KSS01', qty:20, ship:'2025-08-05', workers:['김소연'], stage:'대기'},
{no:6, pri:6, od:'2025-08-01', buyer:'주일기업', site:'여의도IFC 2층', prod:'KSS01', qty:5, ship:'2025-08-05', workers:['홍길동'], stage:'중간검사'},
{no:5, pri:7, od:'2025-08-01', buyer:'주일기업', site:'여의도IFC 3층', prod:'KSS01', qty:5, ship:'2025-08-05', workers:['김철수'], stage:'미싱'},
{no:4, pri:3, od:'2025-08-01', buyer:'이엘도어', site:'파주아르디움 1층', prod:'KSS01', qty:5, ship:'2025-08-07', workers:['박지훈'], stage:'완료'},
{no:3, pri:3, od:'2025-08-03', buyer:'한국특수', site:'인천염림물류창고', prod:'KSS01', qty:5, ship:'2025-08-07', workers:['이강민'], stage:'완료'},
{no:2, pri:2, od:'2025-08-04', buyer:'명보에스티', site:'평택', prod:'KSS02', qty:1, ship:'2025-08-10', workers:['김소연'], stage:'완료'},
{no:1, pri:1, od:'2025-08-04', buyer:'명보에스티', site:'장수', prod:'KSS02', qty:2, ship:'2025-08-10', workers:['홍길동'], stage:'완료'},
];
const pageSize = 10;
let curPage = 1;
let filtered = [...rows];
function stageBadge(stage){
const cls = {
'대기':'badge-wait',
'미싱':'badge-miss',
'중간검사':'badge-check',
'포장':'badge-pack',
'완료':'badge-done',
'취소':'badge-cancel'
}[stage] || 'bg-secondary';
return `<span class="badge badge-state ${cls}">${stage}</span>`;
}
// PC 테이블 렌더
function renderTable(pageItems){
const tbody = pageItems.map(r=>`
<tr class="row-open ${r.stage==='대기'?'row-waiting':''}" data-no="${r.no}">
<td>${r.no}</td>
<td>${r.pri}</td>
<td>${r.od}</td>
<td>${r.buyer}</td>
<td title="${r.site}">${r.site}</td>
<td>${r.prod}</td>
<td>${r.qty}</td>
<td>${r.ship}</td>
<td>${(r.workers||[]).join(', ')}</td>
<td>${stageBadge(r.stage)}</td>
</tr>
`).join('');
$('#jobTbody').html(tbody);
}
// 모바일 카드 렌더
function renderCards(pageItems){
const cards = pageItems.map(r=>`
<div class="card mb-2 job-card ${r.stage==='대기'?'waiting':''}" data-no="${r.no}">
<div class="card-body">
<div class="d-flex justify-content-between">
<div class="title">${r.site}</div>
<div>${stageBadge(r.stage)}</div>
</div>
<div class="meta mt-1">
<span>NO ${r.no}</span> · <span>순위 ${r.pri}</span> · <span>${r.prod}</span> · <span>${r.qty}틀</span>
</div>
<div class="meta">발주처 ${r.buyer} / 출고요청일 ${r.ship}</div>
<div class="meta">작업자 ${(r.workers||[]).join(', ')}</div>
<div class="mt-2 text-end">
<button class="btn btn-sm btn-outline-primary btn-open-wi">작업 지시</button>
</div>
</div>
</div>
`).join('');
$('#jobCards').html(cards);
}
// 페이지네이션 렌더
function renderPager(totalPage){
let html = `
<nav aria-label="pagination">
<ul class="pagination pagination-sm justify-content-center mb-0">
<li class="page-item ${curPage===1?'disabled':''}">
<a class="page-link" href="#" data-page="prev">«</a>
</li>`;
for(let p=1;p<=totalPage;p++){
html += `
<li class="page-item ${p===curPage?'active':''}">
<a class="page-link" href="#" data-page="${p}">${p}</a>
</li>`;
}
html += `
<li class="page-item ${curPage===totalPage?'disabled':''}">
<a class="page-link" href="#" data-page="next">»</a>
</li>
</ul>
</nav>`;
$('#pager').html(html);
}
function render(){
const start = (curPage-1)*pageSize;
const pageItems = filtered.slice(start, start+pageSize);
renderTable(pageItems);
renderCards(pageItems);
renderPager(Math.max(1, Math.ceil(filtered.length / pageSize)));
}
// 검색
$('#searchForm').on('submit', function(e){
e.preventDefault();
const kw = $('#keyword').val().trim().toLowerCase();
const wk = $('#worker').val().trim().toLowerCase();
filtered = rows.filter(r=>{
const s = [r.buyer,r.site,r.prod,r.od,r.stage,String(r.no)].join(' ').toLowerCase();
const w = (r.workers||[]).join(',').toLowerCase();
const inKW = !kw || s.includes(kw);
const inWK = !wk || w.includes(wk);
return inKW && inWK;
});
curPage = 1; render();
});
// 페이지네이션 클릭
$(document).on('click', '.pagination .page-link', function(e){
e.preventDefault();
const val = $(this).data('page');
const total = Math.max(1, Math.ceil(filtered.length / pageSize));
if(val==='prev' && curPage>1) curPage--;
else if(val==='next' && curPage<total) curPage++;
else if(/^\d+$/.test(val)) curPage = parseInt(val,10);
render();
});
// 행/카드 클릭 → 작업지시 모달 로드(동일 흐름 유지)
$(document).on('click', 'tr.row-open, .btn-open-wi', function(){
const $el = $(this).closest('[data-no]');
const no = $el.data('no');
const r = rows.find(x=>x.no==no);
$('#wiBody').html('<div class="p-4 text-center text-muted">로딩 중…</div>');
$('#wiBody').load('/tenant/production/process_detail.php', {
job_id: 'SCR-'+String(no).padStart(3,'0'),
cust: r.buyer,
order_no: r.od.replaceAll('-','')+'-'+String(no).padStart(2,'0')
}, function(){
new bootstrap.Modal('#workInstructionModal').show();
});
});
render();
});
</script>
<?php include '../inc/footer.php'; ?>