feat: [equipment] 점검 데이터 초기화 기능 추가

- 개별 설비 초기화: 장비명 하단 초기화 아이콘 클릭 → 확인 → 해당 월 점검 삭제
- 전체 초기화: 조회 버튼 옆 '전체 초기화' 버튼 → 확인 → 전체 설비 점검 삭제
- DELETE /inspections/reset (개별), /inspections/reset-all (전체) API
- canInspect 권한 체크 적용 (개별 초기화)
- SweetAlert 확인 모달로 실수 방지
This commit is contained in:
김보곤
2026-02-28 15:46:01 +09:00
parent bdc1b2d3e0
commit 3d8606f4d5
5 changed files with 204 additions and 1 deletions

View File

@@ -148,6 +148,62 @@ public function updateNotes(Request $request): JsonResponse
}
}
public function resetInspection(Request $request): JsonResponse
{
$request->validate([
'equipment_id' => 'required|integer',
'cycle' => 'required|string',
'period' => 'required|string',
]);
try {
$deleted = $this->inspectionService->resetEquipmentInspection(
$request->input('equipment_id'),
$request->input('cycle'),
$request->input('period')
);
return response()->json([
'success' => true,
'message' => "점검 데이터 {$deleted}건이 초기화되었습니다.",
'data' => ['deleted' => $deleted],
]);
} catch (\Exception $e) {
$status = $e->getMessage() === '점검 권한이 없습니다.' ? 403 : 400;
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], $status);
}
}
public function resetAllInspections(Request $request): JsonResponse
{
$request->validate([
'cycle' => 'required|string',
'period' => 'required|string',
]);
try {
$deleted = $this->inspectionService->resetAllInspections(
$request->input('cycle'),
$request->input('period')
);
return response()->json([
'success' => true,
'message' => "전체 점검 데이터 {$deleted}건이 초기화되었습니다.",
'data' => ['deleted' => $deleted],
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
public function storeTemplate(Request $request, int $equipmentId): JsonResponse
{
$request->validate([

View File

@@ -224,6 +224,71 @@ public function updateInspectionNotes(int $equipmentId, string $yearMonth, array
return $inspection->fresh();
}
/**
* 개별 설비 점검 데이터 초기화 (해당 주기/기간)
*/
public function resetEquipmentInspection(int $equipmentId, string $cycle, string $period): int
{
$equipment = Equipment::findOrFail($equipmentId);
if (! $equipment->canInspect()) {
throw new \Exception('점검 권한이 없습니다.');
}
$tenantId = session('selected_tenant_id', 1);
$inspection = EquipmentInspection::where('tenant_id', $tenantId)
->where('equipment_id', $equipmentId)
->where('inspection_cycle', $cycle)
->where('year_month', $period)
->first();
if (! $inspection) {
return 0;
}
$deleted = EquipmentInspectionDetail::where('inspection_id', $inspection->id)->delete();
$inspection->update([
'overall_judgment' => null,
'repair_note' => null,
'issue_note' => null,
'inspector_id' => null,
'updated_by' => auth()->id(),
]);
return $deleted;
}
/**
* 전체 점검 데이터 초기화 (해당 주기/기간의 모든 설비)
*/
public function resetAllInspections(string $cycle, string $period): int
{
$tenantId = session('selected_tenant_id', 1);
$inspections = EquipmentInspection::where('tenant_id', $tenantId)
->where('inspection_cycle', $cycle)
->where('year_month', $period)
->get();
if ($inspections->isEmpty()) {
return 0;
}
$totalDeleted = 0;
foreach ($inspections as $inspection) {
$totalDeleted += EquipmentInspectionDetail::where('inspection_id', $inspection->id)->delete();
$inspection->update([
'overall_judgment' => null,
'repair_note' => null,
'issue_note' => null,
'inspector_id' => null,
'updated_by' => auth()->id(),
]);
}
return $totalDeleted;
}
public function getMonthlyStats(string $yearMonth): array
{
$totalEquipments = Equipment::where('is_active', true)

View File

@@ -62,10 +62,13 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none foc
@endforeach
</select>
</div>
<div class="self-end">
<div class="self-end flex gap-2">
<button type="submit" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded-lg transition">
조회
</button>
<button type="button" onclick="resetAllInspections()" class="bg-red-50 hover:bg-red-100 text-red-600 border border-red-200 px-4 py-2 rounded-lg transition text-sm font-medium">
전체 초기화
</button>
</div>
</form>
</x-filter-collapsible>
@@ -127,6 +130,77 @@ function updatePeriodValue() {
}
}
function getCurrentPeriod() {
if (currentCycle === 'daily') {
return document.getElementById('periodMonth').value;
}
return document.getElementById('periodYear').value;
}
function resetEquipment(equipmentId, equipmentName) {
const period = getCurrentPeriod();
showConfirm(
`<strong>${equipmentName}</strong>의 <strong>${period}</strong> 점검 데이터를 모두 초기화하시겠습니까?<br><span class="text-sm text-gray-500">이 작업은 되돌릴 수 없습니다.</span>`,
function() {
fetch('/api/admin/equipment/inspections/reset', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
},
body: JSON.stringify({
equipment_id: equipmentId,
cycle: currentCycle,
period: period,
})
})
.then(r => r.json())
.then(data => {
if (data.success) {
showToast(data.message, 'success');
htmx.trigger('#inspection-grid', 'filterSubmit');
} else {
showToast(data.message || '초기화에 실패했습니다.', 'error');
}
});
},
{ title: '점검 초기화', icon: 'warning', confirmText: '초기화', cancelText: '취소' }
);
}
function resetAllInspections() {
const period = getCurrentPeriod();
const cycleLabel = document.querySelector('.cycle-tab.bg-white')?.textContent?.trim() || currentCycle;
showConfirm(
`<strong>${period}</strong> ${cycleLabel} 점검의 <span class="text-red-600 font-bold">전체 데이터</span>를 초기화하시겠습니까?<br><span class="text-sm text-gray-500">모든 설비의 점검 기록이 삭제됩니다. 이 작업은 되돌릴 수 없습니다.</span>`,
function() {
fetch('/api/admin/equipment/inspections/reset-all', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
},
body: JSON.stringify({
cycle: currentCycle,
period: period,
})
})
.then(r => r.json())
.then(data => {
if (data.success) {
showToast(data.message, 'success');
htmx.trigger('#inspection-grid', 'filterSubmit');
} else {
showToast(data.message || '초기화에 실패했습니다.', 'error');
}
});
},
{ title: '전체 점검 초기화', icon: 'warning', confirmText: '전체 초기화', cancelText: '취소' }
);
}
function toggleCell(equipmentId, templateItemId, checkDate, cell) {
fetch('/api/admin/equipment/inspections/detail', {
method: 'PATCH',

View File

@@ -75,6 +75,12 @@
rowspan="{{ $rowCount }}" style="min-width: 80px;">
<div class="text-xs text-blue-600">{{ $equipment->equipment_code }}</div>
<div class="text-xs">{{ Str::limit($equipment->name, 8) }}</div>
@if($canInspect)
<button type="button" onclick="resetEquipment({{ $equipment->id }}, '{{ addslashes($equipment->equipment_code . ' ' . $equipment->name) }}')"
class="mt-1 text-gray-400 hover:text-red-500 transition" title="점검 초기화">
<svg class="w-3.5 h-3.5 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
</button>
@endif
</td>
@endif
<td class="border border-gray-300 px-2 py-1 whitespace-nowrap sticky bg-white z-10" style="left: 80px; min-width: 80px;">

View File

@@ -1030,6 +1030,8 @@
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::delete('/inspections/reset', [\App\Http\Controllers\Api\Admin\EquipmentInspectionController::class, 'resetInspection'])->name('inspections.reset');
Route::delete('/inspections/reset-all', [\App\Http\Controllers\Api\Admin\EquipmentInspectionController::class, 'resetAllInspections'])->name('inspections.reset-all');
// 수리이력
Route::get('/repairs', [\App\Http\Controllers\Api\Admin\EquipmentRepairController::class, 'index'])->name('repairs.index');