fixed spacing and flex
This commit is contained in:
parent
1d4a91eb7b
commit
1dc0a370f1
6 changed files with 641 additions and 782 deletions
|
|
@ -25,6 +25,7 @@ import {
|
|||
FilterIconPopover,
|
||||
IconKey,
|
||||
} from "@/components/filter-icon-popover";
|
||||
import { useLayout } from "@/contexts/layout-context";
|
||||
|
||||
interface FacetBucket {
|
||||
key: string;
|
||||
|
|
@ -48,7 +49,15 @@ export const filterAccentClasses: Record<FilterColor, string> = {
|
|||
red: "bg-accent-red text-accent-red-foreground",
|
||||
};
|
||||
|
||||
export function KnowledgeFilterPanel() {
|
||||
interface KnowledgeFilterPanelProps {
|
||||
totalTopOffset: number;
|
||||
headerHeight: number;
|
||||
}
|
||||
|
||||
export function KnowledgeFilterPanel({
|
||||
totalTopOffset,
|
||||
headerHeight,
|
||||
}: KnowledgeFilterPanelProps) {
|
||||
const {
|
||||
selectedFilter,
|
||||
parsedFilterData,
|
||||
|
|
@ -135,7 +144,7 @@ export function KnowledgeFilterPanel() {
|
|||
// Load available facets using search aggregations hook
|
||||
const { data: aggregations } = useGetSearchAggregations("*", 1, 0, {
|
||||
enabled: isPanelOpen,
|
||||
placeholderData: (prev) => prev,
|
||||
placeholderData: prev => prev,
|
||||
staleTime: 60_000,
|
||||
gcTime: 5 * 60_000,
|
||||
});
|
||||
|
|
@ -213,7 +222,7 @@ export function KnowledgeFilterPanel() {
|
|||
facetType: keyof typeof selectedFilters,
|
||||
newValues: string[]
|
||||
) => {
|
||||
setSelectedFilters((prev) => ({
|
||||
setSelectedFilters(prev => ({
|
||||
...prev,
|
||||
[facetType]: newValues,
|
||||
}));
|
||||
|
|
@ -231,7 +240,10 @@ export function KnowledgeFilterPanel() {
|
|||
};
|
||||
|
||||
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">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
@ -270,7 +282,7 @@ export function KnowledgeFilterPanel() {
|
|||
<Input
|
||||
id="filter-name"
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
onChange={e => {
|
||||
const v = e.target.value;
|
||||
setName(v);
|
||||
if (nameError && v.trim()) {
|
||||
|
|
@ -301,7 +313,7 @@ export function KnowledgeFilterPanel() {
|
|||
<Textarea
|
||||
id="filter-description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
placeholder="Provide a brief description of your knowledge filter..."
|
||||
rows={3}
|
||||
/>
|
||||
|
|
@ -318,7 +330,7 @@ export function KnowledgeFilterPanel() {
|
|||
placeholder="Enter your search query..."
|
||||
value={query}
|
||||
className="font-mono placeholder:font-mono"
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
rows={2}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -327,13 +339,13 @@ export function KnowledgeFilterPanel() {
|
|||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<MultiSelect
|
||||
options={(availableFacets.data_sources || []).map((bucket) => ({
|
||||
options={(availableFacets.data_sources || []).map(bucket => ({
|
||||
value: bucket.key,
|
||||
label: bucket.key,
|
||||
count: bucket.count,
|
||||
}))}
|
||||
value={selectedFilters.data_sources}
|
||||
onValueChange={(values) =>
|
||||
onValueChange={values =>
|
||||
handleFilterChange("data_sources", values)
|
||||
}
|
||||
placeholder="Select sources..."
|
||||
|
|
@ -343,15 +355,13 @@ export function KnowledgeFilterPanel() {
|
|||
|
||||
<div className="space-y-2">
|
||||
<MultiSelect
|
||||
options={(availableFacets.document_types || []).map(
|
||||
(bucket) => ({
|
||||
value: bucket.key,
|
||||
label: bucket.key,
|
||||
count: bucket.count,
|
||||
})
|
||||
)}
|
||||
options={(availableFacets.document_types || []).map(bucket => ({
|
||||
value: bucket.key,
|
||||
label: bucket.key,
|
||||
count: bucket.count,
|
||||
}))}
|
||||
value={selectedFilters.document_types}
|
||||
onValueChange={(values) =>
|
||||
onValueChange={values =>
|
||||
handleFilterChange("document_types", values)
|
||||
}
|
||||
placeholder="Select types..."
|
||||
|
|
@ -361,13 +371,13 @@ export function KnowledgeFilterPanel() {
|
|||
|
||||
<div className="space-y-2">
|
||||
<MultiSelect
|
||||
options={(availableFacets.owners || []).map((bucket) => ({
|
||||
options={(availableFacets.owners || []).map(bucket => ({
|
||||
value: bucket.key,
|
||||
label: bucket.key,
|
||||
count: bucket.count,
|
||||
}))}
|
||||
value={selectedFilters.owners}
|
||||
onValueChange={(values) => handleFilterChange("owners", values)}
|
||||
onValueChange={values => handleFilterChange("owners", values)}
|
||||
placeholder="Select owners..."
|
||||
allOptionLabel="All Owners"
|
||||
/>
|
||||
|
|
@ -376,14 +386,14 @@ export function KnowledgeFilterPanel() {
|
|||
<div className="space-y-2">
|
||||
<MultiSelect
|
||||
options={(availableFacets.connector_types || []).map(
|
||||
(bucket) => ({
|
||||
bucket => ({
|
||||
value: bucket.key,
|
||||
label: bucket.key,
|
||||
count: bucket.count,
|
||||
})
|
||||
)}
|
||||
value={selectedFilters.connector_types}
|
||||
onValueChange={(values) =>
|
||||
onValueChange={values =>
|
||||
handleFilterChange("connector_types", values)
|
||||
}
|
||||
placeholder="Select connectors..."
|
||||
|
|
@ -403,7 +413,7 @@ export function KnowledgeFilterPanel() {
|
|||
min="1"
|
||||
max="1000"
|
||||
value={resultLimit}
|
||||
onChange={(e) => {
|
||||
onChange={e => {
|
||||
const newLimit = Math.max(
|
||||
1,
|
||||
Math.min(1000, parseInt(e.target.value) || 1)
|
||||
|
|
@ -416,7 +426,7 @@ export function KnowledgeFilterPanel() {
|
|||
</div>
|
||||
<Slider
|
||||
value={[resultLimit]}
|
||||
onValueChange={(values) => setResultLimit(values[0])}
|
||||
onValueChange={values => setResultLimit(values[0])}
|
||||
max={1000}
|
||||
min={1}
|
||||
step={1}
|
||||
|
|
@ -436,7 +446,7 @@ export function KnowledgeFilterPanel() {
|
|||
max="5"
|
||||
step="0.1"
|
||||
value={scoreThreshold}
|
||||
onChange={(e) =>
|
||||
onChange={e =>
|
||||
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"
|
||||
|
|
@ -445,7 +455,7 @@ export function KnowledgeFilterPanel() {
|
|||
</div>
|
||||
<Slider
|
||||
value={[scoreThreshold]}
|
||||
onValueChange={(values) => setScoreThreshold(values[0])}
|
||||
onValueChange={values => setScoreThreshold(values[0])}
|
||||
max={5}
|
||||
min={0}
|
||||
step={0.1}
|
||||
|
|
|
|||
|
|
@ -2047,21 +2047,10 @@ function ChatPage() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
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` }}
|
||||
>
|
||||
<div className="h-full flex flex-col container">
|
||||
{/* Debug header - only show in debug mode */}
|
||||
{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-4">
|
||||
{/* Async Mode Toggle */}
|
||||
|
|
|
|||
|
|
@ -133,22 +133,8 @@ function ChunksPageContent() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`fixed inset-0 md:left-72 flex flex-row 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` }}
|
||||
>
|
||||
<div className="flex-1 flex flex-col min-h-0 px-6 py-6">
|
||||
<div className="flex flex-row h-full">
|
||||
<div className="flex-1 flex flex-col min-h-0">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col mb-6">
|
||||
<div className="flex flex-row items-center gap-3 mb-6">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { KnowledgeDropdown } from "@/components/knowledge-dropdown";
|
|||
import { ProtectedRoute } from "@/components/protected-route";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
||||
import { useLayout } from "@/contexts/layout-context";
|
||||
import { useTask } from "@/contexts/task-context";
|
||||
import { type File, useGetSearchQuery } from "../api/queries/useGetSearchQuery";
|
||||
import "@/components/AgGrid/registerAgGridModules";
|
||||
|
|
@ -47,9 +46,8 @@ function getSourceIcon(connectorType?: string) {
|
|||
|
||||
function SearchPage() {
|
||||
const router = useRouter();
|
||||
const { isMenuOpen, files: taskFiles } = useTask();
|
||||
const { totalTopOffset } = useLayout();
|
||||
const { selectedFilter, setSelectedFilter, parsedFilterData, isPanelOpen } =
|
||||
const { files: taskFiles } = useTask();
|
||||
const { selectedFilter, setSelectedFilter, parsedFilterData } =
|
||||
useKnowledgeFilter();
|
||||
const [selectedRows, setSelectedRows] = useState<File[]>([]);
|
||||
const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);
|
||||
|
|
@ -230,116 +228,83 @@ function SearchPage() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
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` }}
|
||||
>
|
||||
<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>
|
||||
<div className="h-full flex flex-col container">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="flex items-center h-[40px] text-lg font-semibold">
|
||||
Project Knowledge
|
||||
</h2>
|
||||
<KnowledgeDropdown variant="button" />
|
||||
</div>
|
||||
|
||||
{/* Search Input Area */}
|
||||
<div className="flex-shrink-0 mb-6 xl:max-w-[75%]">
|
||||
<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]">
|
||||
{selectedFilter?.name && (
|
||||
<div
|
||||
className={`flex items-center gap-1 h-full px-1.5 py-0.5 mr-1 rounded max-w-[25%] ${
|
||||
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)}
|
||||
{/* Search Input Area */}
|
||||
<div className="flex-shrink-0 mb-6 xl:max-w-[75%]">
|
||||
<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]">
|
||||
{selectedFilter?.name && (
|
||||
<div
|
||||
className={`flex items-center gap-1 h-full px-1.5 py-0.5 mr-1 rounded max-w-[25%] ${
|
||||
filterAccentClasses[parsedFilterData?.color || "zinc"]
|
||||
}`}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" /> Delete
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
)}
|
||||
/>
|
||||
</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>
|
||||
{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>
|
||||
|
||||
{/* Bulk Delete Confirmation Dialog */}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -172,18 +172,16 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
|
|||
headerHeight={headerHeight}
|
||||
totalTopOffset={totalTopOffset}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"py-6 lg:py-8 px-4 lg:px-6",
|
||||
isSmallWidthPath ? "max-w-[850px]" : "container"
|
||||
)}
|
||||
>
|
||||
<div className={cn("py-6 lg:py-8 px-4 lg:px-6 h-full")}>
|
||||
{children}
|
||||
</div>
|
||||
</LayoutProvider>
|
||||
</main>
|
||||
<TaskNotificationMenu />
|
||||
<KnowledgeFilterPanel />
|
||||
<KnowledgeFilterPanel
|
||||
totalTopOffset={totalTopOffset}
|
||||
headerHeight={headerHeight}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue