-
Notifications
You must be signed in to change notification settings - Fork 108
Expand file tree
/
Copy pathMemoryList.tsx
More file actions
146 lines (136 loc) · 5.38 KB
/
Copy pathMemoryList.tsx
File metadata and controls
146 lines (136 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { useState } from 'react'
import { useMemories, useDeleteMemory } from '@/hooks/useMemories'
import { useDebounce } from '@/hooks/useDebounce'
import type { Memory } from '@opencode-manager/shared/types'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Loader2, Trash2, Edit, Search } from 'lucide-react'
import { MemoryFormDialog } from './MemoryFormDialog'
import { DeleteDialog } from '@/components/ui/delete-dialog'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
interface MemoryListProps {
projectId?: string
scope?: string
showFilters?: boolean
}
const scopeColors: Record<string, string> = {
convention: 'border-info/25 bg-info/12 text-info',
decision: 'border-success/25 bg-success/12 text-success',
context: 'border-accent-foreground/10 bg-accent text-accent-foreground',
}
export function MemoryList({ projectId, scope, showFilters = true }: MemoryListProps) {
const [filterScope, setFilterScope] = useState<string>(scope || 'all')
const [searchQuery, setSearchQuery] = useState<string>('')
const debouncedSearch = useDebounce(searchQuery, 300)
const [editMemory, setEditMemory] = useState<Memory | null>(null)
const [deleteMemoryId, setDeleteMemoryId] = useState<number | null>(null)
const filters = {
projectId,
scope: filterScope !== 'all' ? filterScope : undefined,
...(debouncedSearch ? { content: debouncedSearch } : undefined),
}
const { data: memories, isLoading } = useMemories(filters)
const deleteMutation = useDeleteMemory()
const handleDelete = () => {
if (deleteMemoryId !== null) {
deleteMutation.mutate(deleteMemoryId)
setDeleteMemoryId(null)
}
}
return (
<div className="space-y-4">
{showFilters && (
<div className="flex flex-wrap gap-2 items-center">
<Input
placeholder="Search memories..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-64"
/>
<Select value={filterScope} onValueChange={setFilterScope}>
<SelectTrigger className="w-36">
<SelectValue placeholder="Scope" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Scopes</SelectItem>
<SelectItem value="convention">Convention</SelectItem>
<SelectItem value="decision">Decision</SelectItem>
<SelectItem value="context">Context</SelectItem>
</SelectContent>
</Select>
</div>
)}
{isLoading ? (
<div className="flex items-center justify-center p-8">
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
</div>
) : memories && memories.length === 0 ? (
<div className="text-center p-8 text-muted-foreground">
<Search className="w-8 h-8 mx-auto mb-2 opacity-50" />
<p>No memories found</p>
</div>
) : (
<div className="space-y-2">
{memories?.map((memory) => (
<div
key={memory.id}
className="p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors"
>
<div className="flex items-center justify-between gap-2 mb-2">
<div className="flex items-center gap-2 flex-wrap min-w-0">
<Badge className={`text-xs ${scopeColors[memory.scope]}`}>
{memory.scope}
</Badge>
{memory.filePath && (
<span className="text-xs text-muted-foreground truncate max-w-xs">
{memory.filePath}
</span>
)}
</div>
<div className="flex items-center gap-1 flex-shrink-0">
<Button
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => setEditMemory(memory)}
>
<Edit className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-destructive"
onClick={() => setDeleteMemoryId(memory.id)}
>
<Trash2 className="w-4 h-4" />
</Button>
</div>
</div>
<p className="text-sm whitespace-pre-wrap break-words">{memory.content}</p>
<p className="text-xs text-muted-foreground mt-2">
Created: {new Date(memory.createdAt).toLocaleDateString()}
</p>
</div>
))}
</div>
)}
{editMemory && (
<MemoryFormDialog
memory={editMemory}
projectId={projectId}
open={!!editMemory}
onOpenChange={(open: boolean) => !open && setEditMemory(null)}
/>
)}
<DeleteDialog
open={deleteMemoryId !== null}
onOpenChange={(open: boolean) => !open && setDeleteMemoryId(null)}
onConfirm={handleDelete}
onCancel={() => setDeleteMemoryId(null)}
title="Delete Memory"
description="Are you sure you want to delete this memory? This action cannot be undone."
/>
</div>
)
}