fixed spacing and flex

This commit is contained in:
Deon Sanchez 2025-10-06 12:49:39 -06:00
parent 1d4a91eb7b
commit 1dc0a370f1
6 changed files with 641 additions and 782 deletions

View file

@ -25,6 +25,7 @@ import {
FilterIconPopover, FilterIconPopover,
IconKey, IconKey,
} from "@/components/filter-icon-popover"; } from "@/components/filter-icon-popover";
import { useLayout } from "@/contexts/layout-context";
interface FacetBucket { interface FacetBucket {
key: string; key: string;
@ -48,7 +49,15 @@ export const filterAccentClasses: Record<FilterColor, string> = {
red: "bg-accent-red text-accent-red-foreground", red: "bg-accent-red text-accent-red-foreground",
}; };
export function KnowledgeFilterPanel() { interface KnowledgeFilterPanelProps {
totalTopOffset: number;
headerHeight: number;
}
export function KnowledgeFilterPanel({
totalTopOffset,
headerHeight,
}: KnowledgeFilterPanelProps) {
const { const {
selectedFilter, selectedFilter,
parsedFilterData, parsedFilterData,
@ -135,7 +144,7 @@ export function KnowledgeFilterPanel() {
// Load available facets using search aggregations hook // Load available facets using search aggregations hook
const { data: aggregations } = useGetSearchAggregations("*", 1, 0, { const { data: aggregations } = useGetSearchAggregations("*", 1, 0, {
enabled: isPanelOpen, enabled: isPanelOpen,
placeholderData: (prev) => prev, placeholderData: prev => prev,
staleTime: 60_000, staleTime: 60_000,
gcTime: 5 * 60_000, gcTime: 5 * 60_000,
}); });
@ -213,7 +222,7 @@ export function KnowledgeFilterPanel() {
facetType: keyof typeof selectedFilters, facetType: keyof typeof selectedFilters,
newValues: string[] newValues: string[]
) => { ) => {
setSelectedFilters((prev) => ({ setSelectedFilters(prev => ({
...prev, ...prev,
[facetType]: newValues, [facetType]: newValues,
})); }));
@ -231,7 +240,10 @@ export function KnowledgeFilterPanel() {
}; };
return ( return (
<div className="fixed right-0 top-14 bottom-0 w-80 bg-background border-l z-40 overflow-y-auto"> <div
className={`fixed right-0 bottom-0 w-80 bg-background border-l z-40 overflow-y-auto`}
style={{ top: `${totalTopOffset}px` }}
>
<Card className="h-full rounded-none border-0 shadow-lg flex flex-col"> <Card className="h-full rounded-none border-0 shadow-lg flex flex-col">
<CardHeader className="pb-3"> <CardHeader className="pb-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -270,7 +282,7 @@ export function KnowledgeFilterPanel() {
<Input <Input
id="filter-name" id="filter-name"
value={name} value={name}
onChange={(e) => { onChange={e => {
const v = e.target.value; const v = e.target.value;
setName(v); setName(v);
if (nameError && v.trim()) { if (nameError && v.trim()) {
@ -301,7 +313,7 @@ export function KnowledgeFilterPanel() {
<Textarea <Textarea
id="filter-description" id="filter-description"
value={description} value={description}
onChange={(e) => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
placeholder="Provide a brief description of your knowledge filter..." placeholder="Provide a brief description of your knowledge filter..."
rows={3} rows={3}
/> />
@ -318,7 +330,7 @@ export function KnowledgeFilterPanel() {
placeholder="Enter your search query..." placeholder="Enter your search query..."
value={query} value={query}
className="font-mono placeholder:font-mono" className="font-mono placeholder:font-mono"
onChange={(e) => setQuery(e.target.value)} onChange={e => setQuery(e.target.value)}
rows={2} rows={2}
/> />
</div> </div>
@ -327,13 +339,13 @@ export function KnowledgeFilterPanel() {
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<MultiSelect <MultiSelect
options={(availableFacets.data_sources || []).map((bucket) => ({ options={(availableFacets.data_sources || []).map(bucket => ({
value: bucket.key, value: bucket.key,
label: bucket.key, label: bucket.key,
count: bucket.count, count: bucket.count,
}))} }))}
value={selectedFilters.data_sources} value={selectedFilters.data_sources}
onValueChange={(values) => onValueChange={values =>
handleFilterChange("data_sources", values) handleFilterChange("data_sources", values)
} }
placeholder="Select sources..." placeholder="Select sources..."
@ -343,15 +355,13 @@ export function KnowledgeFilterPanel() {
<div className="space-y-2"> <div className="space-y-2">
<MultiSelect <MultiSelect
options={(availableFacets.document_types || []).map( options={(availableFacets.document_types || []).map(bucket => ({
(bucket) => ({ value: bucket.key,
value: bucket.key, label: bucket.key,
label: bucket.key, count: bucket.count,
count: bucket.count, }))}
})
)}
value={selectedFilters.document_types} value={selectedFilters.document_types}
onValueChange={(values) => onValueChange={values =>
handleFilterChange("document_types", values) handleFilterChange("document_types", values)
} }
placeholder="Select types..." placeholder="Select types..."
@ -361,13 +371,13 @@ export function KnowledgeFilterPanel() {
<div className="space-y-2"> <div className="space-y-2">
<MultiSelect <MultiSelect
options={(availableFacets.owners || []).map((bucket) => ({ options={(availableFacets.owners || []).map(bucket => ({
value: bucket.key, value: bucket.key,
label: bucket.key, label: bucket.key,
count: bucket.count, count: bucket.count,
}))} }))}
value={selectedFilters.owners} value={selectedFilters.owners}
onValueChange={(values) => handleFilterChange("owners", values)} onValueChange={values => handleFilterChange("owners", values)}
placeholder="Select owners..." placeholder="Select owners..."
allOptionLabel="All Owners" allOptionLabel="All Owners"
/> />
@ -376,14 +386,14 @@ export function KnowledgeFilterPanel() {
<div className="space-y-2"> <div className="space-y-2">
<MultiSelect <MultiSelect
options={(availableFacets.connector_types || []).map( options={(availableFacets.connector_types || []).map(
(bucket) => ({ bucket => ({
value: bucket.key, value: bucket.key,
label: bucket.key, label: bucket.key,
count: bucket.count, count: bucket.count,
}) })
)} )}
value={selectedFilters.connector_types} value={selectedFilters.connector_types}
onValueChange={(values) => onValueChange={values =>
handleFilterChange("connector_types", values) handleFilterChange("connector_types", values)
} }
placeholder="Select connectors..." placeholder="Select connectors..."
@ -403,7 +413,7 @@ export function KnowledgeFilterPanel() {
min="1" min="1"
max="1000" max="1000"
value={resultLimit} value={resultLimit}
onChange={(e) => { onChange={e => {
const newLimit = Math.max( const newLimit = Math.max(
1, 1,
Math.min(1000, parseInt(e.target.value) || 1) Math.min(1000, parseInt(e.target.value) || 1)
@ -416,7 +426,7 @@ export function KnowledgeFilterPanel() {
</div> </div>
<Slider <Slider
value={[resultLimit]} value={[resultLimit]}
onValueChange={(values) => setResultLimit(values[0])} onValueChange={values => setResultLimit(values[0])}
max={1000} max={1000}
min={1} min={1}
step={1} step={1}
@ -436,7 +446,7 @@ export function KnowledgeFilterPanel() {
max="5" max="5"
step="0.1" step="0.1"
value={scoreThreshold} value={scoreThreshold}
onChange={(e) => onChange={e =>
setScoreThreshold(parseFloat(e.target.value) || 0) setScoreThreshold(parseFloat(e.target.value) || 0)
} }
className="h-6 text-xs text-right px-2 bg-muted/30 !border-0 rounded ml-auto focus:ring-0 focus:outline-none" className="h-6 text-xs text-right px-2 bg-muted/30 !border-0 rounded ml-auto focus:ring-0 focus:outline-none"
@ -445,7 +455,7 @@ export function KnowledgeFilterPanel() {
</div> </div>
<Slider <Slider
value={[scoreThreshold]} value={[scoreThreshold]}
onValueChange={(values) => setScoreThreshold(values[0])} onValueChange={values => setScoreThreshold(values[0])}
max={5} max={5}
min={0} min={0}
step={0.1} step={0.1}

View file

@ -2047,21 +2047,10 @@ function ChatPage() {
}; };
return ( return (
<div <div className="h-full flex flex-col container">
className={`fixed inset-0 md:left-72 flex flex-col transition-all duration-300 ${
isMenuOpen && isPanelOpen
? "md:right-[704px]" // Both open: 384px (menu) + 320px (KF panel)
: isMenuOpen
? "md:right-96" // Only menu open: 384px
: isPanelOpen
? "md:right-80" // Only KF panel open: 320px
: "md:right-6" // Neither open: 24px
}`}
style={{ top: `${totalTopOffset}px` }}
>
{/* Debug header - only show in debug mode */} {/* Debug header - only show in debug mode */}
{isDebugMode && ( {isDebugMode && (
<div className="flex items-center justify-between mb-6 px-6 pt-6"> <div className="flex items-center justify-between mb-6 px-6 pt-6 ">
<div className="flex items-center gap-2"></div> <div className="flex items-center gap-2"></div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{/* Async Mode Toggle */} {/* Async Mode Toggle */}

View file

@ -133,22 +133,8 @@ function ChunksPageContent() {
} }
return ( return (
<div <div className="flex flex-row h-full">
className={`fixed inset-0 md:left-72 flex flex-row transition-all duration-300 ${ <div className="flex-1 flex flex-col min-h-0">
isMenuOpen && isPanelOpen
? "md:right-[704px]"
: // Both open: 384px (menu) + 320px (KF panel)
isMenuOpen
? "md:right-96"
: // Only menu open: 384px
isPanelOpen
? "md:right-80"
: // Only KF panel open: 320px
"md:right-6" // Neither open: 24px
}`}
style={{ top: `${totalTopOffset}px` }}
>
<div className="flex-1 flex flex-col min-h-0 px-6 py-6">
{/* Header */} {/* Header */}
<div className="flex flex-col mb-6"> <div className="flex flex-col mb-6">
<div className="flex flex-row items-center gap-3 mb-6"> <div className="flex flex-row items-center gap-3 mb-6">

View file

@ -11,7 +11,6 @@ import { KnowledgeDropdown } from "@/components/knowledge-dropdown";
import { ProtectedRoute } from "@/components/protected-route"; import { ProtectedRoute } from "@/components/protected-route";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context"; import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
import { useLayout } from "@/contexts/layout-context";
import { useTask } from "@/contexts/task-context"; import { useTask } from "@/contexts/task-context";
import { type File, useGetSearchQuery } from "../api/queries/useGetSearchQuery"; import { type File, useGetSearchQuery } from "../api/queries/useGetSearchQuery";
import "@/components/AgGrid/registerAgGridModules"; import "@/components/AgGrid/registerAgGridModules";
@ -47,9 +46,8 @@ function getSourceIcon(connectorType?: string) {
function SearchPage() { function SearchPage() {
const router = useRouter(); const router = useRouter();
const { isMenuOpen, files: taskFiles } = useTask(); const { files: taskFiles } = useTask();
const { totalTopOffset } = useLayout(); const { selectedFilter, setSelectedFilter, parsedFilterData } =
const { selectedFilter, setSelectedFilter, parsedFilterData, isPanelOpen } =
useKnowledgeFilter(); useKnowledgeFilter();
const [selectedRows, setSelectedRows] = useState<File[]>([]); const [selectedRows, setSelectedRows] = useState<File[]>([]);
const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false); const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);
@ -230,116 +228,83 @@ function SearchPage() {
}; };
return ( return (
<div <div className="h-full flex flex-col container">
className={`fixed inset-0 md:left-72 flex flex-col transition-all duration-300 ${ <div className="flex items-center justify-between mb-6">
isMenuOpen && isPanelOpen <h2 className="flex items-center h-[40px] text-lg font-semibold">
? "md:right-[704px]" Project Knowledge
: // Both open: 384px (menu) + 320px (KF panel) </h2>
isMenuOpen <KnowledgeDropdown variant="button" />
? "md:right-96" </div>
: // Only menu open: 384px
isPanelOpen
? "md:right-80"
: // Only KF panel open: 320px
"md:right-6" // Neither open: 24px
}`}
style={{ top: `${totalTopOffset}px` }}
>
<div className="flex-1 flex flex-col min-h-0 px-6 py-6">
<div className="flex items-center justify-between mb-6 h-10">
<h2 className="text-lg font-semibold">Project Knowledge</h2>
<KnowledgeDropdown variant="button" />
</div>
{/* Search Input Area */} {/* Search Input Area */}
<div className="flex-shrink-0 mb-6 xl:max-w-[75%]"> <div className="flex-shrink-0 mb-6 xl:max-w-[75%]">
<form className="flex gap-3"> <form className="flex gap-3">
<div className="primary-input min-h-10 !flex items-center flex-nowrap focus-within:border-foreground transition-colors !p-[0.3rem]"> <div className="primary-input min-h-10 !flex items-center flex-nowrap focus-within:border-foreground transition-colors !p-[0.3rem]">
{selectedFilter?.name && ( {selectedFilter?.name && (
<div <div
className={`flex items-center gap-1 h-full px-1.5 py-0.5 mr-1 rounded max-w-[25%] ${ className={`flex items-center gap-1 h-full px-1.5 py-0.5 mr-1 rounded max-w-[25%] ${
filterAccentClasses[parsedFilterData?.color || "zinc"] filterAccentClasses[parsedFilterData?.color || "zinc"]
}`} }`}
>
<span className="truncate">{selectedFilter?.name}</span>
<X
aria-label="Remove filter"
className="h-4 w-4 flex-shrink-0 cursor-pointer"
onClick={() => setSelectedFilter(null)}
/>
</div>
)}
<Search
className="h-4 w-4 ml-1 flex-shrink-0 text-placeholder-foreground"
strokeWidth={1.5}
/>
<input
className="bg-transparent w-full h-full ml-2 focus:outline-none focus-visible:outline-none font-mono placeholder:font-mono"
name="search-query"
id="search-query"
type="text"
placeholder="Search your documents..."
onChange={handleTableSearch}
/>
</div>
{/* <Button
type="submit"
variant="outline"
className="rounded-lg p-0 flex-shrink-0"
>
{isFetching ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Search className="h-4 w-4" />
)}
</Button> */}
{/* //TODO: Implement sync button */}
{/* <Button
type="button"
variant="outline"
className="rounded-lg flex-shrink-0"
onClick={() => alert("Not implemented")}
>
Sync
</Button> */}
{selectedRows.length > 0 && (
<Button
type="button"
variant="destructive"
className="rounded-lg flex-shrink-0"
onClick={() => setShowBulkDeleteDialog(true)}
> >
<Trash2 className="h-4 w-4" /> Delete <span className="truncate">{selectedFilter?.name}</span>
</Button> <X
)} aria-label="Remove filter"
</form> className="h-4 w-4 flex-shrink-0 cursor-pointer"
</div> onClick={() => setSelectedFilter(null)}
<div className="flex-1 min-h-0 max-h-[700px] overflow-hidden h-full"> />
<AgGridReact
className="h-full w-full"
columnDefs={columnDefs}
defaultColDef={defaultColDef}
loading={isFetching}
ref={gridRef}
rowData={fileResults}
rowSelection="multiple"
rowMultiSelectWithClick={false}
suppressRowClickSelection={true}
getRowId={params => params.data.filename}
domLayout="normal"
onSelectionChanged={onSelectionChanged}
noRowsOverlayComponent={() => (
<div className="text-center pb-[45px]">
<div className="text-lg text-primary font-semibold">
No knowledge
</div>
<div className="text-sm mt-1 text-muted-foreground">
Add files from local or your preferred cloud.
</div>
</div> </div>
)} )}
/> <Search
</div> className="h-4 w-4 ml-1 flex-shrink-0 text-placeholder-foreground"
strokeWidth={1.5}
/>
<input
className="bg-transparent w-full h-full ml-2 focus:outline-none focus-visible:outline-none font-mono placeholder:font-mono"
name="search-query"
id="search-query"
type="text"
placeholder="Search your documents..."
onChange={handleTableSearch}
/>
</div>
{selectedRows.length > 0 && (
<Button
type="button"
variant="destructive"
className="rounded-lg flex-shrink-0"
onClick={() => setShowBulkDeleteDialog(true)}
>
<Trash2 className="h-4 w-4" /> Delete
</Button>
)}
</form>
</div>
<div className="flex-1 min-h-0">
<AgGridReact
className="h-full w-full"
columnDefs={columnDefs}
defaultColDef={defaultColDef}
loading={isFetching}
ref={gridRef}
rowData={fileResults}
rowSelection="multiple"
rowMultiSelectWithClick={false}
suppressRowClickSelection={true}
getRowId={params => params.data.filename}
domLayout="normal"
onSelectionChanged={onSelectionChanged}
noRowsOverlayComponent={() => (
<div className="text-center pb-[45px]">
<div className="text-lg text-primary font-semibold">
No knowledge
</div>
<div className="text-sm mt-1 text-muted-foreground">
Add files from local or your preferred cloud.
</div>
</div>
)}
/>
</div> </div>
{/* Bulk Delete Confirmation Dialog */} {/* Bulk Delete Confirmation Dialog */}

File diff suppressed because it is too large Load diff

View file

@ -172,18 +172,16 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
headerHeight={headerHeight} headerHeight={headerHeight}
totalTopOffset={totalTopOffset} totalTopOffset={totalTopOffset}
> >
<div <div className={cn("py-6 lg:py-8 px-4 lg:px-6 h-full")}>
className={cn(
"py-6 lg:py-8 px-4 lg:px-6",
isSmallWidthPath ? "max-w-[850px]" : "container"
)}
>
{children} {children}
</div> </div>
</LayoutProvider> </LayoutProvider>
</main> </main>
<TaskNotificationMenu /> <TaskNotificationMenu />
<KnowledgeFilterPanel /> <KnowledgeFilterPanel
totalTopOffset={totalTopOffset}
headerHeight={headerHeight}
/>
</div> </div>
); );
} }