summaryrefslogtreecommitdiff
path: root/lib/tags
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-11 09:02:00 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-11 09:02:00 +0000
commitcbb4c7fe0b94459162ad5e998bc05cd293e0ff96 (patch)
tree0a26712f7685e4f6511e637b9a81269d90a47c8f /lib/tags
parenteb654f88214095f71be142b989e620fd28db3f69 (diff)
(대표님) 입찰, EDP 변경사항 대응, spreadJS 오류 수정, 벤더실사 수정
Diffstat (limited to 'lib/tags')
-rw-r--r--lib/tags/service.ts66
-rw-r--r--lib/tags/table/add-tag-dialog.tsx46
2 files changed, 72 insertions, 40 deletions
diff --git a/lib/tags/service.ts b/lib/tags/service.ts
index a1dff137..bec342e1 100644
--- a/lib/tags/service.ts
+++ b/lib/tags/service.ts
@@ -14,7 +14,7 @@ import { getFormMappingsByTagType } from './form-mapping-service';
import { contractItems, contracts } from "@/db/schema/contract";
import { getCodeListsByID } from "../sedp/sync-object-class";
import { projects } from "@/db/schema";
-
+import { randomBytes } from 'crypto';
// 폼 결과를 위한 인터페이스 정의
interface CreatedOrExistingForm {
@@ -24,6 +24,14 @@ interface CreatedOrExistingForm {
isNewlyCreated: boolean;
}
+/**
+ * 16진수 24자리 고유 식별자 생성
+ * @returns 24자리 16진수 문자열 (예: "a1b2c3d4e5f6789012345678")
+ */
+function generateTagIdx(): string {
+ return randomBytes(12).toString('hex'); // 12바이트 = 24자리 16진수
+}
+
export async function getTags(input: GetTagsSchema, packagesId: number) {
// return unstable_cache(
@@ -90,6 +98,7 @@ export async function getTags(input: GetTagsSchema, packagesId: number) {
// )();
}
+
export async function createTag(
formData: CreateTagSchema,
selectedPackageId: number | null
@@ -259,10 +268,15 @@ export async function createTag(
}
}
- // 5) 새 Tag 생성 (같은 트랜잭션 `tx` 사용)
+ // 🆕 16진수 24자리 태그 고유 식별자 생성
+ const generatedTagIdx = generateTagIdx();
+ console.log(`[CREATE TAG] Generated tagIdx: ${generatedTagIdx}`);
+
+ // 5) 새 Tag 생성 (tagIdx 추가)
const [newTag] = await insertTag(tx, {
contractItemId: selectedPackageId,
formId: primaryFormId,
+ tagIdx: generatedTagIdx, // 🆕 생성된 16진수 24자리 추가
tagNo: validated.data.tagNo,
class: validated.data.class,
tagType: validated.data.tagType,
@@ -271,7 +285,7 @@ export async function createTag(
console.log(`tags-${selectedPackageId}`, "create", newTag)
- // 6) 생성된 각 form에 대해 formEntries에 데이터 추가
+ // 6) 생성된 각 form에 대해 formEntries에 데이터 추가 (TAG_IDX 포함)
for (const form of createdOrExistingForms) {
try {
// 기존 formEntry 가져오기
@@ -282,16 +296,18 @@ export async function createTag(
)
});
- // 새로운 태그 데이터 객체 생성
+ // 새로운 태그 데이터 객체 생성 (TAG_IDX 포함)
const newTagData = {
+ TAG_IDX: generatedTagIdx, // 🆕 같은 16진수 24자리 값 사용
TAG_NO: validated.data.tagNo,
TAG_DESC: validated.data.description ?? null,
status: "New" // 수동으로 생성된 태그임을 표시
};
if (existingEntry && existingEntry.id) {
- // 기존 formEntry가 있는 경우
+ // 기존 formEntry가 있는 경우 - TAG_IDX 타입 추가
let existingData: Array<{
+ TAG_IDX?: string; // 🆕 TAG_IDX 필드 추가
TAG_NO: string;
TAG_DESC?: string;
status?: string;
@@ -302,13 +318,14 @@ export async function createTag(
existingData = existingEntry.data;
}
- // TAG_NO가 이미 존재하는지 확인
+ // TAG_IDX 또는 TAG_NO가 이미 존재하는지 확인 (우선순위: TAG_IDX)
const existingTagIndex = existingData.findIndex(
- item => item.TAG_NO === validated.data.tagNo
+ item => item.TAG_IDX === generatedTagIdx ||
+ (item.TAG_NO === validated.data.tagNo && !item.TAG_IDX)
);
if (existingTagIndex === -1) {
- // TAG_NO가 없으면 새로 추가
+ // 태그가 없으면 새로 추가
const updatedData = [...existingData, newTagData];
await tx
@@ -319,12 +336,12 @@ export async function createTag(
})
.where(eq(formEntries.id, existingEntry.id));
- console.log(`[CREATE TAG] Added tag ${validated.data.tagNo} to existing formEntry for form ${form.formCode}`);
+ console.log(`[CREATE TAG] Added tag ${validated.data.tagNo} with tagIdx ${generatedTagIdx} to existing formEntry for form ${form.formCode}`);
} else {
console.log(`[CREATE TAG] Tag ${validated.data.tagNo} already exists in formEntry for form ${form.formCode}`);
}
} else {
- // formEntry가 없는 경우 새로 생성
+ // formEntry가 없는 경우 새로 생성 (TAG_IDX 포함)
await tx.insert(formEntries).values({
formCode: form.formCode,
contractItemId: selectedPackageId,
@@ -333,7 +350,7 @@ export async function createTag(
updatedAt: new Date(),
});
- console.log(`[CREATE TAG] Created new formEntry with tag ${validated.data.tagNo} for form ${form.formCode}`);
+ console.log(`[CREATE TAG] Created new formEntry with tag ${validated.data.tagNo} and tagIdx ${generatedTagIdx} for form ${form.formCode}`);
}
} catch (formEntryError) {
console.error(`[CREATE TAG] Error updating formEntry for form ${form.formCode}:`, formEntryError);
@@ -351,13 +368,14 @@ export async function createTag(
revalidateTag(`form-data-${form.formCode}-${selectedPackageId}`)
})
- // 8) 성공 시 반환
+ // 8) 성공 시 반환 (tagIdx 추가)
return {
success: true,
data: {
forms: createdOrExistingForms,
primaryFormId,
- tagNo: validated.data.tagNo
+ tagNo: validated.data.tagNo,
+ tagIdx: generatedTagIdx, // 🆕 생성된 tagIdx도 반환
},
}
})
@@ -369,7 +387,6 @@ export async function createTag(
}
}
-
export async function createTagInForm(
formData: CreateTagSchema,
selectedPackageId: number | null,
@@ -499,10 +516,15 @@ export async function createTagInForm(
}
if (form?.id) {
- // 5) 새 Tag 생성 (같은 트랜잭션 `tx` 사용)
+ // 🆕 16진수 24자리 태그 고유 식별자 생성
+ const generatedTagIdx = generateTagIdx();
+ console.log(`[CREATE TAG IN FORM] Generated tagIdx: ${generatedTagIdx}`);
+
+ // 5) 새 Tag 생성 (tagIdx 추가)
const [newTag] = await insertTag(tx, {
contractItemId: selectedPackageId,
formId: form.id,
+ tagIdx: generatedTagIdx, // 🆕 생성된 16진수 24자리 추가
tagNo: validated.data.tagNo,
class: validated.data.class,
tagType: validated.data.tagType,
@@ -518,8 +540,9 @@ export async function createTagInForm(
});
if (entry && entry.id) {
- // 7) 기존 데이터 가져오기 (배열인지 확인)
+ // 7) 기존 데이터 가져오기 (배열인지 확인) - TAG_IDX 타입 추가
let existingData: Array<{
+ TAG_IDX?: string; // 🆕 TAG_IDX 필드 추가
TAG_NO: string;
TAG_DESC?: string;
status?: string;
@@ -532,8 +555,9 @@ export async function createTagInForm(
console.log(`[CREATE TAG IN FORM] Existing data count: ${existingData.length}`);
- // 8) 새로운 태그를 기존 데이터에 추가 (status 필드 포함)
+ // 8) 새로운 태그를 기존 데이터에 추가 (TAG_IDX 포함)
const newTagData = {
+ TAG_IDX: generatedTagIdx, // 🆕 같은 16진수 24자리 값 사용
TAG_NO: validated.data.tagNo,
TAG_DESC: validated.data.description ?? null,
status: "New" // 수동으로 생성된 태그임을 표시
@@ -542,7 +566,7 @@ export async function createTagInForm(
const updatedData = [...existingData, newTagData];
console.log(`[CREATE TAG IN FORM] Updated data count: ${updatedData.length}`);
- console.log(`[CREATE TAG IN FORM] Added tag: ${validated.data.tagNo} with status: 수동 생성`);
+ console.log(`[CREATE TAG IN FORM] Added tag: ${validated.data.tagNo} with tagIdx: ${generatedTagIdx}, status: 수동 생성`);
// 9) formEntries 업데이트
await tx
@@ -553,10 +577,11 @@ export async function createTagInForm(
})
.where(eq(formEntries.id, entry.id));
} else {
- // 10) formEntry가 없는 경우 새로 생성 (status 필드 포함)
+ // 10) formEntry가 없는 경우 새로 생성 (TAG_IDX 포함)
console.log(`[CREATE TAG IN FORM] No existing formEntry found, creating new one`);
const newEntryData = [{
+ TAG_IDX: generatedTagIdx, // 🆕 같은 16진수 24자리 값 사용
TAG_NO: validated.data.tagNo,
TAG_DESC: validated.data.description ?? null,
status: "New" // 수동으로 생성된 태그임을 표시
@@ -571,7 +596,7 @@ export async function createTagInForm(
});
}
- console.log(`[CREATE TAG IN FORM] Successfully created tag: ${validated.data.tagNo}`)
+ console.log(`[CREATE TAG IN FORM] Successfully created tag: ${validated.data.tagNo} with tagIdx: ${generatedTagIdx}`)
} else {
return { error: "Failed to create or find form" };
}
@@ -588,6 +613,7 @@ export async function createTagInForm(
data: {
formId: form.id,
tagNo: validated.data.tagNo,
+ tagIdx: generatedTagIdx, // 🆕 생성된 tagIdx도 반환
formCreated: !form // form이 새로 생성되었는지 여부
}
}
diff --git a/lib/tags/table/add-tag-dialog.tsx b/lib/tags/table/add-tag-dialog.tsx
index 10167c62..e5207cd8 100644
--- a/lib/tags/table/add-tag-dialog.tsx
+++ b/lib/tags/table/add-tag-dialog.tsx
@@ -61,6 +61,7 @@ import {
type ClassOption,
TagTypeOption,
} from "@/lib/tags/service"
+import { ScrollArea } from "@/components/ui/scroll-area"
// Updated to support multiple rows and subclass
interface MultiTagFormValues {
@@ -96,7 +97,7 @@ interface UpdatedClassOption extends ClassOption {
}
interface AddTagDialogProps {
- selectedPackageId: number
+ selectedPackageId: number
}
export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
@@ -171,7 +172,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
setIsLoadingSubFields(true)
try {
// 수정된 getSubfieldsByTagType 함수 호출 (subclassRemark 파라미터 추가)
- const { subFields: apiSubFields } = await getSubfieldsByTagType(tagTypeCode, selectedPackageId, subclassRemark ,subclass )
+ const { subFields: apiSubFields } = await getSubfieldsByTagType(tagTypeCode, selectedPackageId, subclassRemark, subclass)
const formattedSubFields: SubFieldDef[] = apiSubFields.map(field => ({
name: field.name,
label: field.label,
@@ -213,7 +214,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
form.setValue("subclass", "") // 서브클래스 초기화
setSelectedClassOption(classOption)
setSelectedSubclass("")
-
+
if (classOption.tagTypeCode) {
setSelectedTagTypeCode(classOption.tagTypeCode)
// If you have tagTypeList, you can find the label
@@ -223,13 +224,13 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
} else if (classOption.tagTypeDescription) {
form.setValue("tagType", classOption.tagTypeDescription)
}
-
+
// 서브클래스가 있으면 서브필드 로딩을 하지 않고 대기
if (classOption.subclasses && classOption.subclasses.length > 0) {
setSubFields([]) // 서브클래스 선택을 기다림
} else {
// 서브클래스가 없으면 바로 서브필드 로딩
- await loadFilteredSubFieldsByTagTypeCode(classOption.tagTypeCode, "","")
+ await loadFilteredSubFieldsByTagTypeCode(classOption.tagTypeCode, "", "")
}
}
}
@@ -239,13 +240,13 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
// ---------------
async function handleSelectSubclass(subclassCode: string) {
if (!selectedClassOption || !selectedTagTypeCode) return
-
+
setSelectedSubclass(subclassCode)
form.setValue("subclass", subclassCode)
-
+
// 선택된 서브클래스의 리마크 값 가져오기
const subclassRemarkValue = selectedClassOption.subclassRemark[subclassCode] || ""
-
+
// 리마크 값으로 필터링된 서브필드 로드
await loadFilteredSubFieldsByTagTypeCode(selectedTagTypeCode, subclassRemarkValue, subclassCode)
}
@@ -257,28 +258,28 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
if (subFields.length === 0) {
return;
}
-
+
const subscription = form.watch((value) => {
if (!value.rows || subFields.length === 0) {
return;
}
-
+
const rows = [...value.rows];
rows.forEach((row, rowIndex) => {
if (!row) return;
-
+
let combined = "";
subFields.forEach((sf, idx) => {
const fieldValue = row[sf.name] || "";
-
+
// delimiter를 앞에 붙이기 (첫 번째 필드가 아니고, 현재 필드에 값이 있고, delimiter가 있는 경우)
if (idx > 0 && fieldValue && sf.delimiter) {
combined += sf.delimiter;
}
-
+
combined += fieldValue;
});
-
+
const currentTagNo = form.getValues(`rows.${rowIndex}.tagNo`);
if (currentTagNo !== combined) {
form.setValue(`rows.${rowIndex}.tagNo`, combined, {
@@ -289,7 +290,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
}
});
});
-
+
return () => subscription.unsubscribe();
}, [subFields, form]);
@@ -340,7 +341,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
try {
const res = await createTag(tagData, selectedPackageId);
if ("error" in res) {
- console.log(res.error )
+ console.log(res.error)
failedTags.push({ tag: row.tagNo, error: res.error });
} else {
successfulTags.push(row.tagNo);
@@ -468,7 +469,12 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
value={classSearchTerm}
onValueChange={setClassSearchTerm}
/>
- <CommandList key={`${commandId}-list`} className="max-h-[300px]">
+
+ <CommandList key={`${commandId}-list`} className="max-h-[300px]" onWheel={(e) => {
+ e.stopPropagation(); // 이벤트 전파 차단
+ const target = e.currentTarget;
+ target.scrollTop += e.deltaY; // 직접 스크롤 처리
+ }}>
<CommandEmpty key={`${commandId}-empty`}>검색 결과가 없습니다.</CommandEmpty>
<CommandGroup key={`${commandId}-group`}>
{classOptions.map((opt, optIndex) => {
@@ -519,7 +525,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
// ---------------
function renderSubclassField(field: any) {
const hasSubclasses = selectedClassOption?.subclasses && selectedClassOption.subclasses.length > 0
-
+
if (!hasSubclasses) {
return null
}
@@ -542,7 +548,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
<SelectContent>
{selectedClassOption?.subclasses.map((subclass) => (
<SelectItem key={subclass.id} value={subclass.id}>
- {subclass.desc}
+ {subclass.desc}
</SelectItem>
))}
</SelectContent>
@@ -613,7 +619,7 @@ export function AddTagDialog({ selectedPackageId }: AddTagDialogProps) {
const message = selectedClassOption?.subclasses && selectedClassOption.subclasses.length > 0
? "서브클래스를 선택해주세요."
: "이 태그 유형에 대한 필드가 없습니다."
-
+
return (
<div className="py-4 text-center text-muted-foreground">
{message}