diff --git a/app/Http/Controllers/ApiLogController.php b/app/Http/Controllers/ApiLogController.php
index 4b8a2520..29d9ae2b 100644
--- a/app/Http/Controllers/ApiLogController.php
+++ b/app/Http/Controllers/ApiLogController.php
@@ -13,9 +13,17 @@ class ApiLogController extends Controller
*/
public function index(Request $request): View
{
+ // 필터가 적용되면 오래된 순, 아니면 최신순
+ $hasFilter = $request->hasAny(['method', 'status', 'search', 'group_id', 'tenant_id']);
+
$query = ApiRequestLog::query()
- ->with(['tenant', 'user'])
- ->orderByDesc('created_at');
+ ->with(['tenant', 'user']);
+
+ if ($hasFilter) {
+ $query->orderBy('created_at'); // 오래된 순
+ } else {
+ $query->orderByDesc('created_at'); // 최신순
+ }
// 필터: HTTP 메서드
if ($request->filled('method')) {
@@ -81,16 +89,23 @@ public function show(int $id): View
{
$log = ApiRequestLog::with(['tenant', 'user'])->findOrFail($id);
- // 같은 그룹의 다른 요청들
- $groupLogs = [];
+ // 같은 그룹의 다른 요청들 (오래된 순)
+ $groupLogs = collect();
+ $groupMethodCounts = [];
if ($log->group_id) {
$groupLogs = ApiRequestLog::where('group_id', $log->group_id)
->where('id', '!=', $log->id)
- ->orderByDesc('created_at')
+ ->orderBy('created_at') // 오래된 순
->get();
+
+ // 메서드별 개수 집계
+ $groupMethodCounts = $groupLogs->groupBy('method')
+ ->map(fn($items) => $items->count())
+ ->sortKeys()
+ ->toArray();
}
- return view('api-logs.show', compact('log', 'groupLogs'));
+ return view('api-logs.show', compact('log', 'groupLogs', 'groupMethodCounts'));
}
/**
diff --git a/resources/views/api-logs/index.blade.php b/resources/views/api-logs/index.blade.php
index 2d3f7554..0582192b 100644
--- a/resources/views/api-logs/index.blade.php
+++ b/resources/views/api-logs/index.blade.php
@@ -7,21 +7,47 @@
@@ -230,7 +256,7 @@ class="text-purple-600 hover:text-purple-800" title="{{ $log->group_id }} ({{ $g
@php
$responseData = json_decode($log->response_body, true);
if ($responseData !== null) {
- $displayResponse = json_encode($responseData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
+ $displayResponse = stripslashes(json_encode($responseData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
} else {
$displayResponse = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function($m) {
return mb_convert_encoding(pack('H*', $m[1]), 'UTF-8', 'UTF-16BE');
@@ -238,7 +264,7 @@ class="text-purple-600 hover:text-purple-800" title="{{ $log->group_id }} ({{ $g
$displayResponse = str_replace('\\/', '/', $displayResponse);
}
@endphp
-
{{ Str::limit($displayResponse, 2000) }}
+
{!! Str::limit($displayResponse, 2000) !!}
@@ -274,13 +300,88 @@ function copyAiAnalysis(id) {
const textarea = document.getElementById('ai-analysis-' + id);
if (textarea) {
navigator.clipboard.writeText(textarea.value).then(() => {
- alert('AI 분석용 내용이 클립보드에 복사되었습니다.\nClaude나 ChatGPT에 붙여넣기 하세요.');
+ showAlertModal('복사 완료', 'AI 분석용 내용이 클립보드에 복사되었습니다.\nClaude나 ChatGPT에 붙여넣기 하세요.', 'green');
}).catch(err => {
console.error('복사 실패:', err);
- alert('복사에 실패했습니다.');
+ showAlertModal('복사 실패', '복사에 실패했습니다.', 'red');
});
}
}
+
+// 커스텀 확인 모달
+let currentFormId = null;
+
+function showConfirmModal(formId, title, message, color) {
+ currentFormId = formId;
+
+ const modal = document.getElementById('confirmModal');
+ const modalIcon = document.getElementById('modalIcon');
+ const modalTitle = document.getElementById('modalTitle');
+ const modalMessage = document.getElementById('modalMessage');
+ const modalConfirmBtn = document.getElementById('modalConfirmBtn');
+
+ modalTitle.textContent = title;
+ modalMessage.textContent = message;
+
+ // 색상 설정
+ const colors = {
+ yellow: { icon: 'bg-yellow-500', btn: 'bg-yellow-600 hover:bg-yellow-700' },
+ red: { icon: 'bg-red-500', btn: 'bg-red-600 hover:bg-red-700' },
+ green: { icon: 'bg-green-500', btn: 'bg-green-600 hover:bg-green-700' },
+ blue: { icon: 'bg-blue-500', btn: 'bg-blue-600 hover:bg-blue-700' }
+ };
+ const colorSet = colors[color] || colors.blue;
+
+ modalIcon.className = `w-10 h-10 rounded-full flex items-center justify-center ${colorSet.icon}`;
+ modalConfirmBtn.className = `px-4 py-2 text-white rounded-lg transition ${colorSet.btn}`;
+
+ modal.classList.remove('hidden');
+}
+
+function hideConfirmModal() {
+ const modal = document.getElementById('confirmModal');
+ modal.classList.add('hidden');
+ currentFormId = null;
+}
+
+function confirmAction() {
+ if (currentFormId) {
+ const form = document.getElementById(currentFormId + 'Form');
+ if (form) {
+ form.submit();
+ }
+ }
+ hideConfirmModal();
+}
+
+// 커스텀 알림 모달 (alert 대체)
+function showAlertModal(title, message, color = 'blue') {
+ const modal = document.getElementById('confirmModal');
+ const modalIcon = document.getElementById('modalIcon');
+ const modalTitle = document.getElementById('modalTitle');
+ const modalMessage = document.getElementById('modalMessage');
+ const modalConfirmBtn = document.getElementById('modalConfirmBtn');
+
+ modalTitle.textContent = title;
+ modalMessage.textContent = message;
+
+ const colors = {
+ yellow: { icon: 'bg-yellow-500', btn: 'bg-yellow-600 hover:bg-yellow-700' },
+ red: { icon: 'bg-red-500', btn: 'bg-red-600 hover:bg-red-700' },
+ green: { icon: 'bg-green-500', btn: 'bg-green-600 hover:bg-green-700' },
+ blue: { icon: 'bg-blue-500', btn: 'bg-blue-600 hover:bg-blue-700' }
+ };
+ const colorSet = colors[color] || colors.blue;
+
+ modalIcon.className = `w-10 h-10 rounded-full flex items-center justify-center ${colorSet.icon}`;
+ modalConfirmBtn.className = `px-4 py-2 text-white rounded-lg transition ${colorSet.btn}`;
+ modalConfirmBtn.textContent = '확인';
+
+ // 알림 모드: 확인 버튼만 닫기 기능으로 변경
+ currentFormId = null;
+
+ modal.classList.remove('hidden');
+}
diff --git a/resources/views/api-logs/show.blade.php b/resources/views/api-logs/show.blade.php
index f6cffdf5..fbd6cd51 100644
--- a/resources/views/api-logs/show.blade.php
+++ b/resources/views/api-logs/show.blade.php
@@ -104,57 +104,93 @@ class="text-purple-600 hover:text-purple-800 font-mono text-xs">
@if($log->group_id && count($groupLogs) > 0)
-