Phase 1.5: 엔티티/클레임 직접 입력 — OntologyEditorPage + 3탭 (엔티티/클레임/JSON 일괄)
백엔드 (crawler_platform/app/api/routes.py):
- POST /projects/{n}/entities: 단일 엔티티 직접 생성 (upsert)
- CreateEntityRequest (entity_type, name, metadata)
- POST /projects/{n}/entities/bulk: 다수 엔티티 일괄 생성
- BulkCreateEntitiesRequest, 응답 created 수 + entities
- DELETE /projects/{n}/entities/{id}: 단일 엔티티 삭제
- POST /projects/{n}/claims: 단일 클레임 직접 생성
- CreateClaimRequest (source_name, subject_entity_id, predicate,
object_entity_id|object_value, confidence, confidence_reason, evidence_text)
- claim_hash로 중복 검출 → 있으면 confidence/메타 갱신
- status="validated_claim", extraction_method="manual"
- evidence_text 있으면 Evidence 자동 생성
- DELETE /projects/{n}/claims/{id}: 단일 클레임 삭제
프론트엔드 API (src/lib/api/):
- entities.ts: list/create/bulkCreate/delete + Zod 스키마
- claims.ts: list/create/delete + Zod 스키마 (passthrough)
TanStack Query 훅 (src/hooks/):
- useEntities.ts: useEntities, useCreateEntity, useBulkCreateEntities, useDeleteEntity
- useClaims.ts: useClaims, useCreateClaim, useDeleteClaim
- queryKeys에 entities.list, claims.list 키 팩토리
UI 프리미티브 (src/components/ui/):
- tabs.tsx: Tabs, TabsList, TabsTrigger, TabsContent (Context API 기반)
OntologyEditorPage 신규 (src/pages/):
- 3개 탭 구조:
* 엔티티 탭: 도메인의 entity_types에서 타입 선택 + 이름 입력 → 추가
+ 엔티티 목록 (max-h scroll, 타입 배지, 삭제 버튼)
* 클레임 탭: 소스/주어/술어/목적어(엔티티 or 리터럴)/신뢰도 입력
+ 클레임 목록 (S-P-O 시각화, 신뢰도, status 배지)
* JSON 일괄 탭: textarea에 { entities: [...] } 붙여넣기 → 파싱 → bulkCreate
- react-hook-form + zod 검증
- useOntology(domain)으로 entity_types/predicates 자동 로드
- 삭제 confirm 대화상자, sonner 토스트
라우팅 & Sidebar:
- App.tsx: /editor/:projectId 라우트 추가
- AppShell: 사이드바에 "온톨로지 편집" 메뉴 (Network 아이콘)
i18n: editor.*, nav.editor 키 (한/영)
UI_REBUILD_PLAN.md 업데이트:
- Phase 1.4 `00786a4` 커밋 기록
- Phase 1.5 완료 표시 + 대기 보드 Phase 2/3 재정렬
다음 단계: Phase 2 — 그래프 시각화/편집 (Cytoscape React 래퍼)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -31,4 +31,19 @@ export const queryKeys = {
|
||||
session: (jobId: string) =>
|
||||
[...queryKeys.research.all, "session", jobId] as const,
|
||||
},
|
||||
entities: {
|
||||
all: ["entities"] as const,
|
||||
list: (projectName: string, entityType?: string) =>
|
||||
[
|
||||
...queryKeys.entities.all,
|
||||
"list",
|
||||
projectName,
|
||||
entityType ?? "*",
|
||||
] as const,
|
||||
},
|
||||
claims: {
|
||||
all: ["claims"] as const,
|
||||
list: (projectName: string, status?: string) =>
|
||||
[...queryKeys.claims.all, "list", projectName, status ?? "*"] as const,
|
||||
},
|
||||
};
|
||||
|
||||
44
crawler_platform/app/web/frontend/src/hooks/useClaims.ts
Normal file
44
crawler_platform/app/web/frontend/src/hooks/useClaims.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Claim, CreateClaimRequest, claimsApi } from "@/lib/api/claims";
|
||||
import { queryKeys } from "./queryKeys";
|
||||
|
||||
export function useClaims(
|
||||
projectName: string,
|
||||
options: { status?: string; includeCandidates?: boolean } = {},
|
||||
) {
|
||||
return useQuery<Claim[]>({
|
||||
queryKey: queryKeys.claims.list(projectName, options.status),
|
||||
queryFn: () =>
|
||||
claimsApi.list(projectName, {
|
||||
status: options.status,
|
||||
includeCandidates: options.includeCandidates,
|
||||
}),
|
||||
enabled: Boolean(projectName),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateClaim(projectName: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: CreateClaimRequest) =>
|
||||
claimsApi.create(projectName, body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.claims.all,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteClaim(projectName: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (claimId: string | number) =>
|
||||
claimsApi.delete(projectName, claimId),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.claims.all,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
58
crawler_platform/app/web/frontend/src/hooks/useEntities.ts
Normal file
58
crawler_platform/app/web/frontend/src/hooks/useEntities.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
BulkCreateEntitiesRequest,
|
||||
CreateEntityRequest,
|
||||
entitiesApi,
|
||||
Entity,
|
||||
} from "@/lib/api/entities";
|
||||
import { queryKeys } from "./queryKeys";
|
||||
|
||||
export function useEntities(projectName: string, entityType?: string) {
|
||||
return useQuery<Entity[]>({
|
||||
queryKey: queryKeys.entities.list(projectName, entityType),
|
||||
queryFn: () => entitiesApi.list(projectName, entityType),
|
||||
enabled: Boolean(projectName),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateEntity(projectName: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: CreateEntityRequest) =>
|
||||
entitiesApi.create(projectName, body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.entities.all,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useBulkCreateEntities(projectName: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: BulkCreateEntitiesRequest) =>
|
||||
entitiesApi.bulkCreate(projectName, body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.entities.all,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteEntity(projectName: string) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (entityId: string | number) =>
|
||||
entitiesApi.delete(projectName, entityId),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.entities.all,
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.claims.all,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user