{announcement.description}
-- You're all caught up! New announcements will appear here. -
-+ {item.icon} + {item.name} +
+ ); +} diff --git a/surfsense_web/app/layout.config.tsx b/surfsense_web/app/layout.config.tsx index b1b07fd02..214c5b940 100644 --- a/surfsense_web/app/layout.config.tsx +++ b/surfsense_web/app/layout.config.tsx @@ -1,7 +1,7 @@ import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; - export const baseOptions: BaseLayoutProps = { nav: { - title: "SurfSense Documentation", + title: "SurfSense Docs", }, + githubUrl: "https://github.com/MODSetter/SurfSense", }; diff --git a/surfsense_web/app/verify-token/route.ts b/surfsense_web/app/verify-token/route.ts new file mode 100644 index 000000000..1c11d6ce0 --- /dev/null +++ b/surfsense_web/app/verify-token/route.ts @@ -0,0 +1,25 @@ +import { NextRequest, NextResponse } from "next/server"; + +const backendBaseUrl = (process.env.INTERNAL_FASTAPI_BACKEND_URL || "http://backend:8000").replace( + /\/+$/, + "" +); + +export async function GET(request: NextRequest) { + const response = await fetch(`${backendBaseUrl}/verify-token`, { + method: "GET", + headers: { + Authorization: request.headers.get("authorization") || "", + "X-API-Key": request.headers.get("x-api-key") || "", + }, + cache: "no-store", + }); + + return new NextResponse(response.body, { + status: response.status, + headers: { + "content-type": response.headers.get("content-type") || "application/json", + "cache-control": "no-store", + }, + }); +} diff --git a/surfsense_web/components/announcements/AnnouncementCard.tsx b/surfsense_web/components/announcements/AnnouncementCard.tsx new file mode 100644 index 000000000..daaecee07 --- /dev/null +++ b/surfsense_web/components/announcements/AnnouncementCard.tsx @@ -0,0 +1,117 @@ +"use client"; + +import { + Bell, + ExternalLink, + Info, + type LucideIcon, + Rocket, + Wrench, + Zap, +} from "lucide-react"; +import Link from "next/link"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import type { AnnouncementCategory } from "@/contracts/types/announcement.types"; +import type { AnnouncementWithState } from "@/hooks/use-announcements"; +import { formatRelativeDate } from "@/lib/format-date"; + +const categoryConfig: Record< + AnnouncementCategory, + { + label: string; + icon: LucideIcon; + color: string; + badgeVariant: "default" | "secondary" | "destructive" | "outline"; + } +> = { + feature: { + label: "Feature", + icon: Rocket, + color: "text-emerald-500", + badgeVariant: "default", + }, + update: { + label: "Update", + icon: Zap, + color: "text-blue-500", + badgeVariant: "secondary", + }, + maintenance: { + label: "Maintenance", + icon: Wrench, + color: "text-amber-500", + badgeVariant: "outline", + }, + info: { + label: "Info", + icon: Info, + color: "text-muted-foreground", + badgeVariant: "secondary", + }, +}; + +export function AnnouncementCard({ announcement }: { announcement: AnnouncementWithState }) { + const config = categoryConfig[announcement.category] ?? categoryConfig.info; + const Icon = config.icon; + + return ( +{announcement.description}
++ You're all caught up! New announcements will appear here. +
++ Improves search quality but adds latency during indexing +
+- Schedule a meeting with our Head of Product, Eric Lammertsma, or send us an email. + Schedule a meeting with us, or send us an email.
- Connect any AI to your documents, Drive, Notion and more, -
-- then chat with it, generate podcasts and reports, or even invite your team. +
+ Connect any LLM to your internal knowledge sources and chat with it in real time alongside + your team.
Name
-{committedArgs?.name ?? args.name}
-Type
-- {FILE_TYPE_LABELS[committedArgs?.file_type ?? args.file_type] ?? committedArgs?.file_type ?? args.file_type} -
-Content
-- {committedArgs?.content ?? args.content} +
Name
+{committedArgs?.name ?? args.name}
+Type
++ {FILE_TYPE_LABELS[committedArgs?.file_type ?? args.file_type] ?? + committedArgs?.file_type ?? + args.file_type}
Content
++ {committedArgs?.content ?? args.content} +
+{result.message}
- )} + {result.message &&{result.message}
}{result.warning}
+ {truncateCommand(command)}
+
+ Execution failed
+
+ $ {command}
+
+ {error}
+
+
+ {truncateCommand(command)}
+
+ {hasFiles && !open && (
+ + Command +
+
+ {command}
+
+ + Output +
+ )} +
+ {parsed.displayOutput}
+
+ + Output was truncated due to size limits +
+ )} + {hasFiles && threadId && ( ++ Files +
+- {description} -
+{description}
) : frozenFrame ? ( -