chore: frontend linting

This commit is contained in:
Anish Sarkar 2026-01-20 20:47:31 +05:30
parent f67ff41790
commit 80f83e32c6
14 changed files with 106 additions and 100 deletions

View file

@ -196,10 +196,7 @@ export function DashboardClientLayout({
return (
<DocumentUploadDialogProvider>
<OnboardingTour />
<LayoutDataProvider
searchSpaceId={searchSpaceId}
breadcrumb={<DashboardBreadcrumb />}
>
<LayoutDataProvider searchSpaceId={searchSpaceId} breadcrumb={<DashboardBreadcrumb />}>
{children}
</LayoutDataProvider>
</DocumentUploadDialogProvider>

View file

@ -1060,11 +1060,7 @@ export default function NewChatPage() {
<div className="flex flex-col h-[calc(100vh-64px)] overflow-hidden">
<Thread
messageThinkingSteps={messageThinkingSteps}
header={
<ChatHeader
searchSpaceId={searchSpaceId}
/>
}
header={<ChatHeader searchSpaceId={searchSpaceId} />}
/>
</div>
</AssistantRuntimeProvider>

View file

@ -778,8 +778,7 @@ function RolesTab({
role.name === "Owner" && "text-amber-600",
role.name === "Editor" && "text-blue-600",
role.name === "Viewer" && "text-gray-600",
!["Owner", "Editor", "Viewer"].includes(role.name) &&
"text-primary"
!["Owner", "Editor", "Viewer"].includes(role.name) && "text-primary"
)}
/>
</div>
@ -1488,7 +1487,8 @@ function CreateRoleDialog({
</div>
</div>
<p className="text-xs text-muted-foreground">
Use presets to quickly apply Editor (create/read/update) or Viewer (read-only) permissions
Use presets to quickly apply Editor (create/read/update) or Viewer (read-only)
permissions
</p>
<ScrollArea className="h-64 rounded-lg border p-4">
<div className="space-y-4">
@ -1500,9 +1500,7 @@ function CreateRoleDialog({
return (
<div key={category} className="space-y-2">
<label
className="flex items-center gap-2 cursor-pointer hover:bg-muted/50 p-1 rounded w-full text-left"
>
<label className="flex items-center gap-2 cursor-pointer hover:bg-muted/50 p-1 rounded w-full text-left">
<Checkbox
checked={allSelected}
onCheckedChange={() => toggleCategory(category)}

View file

@ -48,12 +48,9 @@ export const addingCommentToMessageIdAtom = atom(
);
// Setter atom for updating thread visibility
export const setThreadVisibilityAtom = atom(
null,
(get, set, newVisibility: ChatVisibility) => {
set(currentThreadAtom, { ...get(currentThreadAtom), visibility: newVisibility });
}
);
export const setThreadVisibilityAtom = atom(null, (get, set, newVisibility: ChatVisibility) => {
set(currentThreadAtom, { ...get(currentThreadAtom), visibility: newVisibility });
});
export const resetCurrentThreadAtom = atom(null, (_, set) => {
set(currentThreadAtom, initialState);

View file

@ -113,7 +113,8 @@ export function CommentItem({
members = [],
membersLoading = false,
}: CommentItemProps) {
const displayName = comment.author?.displayName || comment.author?.email.split("@")[0] || "Unknown";
const displayName =
comment.author?.displayName || comment.author?.email.split("@")[0] || "Unknown";
const email = comment.author?.email || "";
const handleEditSubmit = (content: string) => {

View file

@ -90,13 +90,18 @@ export function CommentPanel({
{!hasThreads && currentUser && (
<div className="flex items-center gap-3 px-4 pt-4 pb-1">
<Avatar className="size-10">
<AvatarImage src={currentUser.avatar_url ?? undefined} alt={currentUser.display_name ?? currentUser.email} />
<AvatarImage
src={currentUser.avatar_url ?? undefined}
alt={currentUser.display_name ?? currentUser.email}
/>
<AvatarFallback className="bg-primary/10 text-primary text-sm font-medium">
{getInitials(currentUser.display_name, currentUser.email)}
</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<span className="text-sm font-medium">{currentUser.display_name ?? currentUser.email}</span>
<span className="text-sm font-medium">
{currentUser.display_name ?? currentUser.email}
</span>
</div>
</div>
)}

View file

@ -300,7 +300,6 @@ export function LayoutDataProvider({
}
}, [router]);
const handleViewAllSharedChats = useCallback(() => {
setIsAllSharedChatsSidebarOpen(true);
}, []);

View file

@ -12,21 +12,18 @@ interface HeaderProps {
mobileMenuTrigger?: React.ReactNode;
}
export function Header({
breadcrumb,
mobileMenuTrigger,
}: HeaderProps) {
export function Header({ breadcrumb, mobileMenuTrigger }: HeaderProps) {
const pathname = usePathname();
// Check if we're on a chat page
const isChatPage = pathname?.includes("/new-chat") ?? false;
// Use Jotai atom for thread state (synced from chat page)
const currentThreadState = useAtomValue(currentThreadAtom);
// Show button only when we have a thread id (thread exists and is synced to Jotai)
const hasThread = isChatPage && currentThreadState.id !== null;
// Create minimal thread object for ChatShareButton (used for API calls)
const threadForButton: ThreadRecord | null =
hasThread && currentThreadState.id !== null
@ -62,10 +59,7 @@ export function Header({
<NotificationButton />
{/* Share button - only show on chat pages when thread exists */}
{hasThread && (
<ChatShareButton
thread={threadForButton}
onVisibilityChange={handleVisibilityChange}
/>
<ChatShareButton thread={threadForButton} onVisibilityChange={handleVisibilityChange} />
)}
</div>
</header>

View file

@ -169,9 +169,7 @@ export function LayoutShell({
/>
<main className="flex-1 flex flex-col min-w-0">
<Header
breadcrumb={breadcrumb}
/>
<Header breadcrumb={breadcrumb} />
<div className={cn("flex-1", isChatPage ? "overflow-hidden" : "overflow-auto")}>
{children}

View file

@ -331,27 +331,27 @@ export function SidebarUserProfile({
<Languages className="mr-2 h-4 w-4" />
{t("language")}
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent className="gap-1">
{LANGUAGES.map((language) => {
const isSelected = locale === language.code;
return (
<DropdownMenuItem
key={language.code}
onClick={() => handleLanguageChange(language.code)}
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent/50",
isSelected && "bg-accent/80"
)}
>
<span className="mr-2">{language.flag}</span>
<span className="flex-1">{language.name}</span>
</DropdownMenuItem>
);
})}
</DropdownMenuSubContent>
</DropdownMenuPortal>
<DropdownMenuPortal>
<DropdownMenuSubContent className="gap-1">
{LANGUAGES.map((language) => {
const isSelected = locale === language.code;
return (
<DropdownMenuItem
key={language.code}
onClick={() => handleLanguageChange(language.code)}
className={cn(
"mb-1 last:mb-0 transition-all",
"hover:bg-accent/50",
isSelected && "bg-accent/80"
)}
>
<span className="mr-2">{language.flag}</span>
<span className="flex-1">{language.name}</span>
</DropdownMenuItem>
);
})}
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSeparator />

View file

@ -174,10 +174,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
size="sm"
role="combobox"
aria-expanded={open}
className={cn(
"h-8 gap-2 px-3 text-sm border-border/60",
className
)}
className={cn("h-8 gap-2 px-3 text-sm border-border/60", className)}
>
{isLoading ? (
<>
@ -187,11 +184,10 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
) : currentConfig ? (
<>
{getProviderIcon(currentConfig.provider)}
<span className="max-w-[100px] md:max-w-[150px] truncate hidden md:inline">{currentConfig.name}</span>
<Badge
variant="secondary"
className="ml-1 text-[10px] px-1.5 py-0 h-4 bg-muted/80"
>
<span className="max-w-[100px] md:max-w-[150px] truncate hidden md:inline">
{currentConfig.name}
</span>
<Badge variant="secondary" className="ml-1 text-[10px] px-1.5 py-0 h-4 bg-muted/80">
{currentConfig.model_name.split("/").pop()?.slice(0, 10) ||
currentConfig.model_name.slice(0, 10)}
</Badge>
@ -202,10 +198,12 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
<span className="text-muted-foreground hidden md:inline">Select Model</span>
</>
)}
<ChevronDown className={cn(
"h-3.5 w-3.5 text-muted-foreground ml-1 shrink-0 transition-transform duration-200",
open && "rotate-180"
)} />
<ChevronDown
className={cn(
"h-3.5 w-3.5 text-muted-foreground ml-1 shrink-0 transition-transform duration-200",
open && "rotate-180"
)}
/>
</Button>
</PopoverTrigger>

View file

@ -28,7 +28,10 @@ export function NotificationButton() {
const stored = localStorage.getItem(NOTIFICATION_FILTER_STORAGE_KEY);
if (stored) {
const parsed = JSON.parse(stored);
if (parsed === null || ["new_mention", "connector_indexing", "document_processing"].includes(parsed)) {
if (
parsed === null ||
["new_mention", "connector_indexing", "document_processing"].includes(parsed)
) {
setActiveFilter(parsed);
}
}

View file

@ -1,7 +1,16 @@
"use client";
import { formatDistanceToNow } from "date-fns";
import { AlertCircle, AtSign, Bell, Cable, CheckCheck, CheckCircle2, FileText, Loader2 } from "lucide-react";
import {
AlertCircle,
AtSign,
Bell,
Cable,
CheckCheck,
CheckCircle2,
FileText,
Loader2,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { convertRenderedToDisplay } from "@/components/chat-comments/comment-item/comment-item";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
@ -152,28 +161,31 @@ export function NotificationPopup({
{/* Filter Pills */}
<div className="flex items-center gap-1.5 px-4 py-2 overflow-x-auto">
{(Object.entries(NOTIFICATION_FILTERS) as [NotificationTypeEnum, typeof NOTIFICATION_FILTERS[keyof typeof NOTIFICATION_FILTERS]][]).map(
([key, { label, icon: Icon }]) => {
const isActive = activeFilter === key;
return (
<button
key={key}
type="button"
onClick={() => onFilterChange(key)}
className={cn(
"inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-[11px] font-medium transition-colors whitespace-nowrap",
"border focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
isActive
? "bg-primary text-primary-foreground border-primary"
: "bg-transparent text-muted-foreground border-border hover:bg-accent hover:text-accent-foreground"
)}
>
<Icon className="h-3 w-3" />
{label}
</button>
);
}
)}
{(
Object.entries(NOTIFICATION_FILTERS) as [
NotificationTypeEnum,
(typeof NOTIFICATION_FILTERS)[keyof typeof NOTIFICATION_FILTERS],
][]
).map(([key, { label, icon: Icon }]) => {
const isActive = activeFilter === key;
return (
<button
key={key}
type="button"
onClick={() => onFilterChange(key)}
className={cn(
"inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-[11px] font-medium transition-colors whitespace-nowrap",
"border focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
isActive
? "bg-primary text-primary-foreground border-primary"
: "bg-transparent text-muted-foreground border-border hover:bg-accent hover:text-accent-foreground"
)}
>
<Icon className="h-3 w-3" />
{label}
</button>
);
})}
</div>
{/* Notifications List */}

View file

@ -132,7 +132,12 @@ export function useNotifications(
}
try {
console.log("[useNotifications] Updating query for searchSpace:", searchSpaceId, "typeFilter:", typeFilter);
console.log(
"[useNotifications] Updating query for searchSpace:",
searchSpaceId,
"typeFilter:",
typeFilter
);
// Build query with optional type filter
const baseQuery = `SELECT * FROM notifications
@ -221,7 +226,10 @@ export function useNotifications(
AND read = false`;
// Fetch initial count
const result = await electricClient.db.query<{ count: number }>(countQuery, [userId, searchSpaceId]);
const result = await electricClient.db.query<{ count: number }>(countQuery, [
userId,
searchSpaceId,
]);
if (mounted && result.rows?.[0]) {
setTotalUnreadCount(Number(result.rows[0].count) || 0);