feat: QMS 체크리스트 템플릿 관리 및 견적/리스트 개선
- QMS 체크리스트 템플릿 에디터 추가 (ChecklistTemplateEditor) - AuditSettingsPanel, Day1DocumentSection 기능 확장 - 견적 등록(QuoteRegistration) 개선 - IntegratedListTemplateV2 수정 - 건설 카테고리 actions 수정
This commit is contained in:
@@ -59,7 +59,8 @@ export async function getCategories(): Promise<{
|
||||
data: ApiCategory[];
|
||||
}>('/categories', { params: { per_page: '100' } });
|
||||
|
||||
const categories = (response.data || [])
|
||||
const rawData = Array.isArray(response.data) ? response.data : (response.data as unknown as { data: ApiCategory[] })?.data || [];
|
||||
const categories = rawData
|
||||
.map(transformCategory)
|
||||
.sort((a, b) => a.order - b.order);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../ui/select";
|
||||
// FormField는 실제로 사용되지 않으므로 제거
|
||||
import { SearchableSelect } from "../ui/searchable-select";
|
||||
|
||||
import { LocationListPanel } from "./LocationListPanel";
|
||||
import { LocationDetailPanel } from "./LocationDetailPanel";
|
||||
@@ -433,16 +433,6 @@ export function QuoteRegistration({
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
}, []);
|
||||
|
||||
// 발주처 선택
|
||||
const handleClientChange = useCallback((clientId: string) => {
|
||||
const client = clients.find((c) => c.id === clientId);
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
clientId,
|
||||
clientName: client?.vendorName || "",
|
||||
}));
|
||||
}, [clients]);
|
||||
|
||||
// 개소 추가 (BOM 계산 성공 시에만 추가, 성공/실패 반환)
|
||||
const handleAddLocation = useCallback(async (location: Omit<LocationItem, "id">): Promise<boolean> => {
|
||||
const newLocation: LocationItem = {
|
||||
@@ -774,37 +764,34 @@ export function QuoteRegistration({
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700">수주처 <span className="text-red-500">*</span></label>
|
||||
<Select
|
||||
<SearchableSelect
|
||||
options={clients.map((c) => ({ value: c.id, label: c.vendorName }))}
|
||||
value={formData.clientId}
|
||||
onValueChange={handleClientChange}
|
||||
onChange={(value, option) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
clientId: value,
|
||||
clientName: option.label,
|
||||
}));
|
||||
}}
|
||||
placeholder={isLoadingClients ? "로딩 중..." : "수주처를 선택하세요"}
|
||||
searchPlaceholder="수주처 검색..."
|
||||
emptyText="수주처가 없습니다"
|
||||
disabled={isViewMode || isLoadingClients}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={isLoadingClients ? "로딩 중..." : "수주처를 선택하세요"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{clients.map((client) => (
|
||||
<SelectItem key={client.id} value={client.id}>
|
||||
{client.vendorName}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
isLoading={isLoadingClients}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700">현장명</label>
|
||||
<Input
|
||||
list="siteNameList"
|
||||
placeholder="현장명을 입력하세요"
|
||||
<SearchableSelect
|
||||
options={siteNames.map((name) => ({ value: name, label: name }))}
|
||||
value={formData.siteName}
|
||||
onChange={(e) => handleFieldChange("siteName", e.target.value)}
|
||||
onChange={(value) => handleFieldChange("siteName", value)}
|
||||
placeholder="현장명을 선택하세요"
|
||||
searchPlaceholder="현장명 검색..."
|
||||
emptyText="현장명이 없습니다"
|
||||
disabled={isViewMode}
|
||||
/>
|
||||
<datalist id="siteNameList">
|
||||
{siteNames.map((name) => (
|
||||
<option key={name} value={name} />
|
||||
))}
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -977,8 +977,8 @@ export function IntegratedListTemplateV2<T = any>({
|
||||
showActions={tableColumns.some(col => col.key === 'actions')}
|
||||
/>
|
||||
) : (
|
||||
<Table className="table-fixed">
|
||||
{columnSettings && (
|
||||
<Table className={columnSettings && Object.keys(columnSettings.columnWidths).length > 0 ? "table-fixed" : "table-auto [&_td]:whitespace-nowrap [&_th]:whitespace-nowrap"}>
|
||||
{columnSettings && Object.keys(columnSettings.columnWidths).length > 0 && (
|
||||
<colgroup>
|
||||
{showCheckbox && <col style={{ width: 50 }} />}
|
||||
{tableColumns.map((col) => (
|
||||
@@ -1046,6 +1046,24 @@ export function IntegratedListTemplateV2<T = any>({
|
||||
e.preventDefault();
|
||||
const th = (e.target as HTMLElement).parentElement;
|
||||
if (!th) return;
|
||||
|
||||
// 첫 리사이즈 시 모든 컬럼의 현재 너비를 스냅샷 저장
|
||||
// → table-auto → table-fixed 전환 시 다른 컬럼 크기 유지
|
||||
if (Object.keys(columnSettings.columnWidths).length === 0) {
|
||||
const headerRow = th.parentElement;
|
||||
if (headerRow) {
|
||||
const allThs = headerRow.querySelectorAll('th');
|
||||
const offset = showCheckbox ? 1 : 0;
|
||||
allThs.forEach((cell, idx) => {
|
||||
if (idx < offset) return;
|
||||
const colKey = tableColumns[idx - offset]?.key;
|
||||
if (colKey) {
|
||||
columnSettings.onColumnResize(colKey, cell.offsetWidth);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const startX = e.clientX;
|
||||
const startWidth = th.offsetWidth;
|
||||
const onMouseMove = (ev: MouseEvent) => {
|
||||
|
||||
Reference in New Issue
Block a user