Files
sam-manage/resources/views/rd/ai-quotation/show.blade.php
김보곤 2803e4a53a fix: [rd] API 호출 URL /admin → /api/admin 수정
- index, create, show 뷰의 fetch URL을 /api/admin/rd/... 로 수정
- api.php 라우트는 api/ prefix가 자동 적용됨
2026-03-02 18:03:08 +09:00

297 lines
16 KiB
PHP

@extends('layouts.app')
@section('title', 'AI 견적 상세')
@section('content')
<!-- 페이지 헤더 -->
<div class="flex justify-between items-center mb-6">
<div class="flex items-center gap-3">
<h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2">
<i class="ri-robot-line text-purple-600"></i>
{{ $quotation->title }}
</h1>
<span class="badge {{ $quotation->status_color }}">{{ $quotation->status_label }}</span>
</div>
<div class="flex gap-2">
<a href="{{ route('rd.ai-quotation.index') }}" class="bg-white hover:bg-gray-100 text-gray-700 px-4 py-2 rounded-lg border transition">
<i class="ri-arrow-left-line"></i> 목록
</a>
@if($quotation->isCompleted() || $quotation->status === 'failed')
<button onclick="reanalyze()" id="reanalyzeBtn"
class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-lg transition">
<i class="ri-refresh-line"></i> AI 재분석
</button>
@endif
</div>
</div>
<!-- 기본 정보 -->
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<p class="text-xs text-gray-500 mb-1">AI Provider</p>
<p class="font-medium text-gray-800">{{ strtoupper($quotation->ai_provider) }}{{ $quotation->ai_model ? ' ('.$quotation->ai_model.')' : '' }}</p>
</div>
<div>
<p class="text-xs text-gray-500 mb-1">입력 유형</p>
<p class="font-medium text-gray-800">{{ ['text' => '텍스트', 'voice' => '음성', 'document' => '문서'][$quotation->input_type] ?? $quotation->input_type }}</p>
</div>
<div>
<p class="text-xs text-gray-500 mb-1">요청자</p>
<p class="font-medium text-gray-800">{{ $quotation->creator?->name ?? '-' }}</p>
</div>
<div>
<p class="text-xs text-gray-500 mb-1">생성일</p>
<p class="font-medium text-gray-800">{{ $quotation->created_at->format('Y-m-d H:i') }}</p>
</div>
</div>
</div>
@if($quotation->isCompleted())
<!-- 업무 분석 결과 -->
@if($quotation->analysis_result)
@php $analysis = $quotation->analysis_result; @endphp
<div class="bg-white rounded-lg shadow-sm mb-6">
<div class="px-6 py-4 border-b border-gray-100">
<h2 class="text-lg font-semibold text-gray-800 flex items-center gap-2">
<i class="ri-search-eye-line text-blue-600"></i> AI 업무 분석 결과
</h2>
</div>
<div class="p-6">
<!-- 기업 분석 -->
@if(isset($analysis['company_analysis']))
@php $company = $analysis['company_analysis']; @endphp
<div class="flex flex-wrap gap-4 mb-6 pb-6 border-b border-gray-100">
<div class="px-4 py-2 bg-blue-50 rounded-lg">
<span class="text-xs text-blue-500 block">업종</span>
<span class="font-semibold text-blue-800">{{ $company['industry'] ?? '-' }}</span>
</div>
<div class="px-4 py-2 bg-green-50 rounded-lg">
<span class="text-xs text-green-500 block">규모</span>
<span class="font-semibold text-green-800">{{ $company['scale'] ?? '-' }}</span>
</div>
<div class="px-4 py-2 bg-purple-50 rounded-lg">
<span class="text-xs text-purple-500 block">디지털화 수준</span>
<span class="font-semibold text-purple-800">{{ $company['digitalization_level'] ?? '-' }}</span>
</div>
@if(!empty($company['current_systems']))
<div class="px-4 py-2 bg-gray-50 rounded-lg">
<span class="text-xs text-gray-500 block">현재 시스템</span>
<span class="font-semibold text-gray-800">{{ implode(', ', $company['current_systems']) }}</span>
</div>
@endif
</div>
@endif
<!-- 업무 영역별 분석 -->
@if(!empty($analysis['business_domains']))
<h3 class="text-sm font-semibold text-gray-600 uppercase mb-3">업무 영역 분석</h3>
<div class="space-y-4">
@foreach($analysis['business_domains'] as $domain)
<div class="border border-gray-200 rounded-lg p-4">
<div class="flex items-center justify-between mb-2">
<h4 class="font-semibold text-gray-800">{{ $domain['domain'] ?? '' }}</h4>
@php
$priorityColor = match($domain['priority'] ?? '') {
'필수' => 'bg-red-100 text-red-700',
'높음' => 'bg-orange-100 text-orange-700',
'보통' => 'bg-yellow-100 text-yellow-700',
default => 'bg-gray-100 text-gray-700',
};
@endphp
<span class="px-2 py-0.5 text-xs rounded-full {{ $priorityColor }}">{{ $domain['priority'] ?? '' }}</span>
</div>
<p class="text-sm text-gray-600 mb-2">{{ $domain['current_process'] ?? '' }}</p>
@if(!empty($domain['pain_points']))
<div class="flex flex-wrap gap-1.5">
@foreach($domain['pain_points'] as $point)
<span class="px-2 py-0.5 bg-red-50 text-red-600 text-xs rounded">{{ $point }}</span>
@endforeach
</div>
@endif
@if(!empty($domain['matched_modules']))
<div class="flex flex-wrap gap-1.5 mt-2">
@foreach($domain['matched_modules'] as $mod)
<span class="px-2 py-0.5 bg-purple-50 text-purple-600 text-xs rounded font-mono">{{ $mod }}</span>
@endforeach
</div>
@endif
</div>
@endforeach
</div>
@endif
</div>
</div>
@endif
<!-- 추천 모듈 + 견적 -->
<div class="bg-white rounded-lg shadow-sm mb-6">
<div class="px-6 py-4 border-b border-gray-100">
<h2 class="text-lg font-semibold text-gray-800 flex items-center gap-2">
<i class="ri-price-tag-3-line text-green-600"></i> 추천 모듈 견적
</h2>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">구분</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">모듈</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">추천 근거</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">개발비</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase"> 구독료</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@foreach($quotation->items as $item)
<tr class="hover:bg-gray-50">
<td class="px-4 py-3">
@if($item->is_required)
<span class="px-2 py-0.5 bg-red-100 text-red-700 text-xs rounded-full font-medium">필수</span>
@else
<span class="px-2 py-0.5 bg-gray-100 text-gray-600 text-xs rounded-full">선택</span>
@endif
</td>
<td class="px-4 py-3">
<div class="font-medium text-gray-800">{{ $item->module_name }}</div>
<div class="text-xs text-gray-400 font-mono">{{ $item->module_code }}</div>
</td>
<td class="px-4 py-3 text-gray-600 text-xs" style="max-width: 300px;">
{{ Str::limit($item->reason, 100) }}
</td>
<td class="px-4 py-3 text-right font-medium text-gray-800">{{ number_format((int)$item->dev_cost) }}</td>
<td class="px-4 py-3 text-right font-medium text-gray-800">{{ number_format((int)$item->monthly_fee) }}</td>
</tr>
@endforeach
</tbody>
<tfoot class="bg-purple-50 border-t-2 border-purple-200">
<tr>
<td colspan="3" class="px-4 py-3 text-right font-bold text-gray-800">합계</td>
<td class="px-4 py-3 text-right font-bold text-purple-700 text-base">{{ number_format((int)$quotation->total_dev_cost) }}</td>
<td class="px-4 py-3 text-right font-bold text-purple-700 text-base">{{ number_format((int)$quotation->total_monthly_fee) }}/</td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- 구현 계획 (AI 생성) -->
@if(!empty($quotation->quotation_result['implementation_plan']))
@php $plan = $quotation->quotation_result['implementation_plan']; @endphp
<div class="bg-white rounded-lg shadow-sm mb-6">
<div class="px-6 py-4 border-b border-gray-100">
<h2 class="text-lg font-semibold text-gray-800 flex items-center gap-2">
<i class="ri-calendar-schedule-line text-indigo-600"></i> 구현 계획 (AI 추천)
</h2>
</div>
<div class="p-6">
<p class="text-sm text-gray-600 mb-4">예상 기간: <span class="font-semibold">{{ $plan['estimated_months'] ?? '?' }}개월</span></p>
@if(!empty($plan['phases']))
<div class="space-y-3">
@foreach($plan['phases'] as $phase)
<div class="flex items-center gap-4 p-3 bg-gray-50 rounded-lg">
<div class="w-10 h-10 bg-indigo-100 text-indigo-700 rounded-full flex items-center justify-center font-bold shrink-0">
{{ $phase['phase'] ?? '' }}
</div>
<div>
<p class="font-medium text-gray-800">{{ $phase['name'] ?? '' }}</p>
<div class="flex items-center gap-2 mt-1">
<span class="text-xs text-gray-500">{{ $phase['duration_weeks'] ?? '?' }}</span>
@if(!empty($phase['modules']))
<span class="text-xs text-gray-400">|</span>
@foreach($phase['modules'] as $mod)
<span class="px-1.5 py-0.5 bg-indigo-50 text-indigo-600 text-xs rounded font-mono">{{ $mod }}</span>
@endforeach
@endif
</div>
</div>
</div>
@endforeach
</div>
@endif
</div>
</div>
@endif
<!-- 분석 요약 -->
@if(!empty($quotation->quotation_result['analysis_summary']))
<div class="bg-gradient-to-r from-purple-50 to-indigo-50 rounded-lg p-6 mb-6">
<h3 class="font-semibold text-gray-800 mb-2 flex items-center gap-2">
<i class="ri-lightbulb-line text-yellow-500"></i> AI 분석 요약
</h3>
<p class="text-gray-700">{{ $quotation->quotation_result['analysis_summary'] }}</p>
</div>
@endif
@elseif($quotation->status === 'failed')
<!-- 실패 상태 -->
<div class="bg-red-50 border border-red-200 rounded-lg p-6 mb-6">
<div class="flex items-center gap-3 mb-2">
<i class="ri-error-warning-line text-2xl text-red-500"></i>
<h2 class="text-lg font-semibold text-red-800">AI 분석 실패</h2>
</div>
<p class="text-red-600">AI 분석 오류가 발생했습니다. 다시 시도하거나 입력 내용을 수정해 주세요.</p>
</div>
@elseif($quotation->isProcessing())
<!-- 분석중 상태 -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-6 mb-6 text-center">
<i class="ri-loader-4-line text-4xl text-blue-500 animate-spin mb-2 block"></i>
<h2 class="text-lg font-semibold text-blue-800">AI 분석 진행중...</h2>
<p class="text-blue-600 text-sm mt-1">분석이 완료되면 자동으로 결과가 표시됩니다.</p>
</div>
@endif
<!-- 입력 원문 (접이식) -->
<details class="bg-white rounded-lg shadow-sm mb-6">
<summary class="px-6 py-4 cursor-pointer hover:bg-gray-50 transition font-semibold text-gray-700">
<i class="ri-file-text-line"></i> 인터뷰 원문 보기
</summary>
<div class="px-6 pb-6">
<div class="bg-gray-50 rounded-lg p-4 text-sm text-gray-700 whitespace-pre-wrap">{{ $quotation->input_text }}</div>
</div>
</details>
@endsection
@push('scripts')
<script>
async function reanalyze() {
if (!confirm('AI 분석을 다시 실행하시겠습니까? 기존 결과가 덮어씌워집니다.')) return;
const btn = document.getElementById('reanalyzeBtn');
const original = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<i class="ri-loader-4-line animate-spin"></i> 재분석중...';
try {
const token = document.querySelector('meta[name="api-token"]')?.content
|| sessionStorage.getItem('api_token') || '';
const response = await fetch('{{ url("/api/admin/rd/ai-quotation/{$quotation->id}/analyze") }}', {
method: 'POST',
headers: {
'Accept': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content || '',
'Authorization': token ? `Bearer ${token}` : '',
},
credentials: 'same-origin',
});
const result = await response.json();
if (result.success) {
location.reload();
} else {
alert(result.message || '재분석에 실패했습니다.');
location.reload();
}
} catch (err) {
alert('서버 통신 중 오류가 발생했습니다.');
} finally {
btn.disabled = false;
btn.innerHTML = original;
}
}
</script>
@endpush