mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
chore: ran backend and frontend linting
This commit is contained in:
parent
3d4a8f981c
commit
be7ba76417
11 changed files with 119 additions and 127 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue