fix: code review cleanup
- Fixed TypeScript error: object possibly undefined in useGraphData.ts - Removed console.error and console.warn statements - Replaced all 'any' types with proper interface types - Added proper type definitions for API DTOs (EntryDto, CreateEntryDto, UpdateEntryDto, etc.) - Improved type safety across mindmap integration components
This commit is contained in:
@@ -101,7 +101,8 @@ export function MindmapViewer({
|
|||||||
const results = await searchNodes(query);
|
const results = await searchNodes(query);
|
||||||
setSearchResults(results);
|
setSearchResults(results);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Search failed:', err);
|
// Search failed - results will remain empty
|
||||||
|
setSearchResults([]);
|
||||||
} finally {
|
} finally {
|
||||||
setIsSearching(false);
|
setIsSearching(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,55 @@ import { useCallback, useEffect, useState } from 'react';
|
|||||||
import { useSession } from '@/lib/auth-client';
|
import { useSession } from '@/lib/auth-client';
|
||||||
import { handleSessionExpired, isSessionExpiring } from '@/lib/api';
|
import { handleSessionExpired, isSessionExpiring } from '@/lib/api';
|
||||||
|
|
||||||
|
// API Response types
|
||||||
|
interface TagDto {
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EntryDto {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
summary: string;
|
||||||
|
status: string;
|
||||||
|
visibility: string;
|
||||||
|
tags: TagDto[];
|
||||||
|
createdBy: string;
|
||||||
|
updatedBy: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EntriesResponse {
|
||||||
|
data: EntryDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BacklinksResponse {
|
||||||
|
backlinks: Array<{ id: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateEntryDto {
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
summary: string;
|
||||||
|
tags: string[];
|
||||||
|
status: string;
|
||||||
|
visibility: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateEntryDto {
|
||||||
|
title?: string;
|
||||||
|
content?: string | null;
|
||||||
|
summary?: string;
|
||||||
|
tags?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SearchResponse {
|
||||||
|
results: EntryDto[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface KnowledgeNode {
|
export interface KnowledgeNode {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -117,14 +166,15 @@ async function apiFetch<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transform Knowledge Entry to Graph Node
|
// Transform Knowledge Entry to Graph Node
|
||||||
function entryToNode(entry: any): KnowledgeNode {
|
function entryToNode(entry: EntryDto): KnowledgeNode {
|
||||||
|
const tags = entry.tags || [];
|
||||||
return {
|
return {
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
title: entry.title,
|
title: entry.title,
|
||||||
node_type: entry.tags?.[0]?.slug || 'concept', // Use first tag as node type, fallback to 'concept'
|
node_type: tags[0]?.slug || 'concept', // Use first tag as node type, fallback to 'concept'
|
||||||
content: entry.content || entry.summary || null,
|
content: entry.content || entry.summary || null,
|
||||||
tags: entry.tags?.map((t: any) => t.slug) || [],
|
tags: tags.map((t) => t.slug),
|
||||||
domain: entry.tags?.length > 0 ? entry.tags[0].name : null,
|
domain: tags.length > 0 ? tags[0]?.name ?? null : null,
|
||||||
metadata: {
|
metadata: {
|
||||||
slug: entry.slug,
|
slug: entry.slug,
|
||||||
status: entry.status,
|
status: entry.status,
|
||||||
@@ -138,7 +188,7 @@ function entryToNode(entry: any): KnowledgeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transform Node to Entry Create DTO
|
// Transform Node to Entry Create DTO
|
||||||
function nodeToCreateDto(node: Omit<KnowledgeNode, 'id' | 'created_at' | 'updated_at'>): any {
|
function nodeToCreateDto(node: Omit<KnowledgeNode, 'id' | 'created_at' | 'updated_at'>): CreateEntryDto {
|
||||||
return {
|
return {
|
||||||
title: node.title,
|
title: node.title,
|
||||||
content: node.content || '',
|
content: node.content || '',
|
||||||
@@ -150,8 +200,8 @@ function nodeToCreateDto(node: Omit<KnowledgeNode, 'id' | 'created_at' | 'update
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transform Node update to Entry Update DTO
|
// Transform Node update to Entry Update DTO
|
||||||
function nodeToUpdateDto(updates: Partial<KnowledgeNode>): any {
|
function nodeToUpdateDto(updates: Partial<KnowledgeNode>): UpdateEntryDto {
|
||||||
const dto: any = {};
|
const dto: UpdateEntryDto = {};
|
||||||
|
|
||||||
if (updates.title !== undefined) dto.title = updates.title;
|
if (updates.title !== undefined) dto.title = updates.title;
|
||||||
if (updates.content !== undefined) {
|
if (updates.content !== undefined) {
|
||||||
@@ -185,7 +235,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
// Fetch all entries
|
// Fetch all entries
|
||||||
const response = await apiFetch<any>('/entries?limit=100', accessToken);
|
const response = await apiFetch<EntriesResponse>('/entries?limit=100', accessToken);
|
||||||
const entries = response.data || [];
|
const entries = response.data || [];
|
||||||
|
|
||||||
// Transform entries to nodes
|
// Transform entries to nodes
|
||||||
@@ -197,7 +247,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
try {
|
try {
|
||||||
const backlinksResponse = await apiFetch<any>(
|
const backlinksResponse = await apiFetch<BacklinksResponse>(
|
||||||
`/entries/${entry.slug}/backlinks`,
|
`/entries/${entry.slug}/backlinks`,
|
||||||
accessToken
|
accessToken
|
||||||
);
|
);
|
||||||
@@ -220,7 +270,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Silently skip backlink errors for individual entries
|
// Silently skip backlink errors for individual entries
|
||||||
console.warn(`Failed to fetch backlinks for ${entry.slug}:`, err);
|
// Logging suppressed to avoid console pollution in production
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,10 +300,11 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
// Group nodes by type
|
// Group nodes by type
|
||||||
const nodesByType: Record<string, KnowledgeNode[]> = {};
|
const nodesByType: Record<string, KnowledgeNode[]> = {};
|
||||||
graph.nodes.forEach(node => {
|
graph.nodes.forEach(node => {
|
||||||
if (!nodesByType[node.node_type]) {
|
const nodeType = node.node_type;
|
||||||
nodesByType[node.node_type] = [];
|
if (!nodesByType[nodeType]) {
|
||||||
|
nodesByType[nodeType] = [];
|
||||||
}
|
}
|
||||||
nodesByType[node.node_type].push(node);
|
nodesByType[nodeType]!.push(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add nodes by type
|
// Add nodes by type
|
||||||
@@ -337,7 +388,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const createDto = nodeToCreateDto(node);
|
const createDto = nodeToCreateDto(node);
|
||||||
const created = await apiFetch<any>('/entries', accessToken, {
|
const created = await apiFetch<EntryDto>('/entries', accessToken, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(createDto),
|
body: JSON.stringify(createDto),
|
||||||
});
|
});
|
||||||
@@ -367,7 +418,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
const slug = node.metadata.slug as string;
|
const slug = node.metadata.slug as string;
|
||||||
const updateDto = nodeToUpdateDto(updates);
|
const updateDto = nodeToUpdateDto(updates);
|
||||||
|
|
||||||
const updated = await apiFetch<any>(`/entries/${slug}`, accessToken, {
|
const updated = await apiFetch<EntryDto>(`/entries/${slug}`, accessToken, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify(updateDto),
|
body: JSON.stringify(updateDto),
|
||||||
});
|
});
|
||||||
@@ -494,7 +545,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams({ q: query, limit: '50' });
|
const params = new URLSearchParams({ q: query, limit: '50' });
|
||||||
const response = await apiFetch<any>(`/search?${params}`, accessToken);
|
const response = await apiFetch<EntriesResponse>(`/search?${params}`, accessToken);
|
||||||
const results = response.data || [];
|
const results = response.data || [];
|
||||||
return results.map(entryToNode);
|
return results.map(entryToNode);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
1339
pnpm-lock.yaml
generated
1339
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user