chore: ran backend and frontend linting

This commit is contained in:
Anish Sarkar 2026-01-22 16:07:06 +05:30
parent 3d4a8f981c
commit be7ba76417
11 changed files with 119 additions and 127 deletions

View file

@ -6,7 +6,6 @@ For older items (beyond the sync window), use the list endpoint.
"""
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel
@ -24,14 +23,14 @@ class NotificationResponse(BaseModel):
id: int
user_id: str
search_space_id: Optional[int]
search_space_id: int | None
type: str
title: str
message: str
read: bool
metadata: dict
created_at: str
updated_at: Optional[str]
updated_at: str | None
class Config:
from_attributes = True
@ -43,7 +42,7 @@ class NotificationListResponse(BaseModel):
items: list[NotificationResponse]
total: int
has_more: bool
next_offset: Optional[int]
next_offset: int | None
class MarkReadResponse(BaseModel):
@ -63,9 +62,15 @@ class MarkAllReadResponse(BaseModel):
@router.get("", response_model=NotificationListResponse)
async def list_notifications(
search_space_id: Optional[int] = Query(None, description="Filter by search space ID"),
type_filter: Optional[str] = Query(None, alias="type", description="Filter by notification type"),
before_date: Optional[str] = Query(None, description="Get notifications before this ISO date (for pagination)"),
search_space_id: int | None = Query(
None, description="Filter by search space ID"
),
type_filter: str | None = Query(
None, alias="type", description="Filter by notification type"
),
before_date: str | None = Query(
None, description="Get notifications before this ISO date (for pagination)"
),
limit: int = Query(50, ge=1, le=100, description="Number of items to return"),
offset: int = Query(0, ge=0, description="Number of items to skip"),
user: User = Depends(current_active_user),
@ -73,32 +78,34 @@ async def list_notifications(
) -> NotificationListResponse:
"""
List notifications for the current user with pagination.
This endpoint is used as a fallback for older notifications that are
outside the Electric SQL sync window (2 weeks).
Use `before_date` to paginate through older notifications efficiently.
"""
# Build base query
query = select(Notification).where(Notification.user_id == user.id)
count_query = select(func.count(Notification.id)).where(Notification.user_id == user.id)
count_query = select(func.count(Notification.id)).where(
Notification.user_id == user.id
)
# Filter by search space (include null search_space_id for global notifications)
if search_space_id is not None:
query = query.where(
(Notification.search_space_id == search_space_id) |
(Notification.search_space_id.is_(None))
(Notification.search_space_id == search_space_id)
| (Notification.search_space_id.is_(None))
)
count_query = count_query.where(
(Notification.search_space_id == search_space_id) |
(Notification.search_space_id.is_(None))
(Notification.search_space_id == search_space_id)
| (Notification.search_space_id.is_(None))
)
# Filter by type
if type_filter:
query = query.where(Notification.type == type_filter)
count_query = count_query.where(Notification.type == type_filter)
# Filter by date (for efficient pagination of older items)
if before_date:
try:
@ -110,39 +117,47 @@ async def list_notifications(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid date format. Use ISO format (e.g., 2024-01-15T00:00:00Z)",
) from None
# Get total count
total_result = await session.execute(count_query)
total = total_result.scalar() or 0
# Apply ordering and pagination
query = query.order_by(desc(Notification.created_at)).offset(offset).limit(limit + 1)
query = (
query.order_by(desc(Notification.created_at)).offset(offset).limit(limit + 1)
)
# Execute query
result = await session.execute(query)
notifications = result.scalars().all()
# Check if there are more items
has_more = len(notifications) > limit
if has_more:
notifications = notifications[:limit]
# Convert to response format
items = []
for notification in notifications:
items.append(NotificationResponse(
id=notification.id,
user_id=str(notification.user_id),
search_space_id=notification.search_space_id,
type=notification.type,
title=notification.title,
message=notification.message,
read=notification.read,
metadata=notification.notification_metadata or {},
created_at=notification.created_at.isoformat() if notification.created_at else "",
updated_at=notification.updated_at.isoformat() if notification.updated_at else None,
))
items.append(
NotificationResponse(
id=notification.id,
user_id=str(notification.user_id),
search_space_id=notification.search_space_id,
type=notification.type,
title=notification.title,
message=notification.message,
read=notification.read,
metadata=notification.notification_metadata or {},
created_at=notification.created_at.isoformat()
if notification.created_at
else "",
updated_at=notification.updated_at.isoformat()
if notification.updated_at
else None,
)
)
return NotificationListResponse(
items=items,
total=total,

View file

@ -711,12 +711,9 @@ function MembersTab({
</SelectContent>
</Select>
) : (
<Badge
variant="secondary"
className="text-[10px] md:text-xs py-0 md:py-0.5"
>
{member.role?.name || "No role"}
</Badge>
<Badge variant="secondary" className="text-[10px] md:text-xs py-0 md:py-0.5">
{member.role?.name || "No role"}
</Badge>
)}
</TableCell>
<TableCell className="hidden md:table-cell">

View file

@ -1,7 +1,13 @@
"use client";
import { MessageSquare } from "lucide-react";
import { Drawer, DrawerContent, DrawerHandle, DrawerHeader, DrawerTitle } from "@/components/ui/drawer";
import {
Drawer,
DrawerContent,
DrawerHandle,
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer";
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
import { CommentPanelContainer } from "../comment-panel-container/comment-panel-container";

View file

@ -87,20 +87,16 @@ export function LayoutDataProvider({
// Inbox hook
const userId = user?.id ? String(user.id) : null;
const {
inboxItems,
unreadCount,
loading: inboxLoading,
const {
inboxItems,
unreadCount,
loading: inboxLoading,
loadingMore: inboxLoadingMore,
hasMore: inboxHasMore,
loadMore: inboxLoadMore,
markAsRead,
markAllAsRead
} = useInbox(
userId,
Number(searchSpaceId) || null,
null
);
markAsRead,
markAllAsRead,
} = useInbox(userId, Number(searchSpaceId) || null, null);
// Delete dialogs state
const [showDeleteChatDialog, setShowDeleteChatDialog] = useState(false);

View file

@ -280,7 +280,9 @@ export function AllPrivateChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<MessageCircleMore className="h-4 w-4" />
<span>Active</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{activeCount}</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
{activeCount}
</span>
</span>
</TabsTrigger>
<TabsTrigger
@ -290,7 +292,9 @@ export function AllPrivateChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<ArchiveIcon className="h-4 w-4" />
<span>Archived</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{archivedCount}</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
{archivedCount}
</span>
</span>
</TabsTrigger>
</TabsList>

View file

@ -280,7 +280,9 @@ export function AllSharedChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<MessageCircleMore className="h-4 w-4" />
<span>Active</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{activeCount}</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
{activeCount}
</span>
</span>
</TabsTrigger>
<TabsTrigger
@ -290,7 +292,9 @@ export function AllSharedChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<ArchiveIcon className="h-4 w-4" />
<span>Archived</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{archivedCount}</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
{archivedCount}
</span>
</span>
</TabsTrigger>
</TabsList>

View file

@ -551,7 +551,9 @@ export function LLMConfigForm({
render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-3 bg-muted/30">
<div className="space-y-0.5">
<FormLabel className="text-xs sm:text-sm font-medium">Enable Citations</FormLabel>
<FormLabel className="text-xs sm:text-sm font-medium">
Enable Citations
</FormLabel>
<FormDescription className="text-[10px] sm:text-xs">
Include [citation:id] references to source documents
</FormDescription>

View file

@ -9,12 +9,7 @@ function Drawer({
shouldScaleBackground = true,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
return (
<DrawerPrimitive.Root
shouldScaleBackground={shouldScaleBackground}
{...props}
/>
);
return <DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />;
}
Drawer.displayName = "Drawer";
@ -62,42 +57,20 @@ function DrawerContent({
}
DrawerContent.displayName = "DrawerContent";
function DrawerHeader({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
{...props}
/>
);
function DrawerHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)} {...props} />;
}
DrawerHeader.displayName = "DrawerHeader";
function DrawerFooter({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
);
function DrawerFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />;
}
DrawerFooter.displayName = "DrawerFooter";
function DrawerTitle({
className,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
return (
<DrawerPrimitive.Title
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
);
@ -119,7 +92,10 @@ DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
function DrawerHandle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div className={cn("mx-auto mt-4 h-1.5 w-12 rounded-full bg-muted-foreground/40", className)} {...props} />
<div
className={cn("mx-auto mt-4 h-1.5 w-12 rounded-full bg-muted-foreground/40", className)}
{...props}
/>
);
}
DrawerHandle.displayName = "DrawerHandle";
@ -137,4 +113,3 @@ export {
DrawerDescription,
DrawerHandle,
};

View file

@ -1,34 +1,33 @@
import { cn } from "@/lib/utils";
interface SpinnerProps {
/** Size of the spinner */
size?: "xs" | "sm" | "md" | "lg" | "xl";
/** Whether to hide the track behind the spinner arc */
hideTrack?: boolean;
/** Additional classes to apply */
className?: string;
/** Size of the spinner */
size?: "xs" | "sm" | "md" | "lg" | "xl";
/** Whether to hide the track behind the spinner arc */
hideTrack?: boolean;
/** Additional classes to apply */
className?: string;
}
const sizeClasses = {
xs: "h-3 w-3 border-[1.5px]",
sm: "h-4 w-4 border-2",
md: "h-6 w-6 border-2",
lg: "h-8 w-8 border-[3px]",
xl: "h-10 w-10 border-4",
xs: "h-3 w-3 border-[1.5px]",
sm: "h-4 w-4 border-2",
md: "h-6 w-6 border-2",
lg: "h-8 w-8 border-[3px]",
xl: "h-10 w-10 border-4",
};
export function Spinner({ size = "md", hideTrack = false, className }: SpinnerProps) {
return (
<output
aria-label="Loading"
className={cn(
"block animate-spin rounded-full",
hideTrack ? "border-transparent" : "border-current/20",
"border-t-current",
sizeClasses[size],
className
)}
/>
);
return (
<output
aria-label="Loading"
className={cn(
"block animate-spin rounded-full",
hideTrack ? "border-transparent" : "border-current/20",
"border-t-current",
sizeClasses[size],
className
)}
/>
);
}

View file

@ -146,4 +146,3 @@ export type InboxItem = z.infer<typeof inboxItem>;
export type ConnectorIndexingInboxItem = z.infer<typeof connectorIndexingInboxItem>;
export type DocumentProcessingInboxItem = z.infer<typeof documentProcessingInboxItem>;
export type NewMentionInboxItem = z.infer<typeof newMentionInboxItem>;

View file

@ -190,9 +190,7 @@ export function useInbox(
ORDER BY created_at DESC
LIMIT ${PAGE_SIZE}`;
const params = typeFilter
? [userId, searchSpaceId, typeFilter]
: [userId, searchSpaceId];
const params = typeFilter ? [userId, searchSpaceId, typeFilter] : [userId, searchSpaceId];
const db = client.db as any;
@ -310,10 +308,7 @@ export function useInbox(
AND read = false
AND created_at > '${cutoff}'`;
const result = await client.db.query<{ count: number }>(query, [
userId,
searchSpaceId,
]);
const result = await client.db.query<{ count: number }>(query, [userId, searchSpaceId]);
if (mounted && result.rows?.[0]) {
setTotalUnreadCount(Number(result.rows[0].count) || 0);
}