First batch of smart stuff
This commit is contained in:
@@ -8,6 +8,7 @@ import { Edit, Calendar, Pin } from "lucide-react";
|
||||
import { getNoteColor } from "@/lib/constants";
|
||||
import clsx from "clsx";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { RelatedNotes } from "./related-notes";
|
||||
|
||||
|
||||
interface NoteViewDialogProps {
|
||||
@@ -15,9 +16,10 @@ interface NoteViewDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onEdit: () => void;
|
||||
onSelectNote?: (id: string) => void;
|
||||
}
|
||||
|
||||
export function NoteViewDialog({ note, open, onOpenChange, onEdit }: NoteViewDialogProps) {
|
||||
export function NoteViewDialog({ note, open, onOpenChange, onEdit, onSelectNote }: NoteViewDialogProps) {
|
||||
const colorClass = getNoteColor(note.color);
|
||||
|
||||
return (
|
||||
@@ -42,6 +44,17 @@ export function NoteViewDialog({ note, open, onOpenChange, onEdit }: NoteViewDia
|
||||
<div className="prose dark:prose-invert max-w-none text-base leading-relaxed break-words pb-6">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{note.content}</ReactMarkdown>
|
||||
</div>
|
||||
|
||||
{/* Smart Features: Related Notes */}
|
||||
<div className="pb-4">
|
||||
<RelatedNotes
|
||||
noteId={note.id}
|
||||
onSelectNote={onSelectNote ? (id) => {
|
||||
onOpenChange(false);
|
||||
setTimeout(() => onSelectNote(id), 100); // Small delay to allow dialog close animation?
|
||||
} : undefined}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="pt-4 mt-2 border-t border-black/5 dark:border-white/5 flex sm:justify-between items-center gap-4 shrink-0">
|
||||
|
||||
62
k-notes-frontend/src/components/related-notes.tsx
Normal file
62
k-notes-frontend/src/components/related-notes.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { useRelatedNotes } from "@/hooks/use-related-notes";
|
||||
import { useNotes } from "@/hooks/use-notes";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Link2 } from "lucide-react";
|
||||
|
||||
interface RelatedNotesProps {
|
||||
noteId: string;
|
||||
onSelectNote?: (id: string) => void;
|
||||
}
|
||||
|
||||
export function RelatedNotes({ noteId, onSelectNote }: RelatedNotesProps) {
|
||||
const { relatedLinks, isRelatedLoading } = useRelatedNotes(noteId);
|
||||
const { data: notes } = useNotes(); // We need to look up note titles from source_id
|
||||
|
||||
if (isRelatedLoading) {
|
||||
return (
|
||||
<div className="space-y-2 mt-4">
|
||||
<h3 className="text-sm font-medium">Related Notes</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Skeleton className="h-8 w-24" />
|
||||
<Skeleton className="h-8 w-32" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!relatedLinks || relatedLinks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2 mt-6 border-t pt-4">
|
||||
<h3 className="text-sm font-medium flex items-center gap-2">
|
||||
<Link2 className="w-4 h-4" />
|
||||
Related Notes
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{relatedLinks.map((link) => {
|
||||
const targetNote = notes?.find((n: any) => n.id === link.target_note_id);
|
||||
if (!targetNote) return null;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={link.target_note_id}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 text-xs max-w-[200px] justify-start"
|
||||
onClick={() => onSelectNote?.(link.target_note_id)}
|
||||
>
|
||||
<span className="truncate">{targetNote.title || "Untitled"}</span>
|
||||
<Badge variant="secondary" className="ml-2 text-[10px] h-5 px-1">
|
||||
{Math.round(link.score * 100)}%
|
||||
</Badge>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
k-notes-frontend/src/hooks/use-related-notes.ts
Normal file
23
k-notes-frontend/src/hooks/use-related-notes.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
export interface NoteLink {
|
||||
source_note_id: string;
|
||||
target_note_id: string;
|
||||
score: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export function useRelatedNotes(noteId: string | undefined) {
|
||||
const { data, error, isLoading } = useQuery({
|
||||
queryKey: ["notes", noteId, "related"],
|
||||
queryFn: () => api.get(`/notes/${noteId}/related`),
|
||||
enabled: !!noteId,
|
||||
});
|
||||
|
||||
return {
|
||||
relatedLinks: data as NoteLink[] | undefined,
|
||||
isRelatedLoading: isLoading,
|
||||
relatedError: error,
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
const NOTE_COLORS = [
|
||||
{ name: "DEFAULT", value: "bg-background border-border", label: "Default" },
|
||||
{ name: "RED", value: "bg-red-50 border-red-200 dark:bg-red-950/20 dark:border-red-900", label: "Red" },
|
||||
{ name: "ORANGE", value: "bg-orange-50 border-orange-200 dark:bg-orange-950/20 dark:border-orange-900", label: "Orange" },
|
||||
{ name: "YELLOW", value: "bg-yellow-50 border-yellow-200 dark:bg-yellow-950/20 dark:border-yellow-900", label: "Yellow" },
|
||||
{ name: "GREEN", value: "bg-green-50 border-green-200 dark:bg-green-950/20 dark:border-green-900", label: "Green" },
|
||||
{ name: "TEAL", value: "bg-teal-50 border-teal-200 dark:bg-teal-950/20 dark:border-teal-900", label: "Teal" },
|
||||
{ name: "BLUE", value: "bg-blue-50 border-blue-200 dark:bg-blue-950/20 dark:border-blue-900", label: "Blue" },
|
||||
{ name: "INDIGO", value: "bg-indigo-50 border-indigo-200 dark:bg-indigo-950/20 dark:border-indigo-900", label: "Indigo" },
|
||||
{ name: "RED", value: "bg-red-50 border-red-200 dark:bg-red-950 dark:border-red-900", label: "Red" },
|
||||
{ name: "ORANGE", value: "bg-orange-50 border-orange-200 dark:bg-orange-950 dark:border-orange-900", label: "Orange" },
|
||||
{ name: "YELLOW", value: "bg-yellow-50 border-yellow-200 dark:bg-yellow-950 dark:border-yellow-900", label: "Yellow" },
|
||||
{ name: "GREEN", value: "bg-green-50 border-green-200 dark:bg-green-950 dark:border-green-900", label: "Green" },
|
||||
{ name: "TEAL", value: "bg-teal-50 border-teal-200 dark:bg-teal-950 dark:border-teal-900", label: "Teal" },
|
||||
{ name: "BLUE", value: "bg-blue-50 border-blue-200 dark:bg-blue-950 dark:border-blue-900", label: "Blue" },
|
||||
{ name: "INDIGO", value: "bg-indigo-50 border-indigo-200 dark:bg-indigo-950 dark:border-indigo-900", label: "Indigo" },
|
||||
];
|
||||
|
||||
export function getNoteColor(colorName: string | undefined): string {
|
||||
|
||||
Reference in New Issue
Block a user