Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions surfsense_backend/app/routes/documents_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ async def read_documents(
Permission.DOCUMENTS_READ.value,
"You don't have permission to read documents in this search space",
)
query = select(Document).filter(Document.search_space_id == search_space_id)
query = (
select(Document)
.options(selectinload(Document.created_by))
.filter(Document.search_space_id == search_space_id)
)
count_query = (
select(func.count())
.select_from(Document)
Expand All @@ -221,6 +225,7 @@ async def read_documents(
# Get documents from all search spaces user has membership in
query = (
select(Document)
.options(selectinload(Document.created_by))
.join(SearchSpace)
.join(SearchSpaceMembership)
.filter(SearchSpaceMembership.user_id == user.id)
Expand Down Expand Up @@ -261,6 +266,11 @@ async def read_documents(
# Convert database objects to API-friendly format
api_documents = []
for doc in db_documents:
# Get user name (display_name or email fallback)
created_by_name = None
if doc.created_by:
created_by_name = doc.created_by.display_name or doc.created_by.email

api_documents.append(
DocumentRead(
id=doc.id,
Expand All @@ -273,6 +283,8 @@ async def read_documents(
created_at=doc.created_at,
updated_at=doc.updated_at,
search_space_id=doc.search_space_id,
created_by_id=doc.created_by_id,
created_by_name=created_by_name,
)
)

Expand Down Expand Up @@ -341,7 +353,11 @@ async def search_documents(
Permission.DOCUMENTS_READ.value,
"You don't have permission to read documents in this search space",
)
query = select(Document).filter(Document.search_space_id == search_space_id)
query = (
select(Document)
.options(selectinload(Document.created_by))
.filter(Document.search_space_id == search_space_id)
)
count_query = (
select(func.count())
.select_from(Document)
Expand All @@ -351,6 +367,7 @@ async def search_documents(
# Get documents from all search spaces user has membership in
query = (
select(Document)
.options(selectinload(Document.created_by))
.join(SearchSpace)
.join(SearchSpaceMembership)
.filter(SearchSpaceMembership.user_id == user.id)
Expand Down Expand Up @@ -395,6 +412,11 @@ async def search_documents(
# Convert database objects to API-friendly format
api_documents = []
for doc in db_documents:
# Get user name (display_name or email fallback)
created_by_name = None
if doc.created_by:
created_by_name = doc.created_by.display_name or doc.created_by.email

api_documents.append(
DocumentRead(
id=doc.id,
Expand All @@ -407,6 +429,8 @@ async def search_documents(
created_at=doc.created_at,
updated_at=doc.updated_at,
search_space_id=doc.search_space_id,
created_by_id=doc.created_by_id,
created_by_name=created_by_name,
)
)

Expand Down
1 change: 1 addition & 0 deletions surfsense_backend/app/schemas/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class DocumentRead(BaseModel):
updated_at: datetime | None
search_space_id: int
created_by_id: UUID | None = None # User who created/uploaded this document
created_by_name: str | None = None # Display name or email of the user who created this document

model_config = ConfigDict(from_attributes=True)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
llmPreferencesAtom,
} from "@/atoms/new-llm-config/new-llm-config-query.atoms";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { ConnectorIndicator } from "@/components/assistant-ui/connector-popup";
import { DocumentUploadDialogProvider } from "@/components/assistant-ui/document-upload-popup";
import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb";
import { LayoutDataProvider } from "@/components/layout";
Expand Down Expand Up @@ -192,6 +193,8 @@ export function DashboardClientLayout({
<LayoutDataProvider searchSpaceId={searchSpaceId} breadcrumb={<DashboardBreadcrumb />}>
{children}
</LayoutDataProvider>
{/* Global connector dialog - triggered from documents page */}
<ConnectorIndicator hideTrigger />
</DocumentUploadDialogProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import type React from "react";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export function getDocumentTypeIcon(type: string): React.ReactNode {
return getConnectorIcon(type);
export function getDocumentTypeIcon(type: string, className?: string): React.ReactNode {
return getConnectorIcon(type, className);
}

export function getDocumentTypeLabel(type: string): string {
Expand All @@ -14,17 +15,35 @@ export function getDocumentTypeLabel(type: string): string {
.join(" ");
}

const MAX_LABEL_LENGTH = 28;

export function DocumentTypeChip({ type, className }: { type: string; className?: string }) {
const icon = getDocumentTypeIcon(type);
return (
const icon = getDocumentTypeIcon(type, "h-4 w-4");
const fullLabel = getDocumentTypeLabel(type);
const truncatedLabel = fullLabel.length > MAX_LABEL_LENGTH
? `${fullLabel.slice(0, MAX_LABEL_LENGTH)}...`
: fullLabel;
const needsTruncation = fullLabel.length > MAX_LABEL_LENGTH;

const chip = (
<span
className={
"inline-flex items-center gap-1.5 rounded-full border border-border bg-primary/5 px-2 py-1 text-xs font-medium " +
(className ?? "")
}
className={`inline-flex items-center gap-1.5 rounded bg-muted/40 px-2 py-1 text-xs text-muted-foreground ${className ?? ""}`}
>
<span className="text-primary">{icon}</span>
{getDocumentTypeLabel(type)}
<span className="opacity-80 flex-shrink-0">{icon}</span>
<span className="truncate">{truncatedLabel}</span>
</span>
);

if (needsTruncation) {
return (
<Tooltip>
<TooltipTrigger asChild>{chip}</TooltipTrigger>
<TooltipContent side="top" className="max-w-xs">
<p>{fullLabel}</p>
</TooltipContent>
</Tooltip>
);
}

return chip;
}
Loading
Loading