- QuoteFormulaRangeService, RangeController 생성 - 범위 CRUD API 엔드포인트 추가 (6개) - edit.blade.php 탭 구조로 개편 (기본정보/범위/매핑/품목) - ranges-tab.blade.php 범위 관리 UI 구현 - Alpine.js 기반 인터랙티브 CRUD
169 lines
8.1 KiB
PHP
169 lines
8.1 KiB
PHP
{{-- 기본 정보 탭 --}}
|
|
<form @submit.prevent="saveFormula()" class="space-y-6">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- 카테고리 -->
|
|
<div>
|
|
<label for="category_id" class="block text-sm font-medium text-gray-700 mb-1">
|
|
카테고리 <span class="text-red-500">*</span>
|
|
</label>
|
|
<select x-model="form.category_id" id="category_id"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required>
|
|
<option value="">카테고리 선택</option>
|
|
<template x-for="cat in categories" :key="cat.id">
|
|
<option :value="cat.id" x-text="cat.name"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- 수식 유형 -->
|
|
<div>
|
|
<label for="type" class="block text-sm font-medium text-gray-700 mb-1">
|
|
수식 유형 <span class="text-red-500">*</span>
|
|
</label>
|
|
<select x-model="form.type" id="type"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required>
|
|
<option value="">유형 선택</option>
|
|
<option value="input">입력값</option>
|
|
<option value="calculation">계산식</option>
|
|
<option value="range">범위별</option>
|
|
<option value="mapping">매핑</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- 수식명 -->
|
|
<div>
|
|
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">
|
|
수식명 <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="text" x-model="form.name" id="name"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required>
|
|
</div>
|
|
|
|
<!-- 변수명 -->
|
|
<div>
|
|
<label for="variable" class="block text-sm font-medium text-gray-700 mb-1">
|
|
변수명 <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="text" :value="form.variable" @input="onVariableInput($event)" id="variable"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
|
|
required>
|
|
<p class="text-xs text-gray-500 mt-1">대문자로 시작, 대문자/숫자/언더스코어만 사용</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 계산식 (type=calculation 일 때 표시) -->
|
|
<div x-show="form.type === 'calculation'" x-transition>
|
|
<label for="formula" class="block text-sm font-medium text-gray-700 mb-1">
|
|
계산식 <span class="text-red-500">*</span>
|
|
</label>
|
|
<textarea x-model="form.formula" id="formula" rows="3"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"></textarea>
|
|
<div class="flex items-center gap-4 mt-2">
|
|
<button type="button" @click="validateFormula()"
|
|
class="text-sm text-blue-600 hover:text-blue-700">
|
|
수식 검증
|
|
</button>
|
|
<span id="validateResult" class="text-sm"></span>
|
|
</div>
|
|
<p class="text-xs text-gray-500 mt-1">지원 함수: SUM, ROUND, CEIL, FLOOR, ABS, MIN, MAX, IF</p>
|
|
</div>
|
|
|
|
<!-- 사용 가능한 변수 목록 (계산식일 때만 표시) -->
|
|
<div x-show="form.type === 'calculation'" x-transition>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">사용 가능한 변수</label>
|
|
<div class="flex flex-wrap gap-2 p-3 bg-gray-50 rounded-lg max-h-32 overflow-y-auto">
|
|
<template x-if="availableVariables.length === 0">
|
|
<span class="text-sm text-gray-500">사용 가능한 변수가 없습니다.</span>
|
|
</template>
|
|
<template x-for="v in availableVariables" :key="v.variable">
|
|
<button type="button" @click="insertVariable(v.variable)"
|
|
class="px-2 py-1 bg-blue-100 text-blue-700 rounded text-xs font-mono hover:bg-blue-200"
|
|
:title="v.name + ' (' + v.category + ')'"
|
|
x-text="v.variable">
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 범위별 안내 -->
|
|
<div x-show="form.type === 'range'" x-transition class="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
<div class="flex items-start gap-3">
|
|
<svg class="w-5 h-5 text-blue-500 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
<div>
|
|
<p class="text-sm font-medium text-blue-800">범위별 수식</p>
|
|
<p class="text-sm text-blue-700 mt-1">
|
|
조건 변수의 값에 따라 다른 결과를 반환합니다.
|
|
<span class="font-semibold">"범위 설정"</span> 탭에서 범위별 조건을 설정하세요.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 매핑 안내 -->
|
|
<div x-show="form.type === 'mapping'" x-transition class="bg-purple-50 border border-purple-200 rounded-lg p-4">
|
|
<div class="flex items-start gap-3">
|
|
<svg class="w-5 h-5 text-purple-500 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
<div>
|
|
<p class="text-sm font-medium text-purple-800">매핑 수식</p>
|
|
<p class="text-sm text-purple-700 mt-1">
|
|
입력값을 미리 정의된 값으로 변환합니다.
|
|
<span class="font-semibold">"매핑 설정"</span> 탭에서 매핑 규칙을 설정하세요.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 설명 -->
|
|
<div>
|
|
<label for="description" class="block text-sm font-medium text-gray-700 mb-1">
|
|
설명
|
|
</label>
|
|
<textarea x-model="form.description" id="description" rows="2"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- 정렬 순서 -->
|
|
<div>
|
|
<label for="sort_order" class="block text-sm font-medium text-gray-700 mb-1">
|
|
정렬 순서
|
|
</label>
|
|
<input type="number" x-model="form.sort_order" id="sort_order"
|
|
min="1"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
|
|
<!-- 활성 상태 -->
|
|
<div class="flex items-center pt-6">
|
|
<input type="checkbox" x-model="form.is_active" id="is_active"
|
|
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
|
<label for="is_active" class="ml-2 block text-sm text-gray-700">
|
|
활성화
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 에러 메시지 -->
|
|
<div id="errorMessage" class="hidden bg-red-50 border border-red-200 rounded-lg p-4 text-sm text-red-700"></div>
|
|
|
|
<!-- 버튼 -->
|
|
<div class="flex justify-end gap-3 pt-4 border-t">
|
|
<a href="{{ route('quote-formulas.index') }}"
|
|
class="px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition">
|
|
취소
|
|
</a>
|
|
<button type="submit"
|
|
class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition">
|
|
저장
|
|
</button>
|
|
</div>
|
|
</form> |