sync: feed pane UX from private repo
- Strip kanban, grid, and saved views from ViewsOverlay (1103→631 lines) - Add sort order dropdown (Updated/Edges/Created) with localStorage persistence - Polish filter button, dropdown, and filter chips - Switch to server-side dimension filtering with AND logic (dimensionsMatch=all) - Add description preview and edge count to node cards - Add 'created' sort option and dimensionsMatch param to types, service, API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
86f43c9975
commit
2940e142c8
+16
-2
@@ -25,12 +25,26 @@ export async function GET(request: NextRequest) {
|
|||||||
filters.dimensions = dimensionsParam.split(',').map(dim => dim.trim()).filter(Boolean);
|
filters.dimensions = dimensionsParam.split(',').map(dim => dim.trim()).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle sortBy parameter
|
// Handle dimensionsMatch parameter (any|all)
|
||||||
|
const dimensionsMatchParam = searchParams.get('dimensionsMatch');
|
||||||
|
if (dimensionsMatchParam === 'all') {
|
||||||
|
filters.dimensionsMatch = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle sortBy parameter (sortBy=edges|updated|created)
|
||||||
const sortByParam = searchParams.get('sortBy');
|
const sortByParam = searchParams.get('sortBy');
|
||||||
if (sortByParam === 'edges' || sortByParam === 'updated') {
|
if (sortByParam === 'edges' || sortByParam === 'updated' || sortByParam === 'created') {
|
||||||
filters.sortBy = sortByParam;
|
filters.sortBy = sortByParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also support sort=created_at|updated_at with order=asc|desc (used by feed)
|
||||||
|
const sortParam = searchParams.get('sort');
|
||||||
|
if (sortParam === 'created_at') {
|
||||||
|
filters.sortBy = 'created';
|
||||||
|
} else if (sortParam === 'updated_at') {
|
||||||
|
filters.sortBy = 'updated';
|
||||||
|
}
|
||||||
|
|
||||||
const nodes = await nodeService.getNodes(filters);
|
const nodes = await nodeService.getNodes(filters);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ export class NodeService {
|
|||||||
// PostgreSQL path removed in SQLite-only consolidation
|
// PostgreSQL path removed in SQLite-only consolidation
|
||||||
|
|
||||||
private async getNodesSQLite(filters: NodeFilters = {}): Promise<Node[]> {
|
private async getNodesSQLite(filters: NodeFilters = {}): Promise<Node[]> {
|
||||||
const { dimensions, search, limit = 100, offset = 0, sortBy } = filters;
|
const { dimensions, search, limit = 100, offset = 0, sortBy, dimensionsMatch = 'any' } = filters;
|
||||||
const sqlite = getSQLiteClient();
|
const sqlite = getSQLiteClient();
|
||||||
|
|
||||||
// Use nodes_v view for array-like dimensions behavior (exclude embedding BLOB for performance)
|
// Use nodes_v view for array-like dimensions behavior (exclude embedding BLOB for performance)
|
||||||
@@ -28,12 +28,23 @@ export class NodeService {
|
|||||||
|
|
||||||
// Filter by dimensions (SQLite JOIN with node_dimensions)
|
// Filter by dimensions (SQLite JOIN with node_dimensions)
|
||||||
if (dimensions && dimensions.length > 0) {
|
if (dimensions && dimensions.length > 0) {
|
||||||
query += ` AND EXISTS (
|
if (dimensionsMatch === 'all' && dimensions.length > 1) {
|
||||||
SELECT 1 FROM node_dimensions nd
|
// AND logic: node must have ALL specified dimensions
|
||||||
WHERE nd.node_id = n.id
|
query += ` AND (
|
||||||
AND nd.dimension IN (${dimensions.map(() => '?').join(',')})
|
SELECT COUNT(DISTINCT nd.dimension) FROM node_dimensions nd
|
||||||
)`;
|
WHERE nd.node_id = n.id
|
||||||
params.push(...dimensions);
|
AND nd.dimension IN (${dimensions.map(() => '?').join(',')})
|
||||||
|
) = ?`;
|
||||||
|
params.push(...dimensions, dimensions.length);
|
||||||
|
} else {
|
||||||
|
// OR logic: node must have at least one of the specified dimensions
|
||||||
|
query += ` AND EXISTS (
|
||||||
|
SELECT 1 FROM node_dimensions nd
|
||||||
|
WHERE nd.node_id = n.id
|
||||||
|
AND nd.dimension IN (${dimensions.map(() => '?').join(',')})
|
||||||
|
)`;
|
||||||
|
params.push(...dimensions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text search in title, description, and content (SQLite LIKE with COLLATE NOCASE)
|
// Text search in title, description, and content (SQLite LIKE with COLLATE NOCASE)
|
||||||
@@ -62,6 +73,8 @@ export class NodeService {
|
|||||||
} else if (sortBy === 'edges') {
|
} else if (sortBy === 'edges') {
|
||||||
// Sort by edge count (most connected first)
|
// Sort by edge count (most connected first)
|
||||||
query += ' ORDER BY edge_count DESC, n.updated_at DESC';
|
query += ' ORDER BY edge_count DESC, n.updated_at DESC';
|
||||||
|
} else if (sortBy === 'created') {
|
||||||
|
query += ' ORDER BY n.created_at DESC';
|
||||||
} else {
|
} else {
|
||||||
query += ' ORDER BY n.updated_at DESC';
|
query += ' ORDER BY n.updated_at DESC';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ export interface NodeFilters {
|
|||||||
search?: string; // Text search in title/content
|
search?: string; // Text search in title/content
|
||||||
limit?: number;
|
limit?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
sortBy?: 'updated' | 'edges'; // Sort by updated_at or edge count
|
sortBy?: 'updated' | 'edges' | 'created'; // Sort by updated_at, edge count, or created_at
|
||||||
|
dimensionsMatch?: 'any' | 'all'; // 'any' = OR (default), 'all' = AND
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChunkData {
|
export interface ChunkData {
|
||||||
|
|||||||
Reference in New Issue
Block a user