- 설비 상세 basic-info 탭에 QR 코드 표시 (qrcode.js CDN)
- QR PNG 다운로드/인쇄 기능
- 모바일 전용 레이아웃 (layouts/mobile.blade.php)
- 모바일 점검 페이지 (/m/inspect/{id})
- setResult API (PATCH /inspections/set-result)
- 4버튼 직접 결과 설정 (양호/이상/수리/취소)
- 전체 양호 일괄 처리
- 주기 탭 전환 (활성 주기만 표시)
224 lines
10 KiB
PHP
224 lines
10 KiB
PHP
@extends('layouts.mobile')
|
|
|
|
@section('title', $equipment->name . ' 점검')
|
|
|
|
@section('content')
|
|
<div class="px-4 pt-4">
|
|
{{-- 설비 정보 --}}
|
|
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-xs text-gray-500 font-mono">{{ $equipment->equipment_code }}</p>
|
|
<h1 class="text-lg font-bold text-gray-900">{{ $equipment->name }}</h1>
|
|
</div>
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {{ $equipment->status_color }}">
|
|
{{ $equipment->status_label }}
|
|
</span>
|
|
</div>
|
|
<div class="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm text-gray-600">
|
|
@if($equipment->production_line)
|
|
<span>{{ $equipment->production_line }}</span>
|
|
@endif
|
|
@if($equipment->manager)
|
|
<span>담당: {{ $equipment->manager->name }}</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 날짜 및 주기 탭 --}}
|
|
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<p class="text-sm font-medium text-gray-700">
|
|
{{ \Carbon\Carbon::parse($today)->format('Y-m-d') }}
|
|
<span class="text-gray-400 ml-1">{{ \App\Enums\InspectionCycle::label($cycle) }} 점검</span>
|
|
</p>
|
|
</div>
|
|
|
|
@if(count($activeCycles) > 1)
|
|
<div class="flex gap-2 overflow-x-auto pb-1">
|
|
@foreach($activeCycles as $c)
|
|
<a href="{{ route('mobile.inspect', ['id' => $equipment->id, 'cycle' => $c]) }}"
|
|
class="shrink-0 px-3 py-1.5 rounded-full text-sm font-medium transition
|
|
{{ $cycle === $c ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
|
|
{{ \App\Enums\InspectionCycle::label($c) }}
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
@if($templates->isEmpty())
|
|
<div class="bg-white rounded-xl shadow-sm p-8 text-center">
|
|
<p class="text-gray-400 text-sm">이 주기에 등록된 점검항목이 없습니다.</p>
|
|
</div>
|
|
@else
|
|
{{-- 점검 항목 리스트 --}}
|
|
<div class="space-y-3" id="inspection-list">
|
|
@foreach($templates as $idx => $template)
|
|
@php
|
|
$detail = $details->get($template->id);
|
|
$currentResult = $detail?->result;
|
|
@endphp
|
|
<div class="bg-white rounded-xl shadow-sm p-4" id="item-{{ $template->id }}">
|
|
<div class="flex items-start justify-between mb-2">
|
|
<div class="flex-1 min-w-0">
|
|
<p class="text-sm font-semibold text-gray-900">
|
|
{{ $idx + 1 }}. {{ $template->check_point }}
|
|
<span class="font-normal text-gray-600">- {{ $template->check_item }}</span>
|
|
</p>
|
|
@if($template->check_method)
|
|
<p class="text-xs text-gray-400 mt-0.5">{{ $template->check_method }}</p>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 결과 버튼 --}}
|
|
<div class="flex gap-2 mt-3">
|
|
<button onclick="setResult({{ $template->id }}, 'good', this)"
|
|
class="result-btn flex-1 py-2 rounded-lg text-sm font-medium border transition
|
|
{{ $currentResult === 'good' ? 'bg-green-600 text-white border-green-600' : 'bg-white text-green-700 border-green-300 hover:bg-green-50' }}"
|
|
data-template="{{ $template->id }}" data-result="good"
|
|
{{ !$canInspect ? 'disabled' : '' }}>
|
|
○ 양호
|
|
</button>
|
|
<button onclick="setResult({{ $template->id }}, 'bad', this)"
|
|
class="result-btn flex-1 py-2 rounded-lg text-sm font-medium border transition
|
|
{{ $currentResult === 'bad' ? 'bg-red-600 text-white border-red-600' : 'bg-white text-red-700 border-red-300 hover:bg-red-50' }}"
|
|
data-template="{{ $template->id }}" data-result="bad"
|
|
{{ !$canInspect ? 'disabled' : '' }}>
|
|
X 이상
|
|
</button>
|
|
<button onclick="setResult({{ $template->id }}, 'repaired', this)"
|
|
class="result-btn flex-1 py-2 rounded-lg text-sm font-medium border transition
|
|
{{ $currentResult === 'repaired' ? 'bg-yellow-500 text-white border-yellow-500' : 'bg-white text-yellow-700 border-yellow-300 hover:bg-yellow-50' }}"
|
|
data-template="{{ $template->id }}" data-result="repaired"
|
|
{{ !$canInspect ? 'disabled' : '' }}>
|
|
△ 수리
|
|
</button>
|
|
<button onclick="setResult({{ $template->id }}, null, this)"
|
|
class="result-btn py-2 px-3 rounded-lg text-sm font-medium border transition
|
|
{{ $currentResult === null && $detail === null ? 'bg-gray-200 text-gray-500 border-gray-300' : 'bg-white text-gray-500 border-gray-300 hover:bg-gray-50' }}"
|
|
data-template="{{ $template->id }}" data-result="null"
|
|
{{ !$canInspect ? 'disabled' : '' }}>
|
|
취소
|
|
</button>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
{{-- 요약 + 전체 양호 --}}
|
|
<div class="bg-white rounded-xl shadow-sm p-4 mt-4 mb-6">
|
|
<div class="flex items-center justify-between">
|
|
<p class="text-sm text-gray-600" id="summary-text">
|
|
전체 {{ $templates->count() }}항목 |
|
|
점검 <span id="checked-count">{{ $details->count() }}</span> |
|
|
미점검 <span id="unchecked-count">{{ $templates->count() - $details->count() }}</span>
|
|
</p>
|
|
@if($canInspect)
|
|
<button onclick="setAllGood()"
|
|
class="px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-lg hover:bg-green-700 transition">
|
|
전체 양호 처리
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
(function() {
|
|
const equipmentId = {{ $equipment->id }};
|
|
const checkDate = '{{ $today }}';
|
|
const cycle = '{{ $cycle }}';
|
|
const totalItems = {{ $templates->count() }};
|
|
|
|
const resultStyles = {
|
|
good: { active: 'bg-green-600 text-white border-green-600', inactive: 'bg-white text-green-700 border-green-300 hover:bg-green-50' },
|
|
bad: { active: 'bg-red-600 text-white border-red-600', inactive: 'bg-white text-red-700 border-red-300 hover:bg-red-50' },
|
|
repaired: { active: 'bg-yellow-500 text-white border-yellow-500', inactive: 'bg-white text-yellow-700 border-yellow-300 hover:bg-yellow-50' },
|
|
'null': { active: 'bg-gray-200 text-gray-500 border-gray-300', inactive: 'bg-white text-gray-500 border-gray-300 hover:bg-gray-50' },
|
|
};
|
|
|
|
function updateButtonStyles(templateId, result) {
|
|
const container = document.getElementById('item-' + templateId);
|
|
if (!container) return;
|
|
|
|
container.querySelectorAll('.result-btn').forEach(function(btn) {
|
|
const btnResult = btn.dataset.result;
|
|
const style = resultStyles[btnResult];
|
|
const isActive = (result === null && btnResult === 'null') || (btnResult === result);
|
|
|
|
// 모든 관련 클래스 제거
|
|
btn.className = 'result-btn py-2 rounded-lg text-sm font-medium border transition ' +
|
|
(btnResult === 'null' ? 'px-3' : 'flex-1') + ' ' +
|
|
(isActive ? style.active : style.inactive);
|
|
});
|
|
}
|
|
|
|
function updateSummary() {
|
|
let checked = 0;
|
|
document.querySelectorAll('[id^="item-"]').forEach(function(container) {
|
|
const hasActive = container.querySelector('.result-btn.bg-green-600, .result-btn.bg-red-600, .result-btn.bg-yellow-500');
|
|
if (hasActive) checked++;
|
|
});
|
|
const el = document.getElementById('checked-count');
|
|
const el2 = document.getElementById('unchecked-count');
|
|
if (el) el.textContent = checked;
|
|
if (el2) el2.textContent = totalItems - checked;
|
|
}
|
|
|
|
window.setResult = function(templateItemId, result, btn) {
|
|
if (btn && btn.disabled) return;
|
|
|
|
// 즉시 UI 반영
|
|
updateButtonStyles(templateItemId, result);
|
|
updateSummary();
|
|
|
|
fetch('/api/admin/equipment/inspections/set-result', {
|
|
method: 'PATCH',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': window.CSRF_TOKEN,
|
|
'Accept': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
equipment_id: equipmentId,
|
|
template_item_id: templateItemId,
|
|
check_date: checkDate,
|
|
cycle: cycle,
|
|
result: result,
|
|
}),
|
|
})
|
|
.then(function(res) { return res.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) {
|
|
alert(data.message || '저장에 실패했습니다.');
|
|
location.reload();
|
|
}
|
|
})
|
|
.catch(function() {
|
|
alert('네트워크 오류가 발생했습니다.');
|
|
location.reload();
|
|
});
|
|
};
|
|
|
|
window.setAllGood = function() {
|
|
if (!confirm('모든 항목을 양호로 처리하시겠습니까?')) return;
|
|
|
|
const templateIds = [];
|
|
document.querySelectorAll('[id^="item-"]').forEach(function(container) {
|
|
const id = parseInt(container.id.replace('item-', ''));
|
|
if (id) templateIds.push(id);
|
|
});
|
|
|
|
templateIds.forEach(function(tid) {
|
|
setResult(tid, 'good', null);
|
|
});
|
|
};
|
|
})();
|
|
</script>
|
|
@endpush
|