mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
chore: frontend linting
This commit is contained in:
parent
f67ff41790
commit
80f83e32c6
14 changed files with 106 additions and 100 deletions
|
|
@ -196,10 +196,7 @@ export function DashboardClientLayout({
|
|||
return (
|
||||
<DocumentUploadDialogProvider>
|
||||
<OnboardingTour />
|
||||
<LayoutDataProvider
|
||||
searchSpaceId={searchSpaceId}
|
||||
breadcrumb={<DashboardBreadcrumb />}
|
||||
>
|
||||
<LayoutDataProvider searchSpaceId={searchSpaceId} breadcrumb={<DashboardBreadcrumb />}>
|
||||
{children}
|
||||
</LayoutDataProvider>
|
||||
</DocumentUploadDialogProvider>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -300,7 +300,6 @@ export function LayoutDataProvider({
|
|||
}
|
||||
}, [router]);
|
||||
|
||||
|
||||
const handleViewAllSharedChats = useCallback(() => {
|
||||
setIsAllSharedChatsSidebarOpen(true);
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue