feat:개발 진행중 → 승인대기로 이동 기능 추가

- revertToPending 서비스 메서드 추가
- revertToPending 컨트롤러 액션 추가
- /approvals/{id}/revert-pending 라우트 추가
- progress-list에 "승인대기로" 버튼 추가
- JavaScript revertToPending 함수 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-01-31 20:33:44 +09:00
parent e993eb5a0a
commit 28f129393d
5 changed files with 100 additions and 2 deletions

View File

@@ -170,6 +170,41 @@ public function updateStatus(Request $request, int $id)
}
}
/**
* 승인대기로 되돌리기
*/
public function revertToPending(Request $request, int $id)
{
// 권한 체크
if (!auth()->user()->isAdmin()) {
abort(403, '접근 권한이 없습니다.');
}
try {
$management = $this->service->revertToPending($id);
$tenantName = $management->tenant?->company_name ?? '알 수 없음';
if ($request->header('HX-Request')) {
return response()->json([
'success' => true,
'message' => "{$tenantName}이(가) 승인대기로 이동되었습니다.",
]);
}
return redirect()->route('sales.development.approvals.index')
->with('success', "{$tenantName}이(가) 승인대기로 이동되었습니다.");
} catch (\InvalidArgumentException $e) {
if ($request->header('HX-Request')) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
return redirect()->back()->with('error', $e->getMessage());
}
}
/**
* 상세 정보 모달
*/

View File

@@ -202,6 +202,30 @@ public function updateHqStatus(int $id, string $status): SalesTenantManagement
return $management->fresh();
}
/**
* 승인대기로 되돌리기 (진행중 → pending)
*/
public function revertToPending(int $id): SalesTenantManagement
{
$management = SalesTenantManagement::findOrFail($id);
// 이미 pending 상태인 경우
if ($management->hq_status === SalesTenantManagement::HQ_STATUS_PENDING) {
throw new \InvalidArgumentException('이미 승인대기 상태입니다.');
}
// handover(인계) 상태에서는 되돌리기 불가
if ($management->hq_status === SalesTenantManagement::HQ_STATUS_HANDOVER) {
throw new \InvalidArgumentException('인계 완료된 항목은 되돌릴 수 없습니다.');
}
$management->update([
'hq_status' => SalesTenantManagement::HQ_STATUS_PENDING,
]);
return $management->fresh();
}
/**
* 상세 정보 조회
*/

View File

@@ -198,6 +198,38 @@ function updateStatus(id, name) {
);
}
// 승인대기로 되돌리기
function revertToPending(id, name) {
showConfirm(
`<strong>${name}</strong>을(를) 승인대기 상태로 되돌리시겠습니까?<br><small class="text-gray-500">개발 진행 상태가 초기화됩니다.</small>`,
() => {
fetch(`/sales/development/approvals/${id}/revert-pending`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
'HX-Request': 'true'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast(data.message, 'success');
window.location.reload();
} else {
showToast(data.message || '되돌리기에 실패했습니다.', 'error');
}
})
.catch(error => {
showToast('서버 오류가 발생했습니다.', 'error');
console.error(error);
});
},
{ title: '승인대기로 이동', icon: 'warning', confirmText: '이동' }
);
}
// 상세 모달 열기
function openDetailModal(id) {
document.getElementById('detailModal').classList.remove('hidden');

View File

@@ -81,8 +81,13 @@ class="px-2 py-1 bg-purple-500 hover:bg-purple-600 text-white text-xs font-mediu
</span>
</div>
{{-- 상세 버튼 --}}
<div class="flex items-center justify-end">
{{-- 버튼 --}}
<div class="flex items-center justify-end gap-1">
<button type="button"
onclick="revertToPending({{ $item->id }}, '{{ addslashes($companyName) }}')"
class="px-2 py-1 bg-yellow-500 hover:bg-yellow-600 text-white text-xs font-medium rounded transition">
승인대기로
</button>
<button type="button"
onclick="openDetailModal({{ $item->id }})"
class="px-2 py-1 bg-gray-400 hover:bg-gray-500 text-white text-xs font-medium rounded transition">

View File

@@ -897,6 +897,8 @@
->name('approvals.reject');
Route::post('/approvals/{id}/status', [\App\Http\Controllers\Sales\SalesDevelopmentApprovalController::class, 'updateStatus'])
->name('approvals.status');
Route::post('/approvals/{id}/revert-pending', [\App\Http\Controllers\Sales\SalesDevelopmentApprovalController::class, 'revertToPending'])
->name('approvals.revert-pending');
Route::get('/approvals/{id}/detail', [\App\Http\Controllers\Sales\SalesDevelopmentApprovalController::class, 'detail'])
->name('approvals.detail');
});