feat: [equipment] 설비 QR 코드 점검 시스템 추가
- 설비 상세 basic-info 탭에 QR 코드 표시 (qrcode.js CDN)
- QR PNG 다운로드/인쇄 기능
- 모바일 전용 레이아웃 (layouts/mobile.blade.php)
- 모바일 점검 페이지 (/m/inspect/{id})
- setResult API (PATCH /inspections/set-result)
- 4버튼 직접 결과 설정 (양호/이상/수리/취소)
- 전체 양호 일괄 처리
- 주기 탭 전환 (활성 주기만 표시)
This commit is contained in:
@@ -79,6 +79,39 @@ public function toggleDetail(Request $request): JsonResponse
|
||||
}
|
||||
}
|
||||
|
||||
public function setResult(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'equipment_id' => 'required|integer',
|
||||
'template_item_id' => 'required|integer',
|
||||
'check_date' => 'required|date',
|
||||
'cycle' => 'nullable|string',
|
||||
'result' => 'nullable|in:good,bad,repaired',
|
||||
]);
|
||||
|
||||
try {
|
||||
$result = $this->inspectionService->setResult(
|
||||
$request->input('equipment_id'),
|
||||
$request->input('template_item_id'),
|
||||
$request->input('check_date'),
|
||||
$request->input('cycle', InspectionCycle::DAILY),
|
||||
$request->input('result')
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $result,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$status = $e->getMessage() === '점검 권한이 없습니다.' ? 403 : 400;
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
], $status);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateNotes(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
|
||||
62
app/Http/Controllers/Mobile/MobileInspectionController.php
Normal file
62
app/Http/Controllers/Mobile/MobileInspectionController.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Mobile;
|
||||
|
||||
use App\Enums\InspectionCycle;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Equipment\Equipment;
|
||||
use App\Models\Equipment\EquipmentInspection;
|
||||
use App\Models\Equipment\EquipmentInspectionDetail;
|
||||
use App\Models\Equipment\EquipmentInspectionTemplate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class MobileInspectionController extends Controller
|
||||
{
|
||||
public function show(Request $request, int $id): View
|
||||
{
|
||||
$equipment = Equipment::with(['manager', 'subManager'])->findOrFail($id);
|
||||
|
||||
$cycle = $request->input('cycle', InspectionCycle::DAILY);
|
||||
$today = now()->format('Y-m-d');
|
||||
$period = InspectionCycle::resolvePeriod($cycle, $today);
|
||||
|
||||
$activeCycles = EquipmentInspectionTemplate::where('equipment_id', $equipment->id)
|
||||
->where('is_active', true)
|
||||
->distinct()
|
||||
->pluck('inspection_cycle')
|
||||
->toArray();
|
||||
|
||||
$templates = EquipmentInspectionTemplate::where('equipment_id', $equipment->id)
|
||||
->where('inspection_cycle', $cycle)
|
||||
->where('is_active', true)
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
|
||||
$inspection = EquipmentInspection::where('equipment_id', $equipment->id)
|
||||
->where('inspection_cycle', $cycle)
|
||||
->where('year_month', $period)
|
||||
->first();
|
||||
|
||||
$details = collect();
|
||||
if ($inspection) {
|
||||
$details = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
|
||||
->where('check_date', $today)
|
||||
->get()
|
||||
->keyBy('template_item_id');
|
||||
}
|
||||
|
||||
$canInspect = $equipment->canInspect();
|
||||
|
||||
return view('mobile.inspection.show', compact(
|
||||
'equipment',
|
||||
'cycle',
|
||||
'today',
|
||||
'period',
|
||||
'activeCycles',
|
||||
'templates',
|
||||
'details',
|
||||
'canInspect',
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,62 @@ public function toggleDetail(int $equipmentId, int $templateItemId, string $chec
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 점검 결과 직접 설정 (모바일용)
|
||||
*/
|
||||
public function setResult(int $equipmentId, int $templateItemId, string $checkDate, string $cycle, ?string $result): array
|
||||
{
|
||||
$equipment = Equipment::findOrFail($equipmentId);
|
||||
if (! $equipment->canInspect()) {
|
||||
throw new \Exception('점검 권한이 없습니다.');
|
||||
}
|
||||
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
$period = InspectionCycle::resolvePeriod($cycle, $checkDate);
|
||||
|
||||
$inspection = EquipmentInspection::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenantId,
|
||||
'equipment_id' => $equipmentId,
|
||||
'inspection_cycle' => $cycle,
|
||||
'year_month' => $period,
|
||||
],
|
||||
[
|
||||
'created_by' => auth()->id(),
|
||||
]
|
||||
);
|
||||
|
||||
$detail = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
|
||||
->where('template_item_id', $templateItemId)
|
||||
->where('check_date', $checkDate)
|
||||
->first();
|
||||
|
||||
if ($result === null) {
|
||||
if ($detail) {
|
||||
$detail->delete();
|
||||
}
|
||||
|
||||
return ['result' => null, 'symbol' => '', 'color' => 'text-gray-400'];
|
||||
}
|
||||
|
||||
if ($detail) {
|
||||
$detail->update(['result' => $result]);
|
||||
} else {
|
||||
$detail = EquipmentInspectionDetail::create([
|
||||
'inspection_id' => $inspection->id,
|
||||
'template_item_id' => $templateItemId,
|
||||
'check_date' => $checkDate,
|
||||
'result' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'result' => $result,
|
||||
'symbol' => $detail->fresh()->result_symbol,
|
||||
'color' => $detail->fresh()->result_color,
|
||||
];
|
||||
}
|
||||
|
||||
public function updateInspectionNotes(int $equipmentId, string $yearMonth, array $data, string $cycle = 'daily'): EquipmentInspection
|
||||
{
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
|
||||
16
app/Services/QrCodeService.php
Normal file
16
app/Services/QrCodeService.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class QrCodeService
|
||||
{
|
||||
/**
|
||||
* 모바일 점검 페이지 URL 생성
|
||||
*/
|
||||
public static function inspectionUrl(int $equipmentId): string
|
||||
{
|
||||
$baseUrl = rtrim(config('app.url'), '/');
|
||||
|
||||
return $baseUrl.'/m/inspect/'.$equipmentId;
|
||||
}
|
||||
}
|
||||
75
resources/views/equipment/partials/qr-code.blade.php
Normal file
75
resources/views/equipment/partials/qr-code.blade.php
Normal file
@@ -0,0 +1,75 @@
|
||||
{{-- QR 코드 (모바일 점검용) --}}
|
||||
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 mb-4 pb-2 border-b">QR 코드 (모바일 점검)</h2>
|
||||
<div class="flex items-start gap-6">
|
||||
<div>
|
||||
<div id="qr-code-container" class="bg-white p-2 border rounded-lg inline-block"></div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-mono text-gray-700 mb-1">{{ $equipment->equipment_code }}</p>
|
||||
<p class="text-base font-semibold text-gray-900 mb-3">{{ $equipment->name }}</p>
|
||||
<p class="text-xs text-gray-500 mb-4 break-all" id="qr-url-text"></p>
|
||||
<div class="flex gap-2">
|
||||
<button onclick="downloadQrCode()" class="inline-flex items-center px-3 py-1.5 text-sm font-medium text-blue-700 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 transition">
|
||||
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
PNG 다운로드
|
||||
</button>
|
||||
<button onclick="printQrCode()" class="inline-flex items-center px-3 py-1.5 text-sm font-medium text-gray-700 bg-gray-50 border border-gray-200 rounded-lg hover:bg-gray-100 transition">
|
||||
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/></svg>
|
||||
인쇄
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
const qrUrl = @json(\App\Services\QrCodeService::inspectionUrl($equipment->id));
|
||||
const equipCode = @json($equipment->equipment_code);
|
||||
const equipName = @json($equipment->name);
|
||||
|
||||
document.getElementById('qr-url-text').textContent = qrUrl;
|
||||
|
||||
new QRCode(document.getElementById('qr-code-container'), {
|
||||
text: qrUrl,
|
||||
width: 150,
|
||||
height: 150,
|
||||
colorDark: '#000000',
|
||||
colorLight: '#ffffff',
|
||||
correctLevel: QRCode.CorrectLevel.M,
|
||||
});
|
||||
|
||||
window.downloadQrCode = function() {
|
||||
const canvas = document.querySelector('#qr-code-container canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
canvas.toBlob(function(blob) {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'QR_' + equipCode + '.png';
|
||||
a.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
}, 'image/png');
|
||||
};
|
||||
|
||||
window.printQrCode = function() {
|
||||
const canvas = document.querySelector('#qr-code-container canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/png');
|
||||
const win = window.open('', '_blank', 'width=400,height=500');
|
||||
win.document.write(`<!DOCTYPE html><html><head><title>QR 코드 인쇄</title>
|
||||
<style>body{text-align:center;font-family:sans-serif;padding:20px}
|
||||
img{margin:20px auto}p{margin:8px 0}</style></head><body>
|
||||
<img src="${dataUrl}" width="200" height="200">
|
||||
<p style="font-weight:bold;font-size:16px">${equipCode}</p>
|
||||
<p style="font-size:14px">${equipName}</p>
|
||||
<p style="font-size:10px;color:#888">${qrUrl}</p>
|
||||
<script>window.onload=function(){window.print();}<\/script>
|
||||
</body></html>`);
|
||||
win.document.close();
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
@@ -145,6 +145,8 @@ function closePhotoModal(e) {
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@include('equipment.partials.qr-code', ['equipment' => $equipment])
|
||||
|
||||
@if($equipment->processes->isNotEmpty())
|
||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 mb-4 pb-2 border-b">연결된 공정</h2>
|
||||
|
||||
29
resources/views/layouts/mobile.blade.php
Normal file
29
resources/views/layouts/mobile.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>@yield('title', '점검') - SAM</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
<style>
|
||||
body { -webkit-tap-highlight-color: transparent; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
{{-- 최소 헤더 --}}
|
||||
<header class="bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between sticky top-0 z-10">
|
||||
<span class="text-lg font-bold text-blue-600">SAM</span>
|
||||
<span class="text-sm text-gray-600">{{ auth()->user()->name }} 님</span>
|
||||
</header>
|
||||
|
||||
<main class="pb-6">
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
<script>
|
||||
window.CSRF_TOKEN = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
</script>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
223
resources/views/mobile/inspection/show.blade.php
Normal file
223
resources/views/mobile/inspection/show.blade.php
Normal file
@@ -0,0 +1,223 @@
|
||||
@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
|
||||
@@ -1029,6 +1029,7 @@
|
||||
Route::get('/inspections', [\App\Http\Controllers\Api\Admin\EquipmentInspectionController::class, 'index'])->name('inspections.index');
|
||||
Route::patch('/inspections/detail', [\App\Http\Controllers\Api\Admin\EquipmentInspectionController::class, 'toggleDetail'])->name('inspections.toggle');
|
||||
Route::patch('/inspections/notes', [\App\Http\Controllers\Api\Admin\EquipmentInspectionController::class, 'updateNotes'])->name('inspections.notes');
|
||||
Route::patch('/inspections/set-result', [\App\Http\Controllers\Api\Admin\EquipmentInspectionController::class, 'setResult'])->name('inspections.set-result');
|
||||
|
||||
// 수리이력
|
||||
Route::get('/repairs', [\App\Http\Controllers\Api\Admin\EquipmentRepairController::class, 'index'])->name('repairs.index');
|
||||
|
||||
@@ -1667,6 +1667,15 @@
|
||||
Route::delete('/history', [\App\Http\Controllers\Video\TutorialVideoController::class, 'destroy'])->name('destroy');
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 모바일 점검 (QR 코드 → 모바일 점검)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::middleware(['auth', 'hq.member'])->group(function () {
|
||||
Route::get('/m/inspect/{id}', [\App\Http\Controllers\Mobile\MobileInspectionController::class, 'show'])->whereNumber('id')->name('mobile.inspect');
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 설비관리 (Equipment Management)
|
||||
|
||||
Reference in New Issue
Block a user