Skip to content

Commit 9637041

Browse files
authored
Merge pull request #80 from cortex-reply/fix/knowledge-fixes
fix: applies minor fixes on knowledge page
2 parents 6384fa9 + 608cd16 commit 9637041

6 files changed

Lines changed: 645 additions & 61 deletions

File tree

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
import { useState } from 'react'
2+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
3+
import { Button } from '@/components/ui/button'
4+
import { Input } from '@/components/ui/input'
5+
import { Label } from '@/components/ui/label'
6+
import { Textarea } from '@/components/ui/textarea'
7+
import {
8+
Select,
9+
SelectContent,
10+
SelectItem,
11+
SelectTrigger,
12+
SelectValue,
13+
} from '@/components/ui/select'
14+
import { Checkbox } from '@/components/ui/checkbox'
15+
import { ScrollArea } from '@/components/ui/scroll-area'
16+
import { X, Search, FileText, Trash2 } from 'lucide-react'
17+
import { toast } from 'sonner'
18+
import { cn } from '@/lib/utils'
19+
import { Team, TeamKnowledgeContext } from '../types'
20+
21+
interface ManageTeamContextModalProps {
22+
isOpen: boolean
23+
onClose: () => void
24+
knowledgeSources: TeamKnowledgeContext[]
25+
onCreate?: (teamContext: TeamKnowledgeContext) => void
26+
onUpdate?: (data: TeamKnowledgeContext) => void
27+
onDelete?: (id: string) => void
28+
}
29+
30+
export const ManageTeamContextModal = ({
31+
isOpen,
32+
onClose,
33+
knowledgeSources,
34+
onCreate,
35+
onUpdate,
36+
onDelete,
37+
}: ManageTeamContextModalProps) => {
38+
const [searchQuery, setSearchQuery] = useState('')
39+
const [selectedSource, setSelectedSource] = useState<TeamKnowledgeContext | null>(null)
40+
const [label, setLabel] = useState('')
41+
const [description, setDescription] = useState('')
42+
const [groupBy, setGroupBy] = useState<string[]>([])
43+
const [tagInput, setTagInput] = useState('')
44+
const [sortBy, setSortBy] = useState<'name' | 'createdAt'>('name')
45+
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc')
46+
const [showDocumentCount, setShowDocumentCount] = useState(true)
47+
48+
const filteredSources = knowledgeSources.filter(
49+
(source) =>
50+
source.label.toLowerCase().includes(searchQuery.toLowerCase()) ||
51+
source.description?.toLowerCase().includes(searchQuery.toLowerCase()),
52+
)
53+
54+
const handleSelectSource = (source: TeamKnowledgeContext) => {
55+
setSelectedSource(source)
56+
setLabel(source.label)
57+
setDescription(source.description || '')
58+
setGroupBy(source.menuConfig?.groupBy || [])
59+
setSortBy((source.menuConfig?.sortBy as 'name' | 'createdAt') || 'name')
60+
setSortOrder(source.menuConfig?.sortOrder || 'asc')
61+
setShowDocumentCount(source.menuConfig?.showDocumentCount || true)
62+
}
63+
64+
const handleAddTag = () => {
65+
if (tagInput.trim() && !groupBy.includes(tagInput.trim())) {
66+
setGroupBy([...groupBy, tagInput.trim()])
67+
setTagInput('')
68+
}
69+
}
70+
71+
const handleRemoveTag = (tag: string) => {
72+
setGroupBy(groupBy.filter((t) => t !== tag))
73+
}
74+
75+
const handleKeyPress = (e: React.KeyboardEvent) => {
76+
if (e.key === 'Enter') {
77+
e.preventDefault()
78+
handleAddTag()
79+
}
80+
}
81+
82+
const resetForm = () => {
83+
setSelectedSource(null)
84+
setLabel('')
85+
setDescription('')
86+
setGroupBy([])
87+
setTagInput('')
88+
setSortBy('name')
89+
setSortOrder('asc')
90+
setShowDocumentCount(true)
91+
}
92+
93+
const handleCreate = () => {
94+
if (!label.trim()) {
95+
toast.error('Label is required')
96+
return
97+
}
98+
99+
onCreate?.({
100+
id: label.trim(),
101+
label: label.trim(),
102+
description: description.trim(),
103+
menuConfig: {
104+
groupBy,
105+
sortBy,
106+
sortOrder,
107+
showDocumentCount,
108+
},
109+
})
110+
111+
toast.success('Knowledge source created successfully')
112+
resetForm()
113+
}
114+
115+
const handleUpdate = () => {
116+
if (!selectedSource) return
117+
if (!label.trim()) {
118+
toast.error('Label is required')
119+
return
120+
}
121+
122+
onUpdate?.({
123+
id: selectedSource.id,
124+
label: label.trim(),
125+
description: description.trim(),
126+
menuConfig: {
127+
groupBy,
128+
sortBy,
129+
sortOrder,
130+
showDocumentCount,
131+
},
132+
})
133+
134+
toast.success('Knowledge source updated successfully')
135+
resetForm()
136+
}
137+
138+
const handleDelete = () => {
139+
if (!selectedSource) return
140+
141+
if (window.confirm(`Are you sure you want to delete "${selectedSource.label}"?`)) {
142+
onDelete?.(selectedSource.id)
143+
toast.success('Knowledge source deleted successfully')
144+
resetForm()
145+
}
146+
}
147+
148+
return (
149+
<Dialog open={isOpen} onOpenChange={onClose}>
150+
<DialogContent className="sm:max-w-[900px] max-h-[90vh] bg-card border-border p-0">
151+
<DialogHeader className="px-6 pt-6 pb-4 border-b border-border">
152+
<DialogTitle className="text-foreground text-2xl">Manage Knowledge Sources</DialogTitle>
153+
</DialogHeader>
154+
155+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 p-6 overflow-hidden">
156+
{/* Left side - List of contexts */}
157+
<div className="flex flex-col gap-4">
158+
<div className="relative">
159+
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
160+
<Input
161+
value={searchQuery}
162+
onChange={(e) => setSearchQuery(e.target.value)}
163+
placeholder="Search contexts..."
164+
className="pl-10 bg-background border-input"
165+
/>
166+
</div>
167+
168+
<ScrollArea className="rounded-md border border-border bg-background flex-1">
169+
<div className="p-2 space-y-2">
170+
{filteredSources.length === 0 ? (
171+
<div className="flex flex-col items-center justify-center py-8 text-center">
172+
<FileText className="h-12 w-12 text-muted-foreground mb-2" />
173+
<p className="text-sm text-muted-foreground">
174+
{searchQuery ? 'No contexts found' : 'No contexts yet'}
175+
</p>
176+
</div>
177+
) : (
178+
filteredSources.map((source) => (
179+
<div
180+
key={source.id}
181+
onClick={() => handleSelectSource(source)}
182+
className={cn(
183+
'p-3 rounded-lg cursor-pointer transition-all hover:bg-accent/50',
184+
selectedSource?.id === source.id && 'bg-accent text-accent-foreground',
185+
)}
186+
>
187+
<div className="flex items-start justify-between gap-2">
188+
<div className="flex-1 min-w-0">
189+
<h4 className="font-medium truncate">{source.label}</h4>
190+
{source.description && (
191+
<p className="text-sm opacity-80 truncate mt-1">{source.description}</p>
192+
)}
193+
{(source.menuConfig?.groupBy || []).length > 0 && (
194+
<div className="flex flex-wrap gap-1 mt-2">
195+
{source.menuConfig?.groupBy?.slice(0, 3).map((tag) => (
196+
<span
197+
key={tag}
198+
className="text-xs px-2 py-0.5 rounded-full bg-secondary text-secondary-foreground"
199+
>
200+
{tag}
201+
</span>
202+
))}
203+
{(source.menuConfig?.groupBy || []).length > 3 && (
204+
<span className="text-xs px-2 py-0.5">
205+
+{(source.menuConfig?.groupBy || []).length - 3}
206+
</span>
207+
)}
208+
</div>
209+
)}
210+
</div>
211+
</div>
212+
</div>
213+
))
214+
)}
215+
</div>
216+
</ScrollArea>
217+
218+
<Button variant="outline" onClick={resetForm} className="w-full">
219+
Clear Selection / New Context
220+
</Button>
221+
</div>
222+
223+
{/* Right side - Form */}
224+
<div className="space-y-4">
225+
<ScrollArea className="h-[400px] pr-4">
226+
<div className="space-y-4">
227+
<div className="space-y-2">
228+
<Label htmlFor="label" className="text-foreground">
229+
Label <span className="text-destructive">*</span>
230+
</Label>
231+
<Input
232+
id="label"
233+
value={label}
234+
onChange={(e) => setLabel(e.target.value)}
235+
placeholder="Enter label"
236+
className="bg-background border-input"
237+
/>
238+
</div>
239+
240+
<div className="space-y-2">
241+
<Label htmlFor="description" className="text-foreground">
242+
Description
243+
</Label>
244+
<Textarea
245+
id="description"
246+
value={description}
247+
onChange={(e) => setDescription(e.target.value)}
248+
placeholder="Enter description"
249+
className="bg-background border-input resize-none"
250+
rows={3}
251+
/>
252+
</div>
253+
254+
<div className="space-y-2">
255+
<Label className="text-foreground">Group By</Label>
256+
<div className="flex gap-2">
257+
<Input
258+
value={tagInput}
259+
onChange={(e) => setTagInput(e.target.value)}
260+
onKeyPress={handleKeyPress}
261+
placeholder="Type a tag and press Enter"
262+
className="bg-background border-input flex-1"
263+
/>
264+
<Button onClick={handleAddTag} className="bg-accent hover:bg-accent/90">
265+
Add
266+
</Button>
267+
</div>
268+
{groupBy.length > 0 && (
269+
<div className="flex flex-wrap gap-2 mt-2">
270+
{groupBy.map((tag) => (
271+
<div
272+
key={tag}
273+
className="bg-secondary text-secondary-foreground px-3 py-1 rounded-md flex items-center gap-2 text-sm"
274+
>
275+
{tag}
276+
<X
277+
className="h-3 w-3 cursor-pointer hover:text-destructive"
278+
onClick={() => handleRemoveTag(tag)}
279+
/>
280+
</div>
281+
))}
282+
</div>
283+
)}
284+
</div>
285+
286+
<div className="space-y-2">
287+
<Label htmlFor="sortBy" className="text-foreground">
288+
Sort By
289+
</Label>
290+
<Select
291+
value={sortBy}
292+
onValueChange={(val: 'name' | 'createdAt') => setSortBy(val)}
293+
>
294+
<SelectTrigger className="bg-background border-input">
295+
<SelectValue />
296+
</SelectTrigger>
297+
<SelectContent className="bg-popover border-border">
298+
<SelectItem value="name">Name</SelectItem>
299+
<SelectItem value="createdAt">Created At</SelectItem>
300+
</SelectContent>
301+
</Select>
302+
</div>
303+
304+
<div className="space-y-2">
305+
<Label htmlFor="sortOrder" className="text-foreground">
306+
Sort Order
307+
</Label>
308+
<Select
309+
value={sortOrder}
310+
onValueChange={(value: 'asc' | 'desc') => setSortOrder(value)}
311+
>
312+
<SelectTrigger className="bg-background border-input">
313+
<SelectValue />
314+
</SelectTrigger>
315+
<SelectContent className="bg-popover border-border">
316+
<SelectItem value="asc">Ascending</SelectItem>
317+
<SelectItem value="desc">Descending</SelectItem>
318+
</SelectContent>
319+
</Select>
320+
</div>
321+
322+
<div className="flex items-center space-x-2">
323+
<Checkbox
324+
id="showDocumentCount"
325+
checked={showDocumentCount}
326+
onCheckedChange={(checked) => setShowDocumentCount(checked as boolean)}
327+
/>
328+
<Label
329+
htmlFor="showDocumentCount"
330+
className="text-sm font-normal cursor-pointer text-foreground"
331+
>
332+
Show Document Count
333+
</Label>
334+
</div>
335+
</div>
336+
</ScrollArea>
337+
338+
<div className="flex gap-3 pt-4 border-t border-border">
339+
{selectedSource ? (
340+
<>
341+
<Button variant="destructive" onClick={handleDelete} className="flex-1">
342+
<Trash2 className="mr-2 h-4 w-4" />
343+
Delete
344+
</Button>
345+
<Button onClick={handleUpdate} className="flex-1 bg-accent hover:bg-accent/90">
346+
Update
347+
</Button>
348+
</>
349+
) : (
350+
<Button onClick={handleCreate} className="w-full bg-accent hover:bg-accent/90">
351+
Create
352+
</Button>
353+
)}
354+
</div>
355+
</div>
356+
</div>
357+
358+
<div className="px-6 py-4 border-t border-border flex justify-end">
359+
<Button variant="outline" onClick={onClose}>
360+
Close
361+
</Button>
362+
</div>
363+
</DialogContent>
364+
</Dialog>
365+
)
366+
}

src/components/Foundary/RichText/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const RichText: React.FC<RichTextProps> = ({ setValue, value, name, editable = t
9292
theme: {
9393
code: 'editor-code',
9494
heading: {
95-
h1: 'text-4xl',
95+
h1: 'text-4xl text-primary',
9696
h2: 'text-3xl',
9797
h3: 'text-2xl',
9898
h4: 'text-lg',

src/components/Foundary/RichText/plugins/toolbar-plugin.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ export default function ToolbarPlugin() {
398398
]
399399

400400
return (
401-
<div className="flex justify-between border border-[#222222] py-2 px-2">
401+
<div className="flex justify-between border border-[#222222] py-2 px-2 sticky top-0 bg-black z-50">
402402
<div className="flex gap-2 " ref={toolbarRef}>
403403
{toolbarActions.map((group, index) => (
404404
<Fragment key={`toolbar-option-${group.length}-${index}`}>

0 commit comments

Comments
 (0)