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:
Jason Woltje
2026-01-29 23:36:51 -06:00
parent a4be8b311d
commit 40f897020d
3 changed files with 1404 additions and 21 deletions

View File

@@ -101,7 +101,8 @@ export function MindmapViewer({
const results = await searchNodes(query);
setSearchResults(results);
} catch (err) {
console.error('Search failed:', err);
// Search failed - results will remain empty
setSearchResults([]);
} finally {
setIsSearching(false);
}

View File

@@ -4,6 +4,55 @@ import { useCallback, useEffect, useState } from 'react';
import { useSession } from '@/lib/auth-client';
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 {
id: string;
title: string;
@@ -117,14 +166,15 @@ async function apiFetch<T>(
}
// Transform Knowledge Entry to Graph Node
function entryToNode(entry: any): KnowledgeNode {
function entryToNode(entry: EntryDto): KnowledgeNode {
const tags = entry.tags || [];
return {
id: entry.id,
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,
tags: entry.tags?.map((t: any) => t.slug) || [],
domain: entry.tags?.length > 0 ? entry.tags[0].name : null,
tags: tags.map((t) => t.slug),
domain: tags.length > 0 ? tags[0]?.name ?? null : null,
metadata: {
slug: entry.slug,
status: entry.status,
@@ -138,7 +188,7 @@ function entryToNode(entry: any): KnowledgeNode {
}
// 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 {
title: node.title,
content: node.content || '',
@@ -150,8 +200,8 @@ function nodeToCreateDto(node: Omit<KnowledgeNode, 'id' | 'created_at' | 'update
}
// Transform Node update to Entry Update DTO
function nodeToUpdateDto(updates: Partial<KnowledgeNode>): any {
const dto: any = {};
function nodeToUpdateDto(updates: Partial<KnowledgeNode>): UpdateEntryDto {
const dto: UpdateEntryDto = {};
if (updates.title !== undefined) dto.title = updates.title;
if (updates.content !== undefined) {
@@ -185,7 +235,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
setError(null);
try {
// 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 || [];
// Transform entries to nodes
@@ -197,7 +247,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
for (const entry of entries) {
try {
const backlinksResponse = await apiFetch<any>(
const backlinksResponse = await apiFetch<BacklinksResponse>(
`/entries/${entry.slug}/backlinks`,
accessToken
);
@@ -220,7 +270,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
}
} catch (err) {
// 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
const nodesByType: Record<string, KnowledgeNode[]> = {};
graph.nodes.forEach(node => {
if (!nodesByType[node.node_type]) {
nodesByType[node.node_type] = [];
const nodeType = node.node_type;
if (!nodesByType[nodeType]) {
nodesByType[nodeType] = [];
}
nodesByType[node.node_type].push(node);
nodesByType[nodeType]!.push(node);
});
// Add nodes by type
@@ -337,7 +388,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
}
try {
const createDto = nodeToCreateDto(node);
const created = await apiFetch<any>('/entries', accessToken, {
const created = await apiFetch<EntryDto>('/entries', accessToken, {
method: 'POST',
body: JSON.stringify(createDto),
});
@@ -367,7 +418,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
const slug = node.metadata.slug as string;
const updateDto = nodeToUpdateDto(updates);
const updated = await apiFetch<any>(`/entries/${slug}`, accessToken, {
const updated = await apiFetch<EntryDto>(`/entries/${slug}`, accessToken, {
method: 'PUT',
body: JSON.stringify(updateDto),
});
@@ -494,7 +545,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
}
try {
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 || [];
return results.map(entryToNode);
} catch (err) {

1339
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff