feat: add created_by_email field to document schema and update related components for improved user information display

This commit is contained in:
Anish Sarkar 2026-02-21 23:41:00 +05:30
parent 12b119be59
commit f3652ad7cf
7 changed files with 65 additions and 18 deletions

View file

@ -373,10 +373,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
created_by_email = None
if doc.created_by:
created_by_name = doc.created_by.display_name or doc.created_by.email
created_by_name = doc.created_by.display_name
created_by_email = doc.created_by.email
# Parse status from JSONB
status_data = None
@ -400,6 +401,7 @@ async def read_documents(
search_space_id=doc.search_space_id,
created_by_id=doc.created_by_id,
created_by_name=created_by_name,
created_by_email=created_by_email,
status=status_data,
)
)
@ -528,10 +530,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
created_by_email = None
if doc.created_by:
created_by_name = doc.created_by.display_name or doc.created_by.email
created_by_name = doc.created_by.display_name
created_by_email = doc.created_by.email
# Parse status from JSONB
status_data = None
@ -555,6 +558,7 @@ async def search_documents(
search_space_id=doc.search_space_id,
created_by_id=doc.created_by_id,
created_by_name=created_by_name,
created_by_email=created_by_email,
status=status_data,
)
)

View file

@ -60,9 +60,8 @@ 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
)
created_by_name: str | None = None
created_by_email: str | None = None
status: DocumentStatusSchema | None = (
None # Processing status (ready, processing, failed)
)

View file

@ -453,7 +453,7 @@ export function DocumentsTableShell({
) : error ? (
<div className="flex h-[50vh] w-full items-center justify-center">
<div className="flex flex-col items-center gap-3">
<AlertCircle className="h-8 w-8 text-destructive/60" />
<AlertCircle className="h-8 w-8 text-destructive" />
<p className="text-sm text-destructive">{t("error_loading")}</p>
</div>
</div>
@ -627,11 +627,30 @@ export function DocumentsTableShell({
<DocumentTypeChip type={doc.document_type} />
</TableCell>
)}
{columnVisibility.created_by && (
<TableCell className="w-36 py-2.5 text-sm text-foreground truncate border-r border-border/40">
{doc.created_by_name || "—"}
</TableCell>
)}
{columnVisibility.created_by && (
<TableCell className="w-36 py-2.5 text-sm text-foreground truncate border-r border-border/40">
{doc.created_by_name ? (
doc.created_by_email ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="cursor-default truncate block">
{doc.created_by_name}
</span>
</TooltipTrigger>
<TooltipContent side="top" align="start">
{doc.created_by_email}
</TooltipContent>
</Tooltip>
) : (
<span className="truncate block">{doc.created_by_name}</span>
)
) : (
<span className="truncate block">
{doc.created_by_email || "—"}
</span>
)}
</TableCell>
)}
{columnVisibility.created_at && (
<TableCell className="w-32 py-2.5 text-sm text-foreground border-r border-border/40">
<Tooltip>

View file

@ -16,6 +16,7 @@ export type Document = {
search_space_id: number;
created_by_id?: string | null;
created_by_name?: string | null;
created_by_email?: string | null;
status?: DocumentStatus;
};

View file

@ -115,6 +115,7 @@ export default function DocumentsTable() {
title: item.title,
created_by_id: item.created_by_id ?? null,
created_by_name: item.created_by_name ?? null,
created_by_email: item.created_by_email ?? null,
created_at: item.created_at,
status: (
item as {

View file

@ -44,6 +44,7 @@ export const document = z.object({
search_space_id: z.number(),
created_by_id: z.string().nullable().optional(),
created_by_name: z.string().nullable().optional(),
created_by_email: z.string().nullable().optional(),
});
export const extensionDocumentContent = z.object({

View file

@ -26,7 +26,7 @@ interface DocumentElectric {
status: DocumentStatusType | null;
}
// Document for display (with resolved user name)
// Document for display (with resolved user name and email)
export interface DocumentDisplay {
id: number;
search_space_id: number;
@ -34,6 +34,7 @@ export interface DocumentDisplay {
title: string;
created_by_id: string | null;
created_by_name: string | null;
created_by_email: string | null;
created_at: string;
status: DocumentStatusType;
}
@ -94,8 +95,9 @@ export function useDocuments(
// Track if initial API load is complete (source of truth)
const apiLoadedRef = useRef(false);
// User cache: userId → displayName
// User cache: userId → displayName / email
const userCacheRef = useRef<Map<string, string>>(new Map());
const emailCacheRef = useRef<Map<string, string>>(new Map());
// Electric sync refs
const syncHandleRef = useRef<SyncHandle | null>(null);
@ -119,10 +121,21 @@ export function useDocuments(
// Populate user cache from API response
const populateUserCache = useCallback(
(items: Array<{ created_by_id?: string | null; created_by_name?: string | null }>) => {
(
items: Array<{
created_by_id?: string | null;
created_by_name?: string | null;
created_by_email?: string | null;
}>
) => {
for (const item of items) {
if (item.created_by_id && item.created_by_name) {
userCacheRef.current.set(item.created_by_id, item.created_by_name);
if (item.created_by_id) {
if (item.created_by_name) {
userCacheRef.current.set(item.created_by_id, item.created_by_name);
}
if (item.created_by_email) {
emailCacheRef.current.set(item.created_by_id, item.created_by_email);
}
}
}
},
@ -138,6 +151,7 @@ export function useDocuments(
title: string;
created_by_id?: string | null;
created_by_name?: string | null;
created_by_email?: string | null;
created_at: string;
status?: DocumentStatusType | null;
}): DocumentDisplay => ({
@ -147,6 +161,7 @@ export function useDocuments(
title: item.title,
created_by_id: item.created_by_id ?? null,
created_by_name: item.created_by_name ?? null,
created_by_email: item.created_by_email ?? null,
created_at: item.created_at,
status: item.status ?? { state: "ready" },
}),
@ -160,6 +175,9 @@ export function useDocuments(
created_by_name: doc.created_by_id
? (userCacheRef.current.get(doc.created_by_id) ?? null)
: null,
created_by_email: doc.created_by_id
? (emailCacheRef.current.get(doc.created_by_id) ?? null)
: null,
status: doc.status ?? { state: "ready" },
}),
[]
@ -351,6 +369,9 @@ export function useDocuments(
created_by_name: doc.created_by_id
? (userCacheRef.current.get(doc.created_by_id) ?? null)
: null,
created_by_email: doc.created_by_id
? (emailCacheRef.current.get(doc.created_by_id) ?? null)
: null,
}))
);
}
@ -455,6 +476,7 @@ export function useDocuments(
setAllDocuments([]);
apiLoadedRef.current = false;
userCacheRef.current.clear();
emailCacheRef.current.clear();
}
prevSearchSpaceIdRef.current = searchSpaceId;
}, [searchSpaceId]);