백엔드 (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>
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
export const queryKeys = {
|
|
projects: {
|
|
all: ["projects"] as const,
|
|
list: () => [...queryKeys.projects.all, "list"] as const,
|
|
detail: (name: string) =>
|
|
[...queryKeys.projects.all, "detail", name] as const,
|
|
},
|
|
domains: {
|
|
all: ["domains"] as const,
|
|
list: () => [...queryKeys.domains.all, "list"] as const,
|
|
},
|
|
ontology: {
|
|
all: ["ontology"] as const,
|
|
byDomain: (domain: string) =>
|
|
[...queryKeys.ontology.all, "byDomain", domain] as const,
|
|
},
|
|
sources: {
|
|
all: ["sources"] as const,
|
|
byProject: (projectName: string) =>
|
|
[...queryKeys.sources.all, "byProject", projectName] as const,
|
|
},
|
|
crawl: {
|
|
all: ["crawl"] as const,
|
|
job: (jobId: string) =>
|
|
[...queryKeys.crawl.all, "job", jobId] as const,
|
|
},
|
|
research: {
|
|
all: ["research"] as const,
|
|
sessions: (projectName: string) =>
|
|
[...queryKeys.research.all, "sessions", projectName] as const,
|
|
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,
|
|
},
|
|
};
|