- AI 2단계 분석: 고객 인터뷰 → 요구사항 추출 → 견적 산출 - 모델 확장: AiQuotation(모드/견적번호), AiQuotationItem(규격/단가/금액) - AiQuotePriceTable 모델 신규 생성 - Create 페이지: 모듈/제조 모드 탭, 제품 카테고리, 고객 정보 입력 - Show 페이지: 제조 모드 분기 렌더링 (품목/금액/고객정보) - Edit 페이지: 품목 인라인 편집, 할인/부가세/조건 입력 - Document: 한국 표준 제조업 견적서 양식 템플릿 - Controller/Route: update 엔드포인트, edit 라우트 추가
573 lines
33 KiB
PHP
573 lines
33 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>
|
||
@if($quotation->isManufacture())
|
||
<span class="px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded-full font-medium">제조 견적</span>
|
||
@endif
|
||
</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())
|
||
@if($quotation->isManufacture())
|
||
<a href="{{ route('rd.ai-quotation.edit', $quotation->id) }}"
|
||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition">
|
||
<i class="ri-edit-line"></i> 견적서 편집
|
||
</a>
|
||
@endif
|
||
<a href="{{ route('rd.ai-quotation.document', $quotation->id) }}" target="_blank"
|
||
class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition">
|
||
<i class="ri-file-text-line"></i> 견적서 보기
|
||
</a>
|
||
@endif
|
||
@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>
|
||
@if($quotation->isManufacture())
|
||
<div>
|
||
<p class="text-xs text-gray-500 mb-1">견적번호</p>
|
||
<p class="font-medium text-gray-800 font-mono">{{ $quotation->quote_number ?? '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-xs text-gray-500 mb-1">제품 카테고리</p>
|
||
<p class="font-medium text-gray-800">{{ $quotation->product_category === 'STEEL' ? '철재' : '방화스크린' }}</p>
|
||
</div>
|
||
@else
|
||
<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>
|
||
@endif
|
||
<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->isManufacture())
|
||
{{-- ============================================ --}}
|
||
{{-- 제조 견적 모드 --}}
|
||
{{-- ============================================ --}}
|
||
|
||
@php
|
||
$options = $quotation->options ?? [];
|
||
$client = $options['client'] ?? [];
|
||
$project = $options['project'] ?? [];
|
||
$pricing = $options['pricing'] ?? [];
|
||
$terms = $options['terms'] ?? [];
|
||
$analysis = $quotation->analysis_result ?? [];
|
||
$productSpecs = $analysis['product_specs'] ?? [];
|
||
@endphp
|
||
|
||
<!-- 고객 정보 -->
|
||
@if(!empty($client))
|
||
<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-user-line text-blue-600"></i> 고객 정보
|
||
</h2>
|
||
</div>
|
||
<div class="p-6">
|
||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
<div>
|
||
<p class="text-xs text-gray-500 mb-1">회사명</p>
|
||
<p class="font-medium text-gray-800">{{ $client['company'] ?? '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-xs text-gray-500 mb-1">담당자</p>
|
||
<p class="font-medium text-gray-800">{{ $client['contact'] ?? '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-xs text-gray-500 mb-1">연락처</p>
|
||
<p class="font-medium text-gray-800">{{ $client['phone'] ?? '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-xs text-gray-500 mb-1">이메일</p>
|
||
<p class="font-medium text-gray-800">{{ $client['email'] ?? '-' }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<!-- 프로젝트 분석 -->
|
||
@if(!empty($project))
|
||
<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-building-line text-green-600"></i> 프로젝트 분석
|
||
</h2>
|
||
</div>
|
||
<div class="p-6">
|
||
<div class="flex flex-wrap gap-4">
|
||
<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">{{ $project['name'] ?? '-' }}</span>
|
||
</div>
|
||
<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">{{ $project['building_type'] ?? '-' }}</span>
|
||
</div>
|
||
@if(!empty($project['location']))
|
||
<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">{{ $project['location'] }}</span>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<!-- 위치별 사양 테이블 -->
|
||
@if(!empty($productSpecs))
|
||
<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-ruler-line text-indigo-600"></i> 위치별 제품 사양 (AI 추출)
|
||
</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-center text-xs font-medium text-gray-500 uppercase">규격 (W×H)</th>
|
||
<th class="px-4 py-3 text-center 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-left text-xs font-medium text-gray-500 uppercase">비고</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-50">
|
||
@foreach($productSpecs as $spec)
|
||
<tr class="hover:bg-gray-50">
|
||
<td class="px-4 py-3">
|
||
<div class="font-medium text-gray-800">{{ $spec['floor_name'] ?? '-' }}</div>
|
||
<div class="text-xs text-gray-400 font-mono">{{ $spec['floor_code'] ?? '' }}</div>
|
||
</td>
|
||
<td class="px-4 py-3">
|
||
<span class="px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded-full">{{ $spec['product_type'] ?? '-' }}</span>
|
||
</td>
|
||
<td class="px-4 py-3 text-center font-mono">{{ ($spec['width_mm'] ?? 0) }}×{{ ($spec['height_mm'] ?? 0) }}</td>
|
||
<td class="px-4 py-3 text-center font-medium">{{ $spec['quantity'] ?? 1 }}</td>
|
||
<td class="px-4 py-3 text-gray-600">{{ $spec['guide_rail'] ?? '-' }}</td>
|
||
<td class="px-4 py-3 text-gray-600">{{ $spec['motor'] ?? '-' }}</td>
|
||
<td class="px-4 py-3 text-xs text-gray-500">{{ $spec['note'] ?? '' }}</td>
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</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">
|
||
@php
|
||
$materialItems = $quotation->items->where('item_category', 'material');
|
||
$laborItems = $quotation->items->where('item_category', 'labor');
|
||
$installItems = $quotation->items->where('item_category', 'install');
|
||
$otherItems = $quotation->items->whereNotIn('item_category', ['material', 'labor', 'install']);
|
||
@endphp
|
||
<table class="w-full text-sm">
|
||
<thead class="bg-gray-50 border-b">
|
||
<tr>
|
||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase" style="width: 40px;">No</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-center text-xs font-medium text-gray-500 uppercase">규격</th>
|
||
<th class="px-4 py-3 text-center 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">
|
||
@php $no = 1; @endphp
|
||
|
||
{{-- 재료비 --}}
|
||
@if($materialItems->isNotEmpty())
|
||
<tr class="bg-blue-50">
|
||
<td colspan="7" class="px-4 py-2 text-xs font-semibold text-blue-700">재료비</td>
|
||
</tr>
|
||
@foreach($materialItems as $item)
|
||
<tr class="hover:bg-gray-50">
|
||
<td class="px-4 py-3 text-center text-gray-500">{{ $no++ }}</td>
|
||
<td class="px-4 py-3 text-xs text-gray-500 font-mono">{{ $item->floor_code }}</td>
|
||
<td class="px-4 py-3 font-medium text-gray-800">{{ $item->module_name }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600 font-mono text-xs">{{ $item->specification }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600">{{ number_format((float)$item->quantity, 0) }} {{ $item->unit }}</td>
|
||
<td class="px-4 py-3 text-right text-gray-600">{{ number_format((float)$item->unit_price) }}</td>
|
||
<td class="px-4 py-3 text-right font-medium text-gray-800">{{ number_format((float)$item->total_price) }}</td>
|
||
</tr>
|
||
@endforeach
|
||
@endif
|
||
|
||
{{-- 노무비 --}}
|
||
@if($laborItems->isNotEmpty())
|
||
<tr class="bg-green-50">
|
||
<td colspan="7" class="px-4 py-2 text-xs font-semibold text-green-700">노무비</td>
|
||
</tr>
|
||
@foreach($laborItems as $item)
|
||
<tr class="hover:bg-gray-50">
|
||
<td class="px-4 py-3 text-center text-gray-500">{{ $no++ }}</td>
|
||
<td class="px-4 py-3 text-xs text-gray-500 font-mono">{{ $item->floor_code }}</td>
|
||
<td class="px-4 py-3 font-medium text-gray-800">{{ $item->module_name }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600 font-mono text-xs">{{ $item->specification }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600">{{ number_format((float)$item->quantity, 0) }} {{ $item->unit }}</td>
|
||
<td class="px-4 py-3 text-right text-gray-600">{{ number_format((float)$item->unit_price) }}</td>
|
||
<td class="px-4 py-3 text-right font-medium text-gray-800">{{ number_format((float)$item->total_price) }}</td>
|
||
</tr>
|
||
@endforeach
|
||
@endif
|
||
|
||
{{-- 설치비 --}}
|
||
@if($installItems->isNotEmpty())
|
||
<tr class="bg-orange-50">
|
||
<td colspan="7" class="px-4 py-2 text-xs font-semibold text-orange-700">설치비</td>
|
||
</tr>
|
||
@foreach($installItems as $item)
|
||
<tr class="hover:bg-gray-50">
|
||
<td class="px-4 py-3 text-center text-gray-500">{{ $no++ }}</td>
|
||
<td class="px-4 py-3 text-xs text-gray-500 font-mono">{{ $item->floor_code }}</td>
|
||
<td class="px-4 py-3 font-medium text-gray-800">{{ $item->module_name }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600 font-mono text-xs">{{ $item->specification }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600">{{ number_format((float)$item->quantity, 0) }} {{ $item->unit }}</td>
|
||
<td class="px-4 py-3 text-right text-gray-600">{{ number_format((float)$item->unit_price) }}</td>
|
||
<td class="px-4 py-3 text-right font-medium text-gray-800">{{ number_format((float)$item->total_price) }}</td>
|
||
</tr>
|
||
@endforeach
|
||
@endif
|
||
|
||
{{-- 기타 --}}
|
||
@foreach($otherItems as $item)
|
||
<tr class="hover:bg-gray-50">
|
||
<td class="px-4 py-3 text-center text-gray-500">{{ $no++ }}</td>
|
||
<td class="px-4 py-3 text-xs text-gray-500 font-mono">{{ $item->floor_code }}</td>
|
||
<td class="px-4 py-3 font-medium text-gray-800">{{ $item->module_name }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600 font-mono text-xs">{{ $item->specification }}</td>
|
||
<td class="px-4 py-3 text-center text-gray-600">{{ number_format((float)$item->quantity, 0) }} {{ $item->unit }}</td>
|
||
<td class="px-4 py-3 text-right text-gray-600">{{ number_format((float)$item->unit_price) }}</td>
|
||
<td class="px-4 py-3 text-right font-medium text-gray-800">{{ number_format((float)$item->total_price) }}</td>
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 금액 요약 -->
|
||
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-lg p-6 mb-6">
|
||
<h3 class="font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
||
<i class="ri-money-cny-circle-line text-blue-600"></i> 금액 요약
|
||
</h3>
|
||
<div class="grid gap-3" style="grid-template-columns: 1fr 1fr;">
|
||
<div class="space-y-2 text-sm">
|
||
<div class="flex justify-between"><span class="text-gray-600">재료비</span><span class="font-medium">{{ number_format((int)($pricing['material_cost'] ?? 0)) }}원</span></div>
|
||
<div class="flex justify-between"><span class="text-gray-600">노무비</span><span class="font-medium">{{ number_format((int)($pricing['labor_cost'] ?? 0)) }}원</span></div>
|
||
<div class="flex justify-between"><span class="text-gray-600">설치비</span><span class="font-medium">{{ number_format((int)($pricing['install_cost'] ?? 0)) }}원</span></div>
|
||
<div class="flex justify-between border-t pt-2"><span class="font-semibold">소계</span><span class="font-bold">{{ number_format((int)($pricing['subtotal'] ?? 0)) }}원</span></div>
|
||
</div>
|
||
<div class="space-y-2 text-sm">
|
||
@if(($pricing['discount_rate'] ?? 0) > 0)
|
||
<div class="flex justify-between"><span class="text-red-600">할인 ({{ $pricing['discount_rate'] }}%)</span><span class="font-medium text-red-600">-{{ number_format((int)($pricing['discount_amount'] ?? 0)) }}원</span></div>
|
||
@endif
|
||
<div class="flex justify-between"><span class="text-gray-600">부가세 (10%)</span><span class="font-medium">{{ number_format((int)($pricing['vat_amount'] ?? 0)) }}원</span></div>
|
||
<div class="flex justify-between border-t pt-2 text-lg">
|
||
<span class="font-bold text-blue-700">최종 금액</span>
|
||
<span class="font-bold text-blue-700">{{ number_format((int)($pricing['final_amount'] ?? 0)) }}원</span>
|
||
</div>
|
||
@php
|
||
$koreanAmount = \App\Services\Rd\AiQuotationService::numberToKorean((int)($pricing['subtotal'] ?? 0));
|
||
@endphp
|
||
<div class="text-xs text-gray-500 text-right">금 {{ $koreanAmount }}원정 (VAT 별도)</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@else
|
||
{{-- ============================================ --}}
|
||
{{-- 모듈 추천 모드 (기존) --}}
|
||
{{-- ============================================ --}}
|
||
|
||
<!-- 업무 분석 결과 -->
|
||
@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>
|
||
|
||
<!-- 구현 계획 -->
|
||
@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
|
||
@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
|