mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-22 23:31:12 +02:00
feat: update settings navigation to include section parameters for improved user experience
This commit is contained in:
parent
c7736aa62c
commit
ccf8c063da
9 changed files with 47 additions and 31 deletions
|
|
@ -259,7 +259,7 @@ export default function OnboardPage() {
|
||||||
You can add more configurations and customize settings anytime in{" "}
|
You can add more configurations and customize settings anytime in{" "}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.push(`/dashboard/${searchSpaceId}/settings`)}
|
onClick={() => router.push(`/dashboard/${searchSpaceId}/settings?section=general`)}
|
||||||
className="text-violet-500 hover:underline"
|
className="text-violet-500 hover:underline"
|
||||||
>
|
>
|
||||||
Settings
|
Settings
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import {
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { AnimatePresence, motion } from "motion/react";
|
import { AnimatePresence, motion } from "motion/react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { PublicChatSnapshotsManager } from "@/components/public-chat-snapshots/public-chat-snapshots-manager";
|
import { PublicChatSnapshotsManager } from "@/components/public-chat-snapshots/public-chat-snapshots-manager";
|
||||||
|
|
@ -248,7 +248,7 @@ function SettingsContent({
|
||||||
{/* Section Header */}
|
{/* Section Header */}
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
<motion.div
|
<motion.div
|
||||||
key={activeSection + "-header"}
|
key={`${activeSection}-header`}
|
||||||
initial={{ opacity: 0, y: 10 }}
|
initial={{ opacity: 0, y: 10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: -10 }}
|
exit={{ opacity: 0, y: -10 }}
|
||||||
|
|
@ -317,14 +317,26 @@ function SettingsContent({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VALID_SECTIONS = new Set(settingsNavItems.map((item) => item.id));
|
||||||
|
const DEFAULT_SECTION = "general";
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
const searchSpaceId = Number(params.search_space_id);
|
const searchSpaceId = Number(params.search_space_id);
|
||||||
const [activeSection, setActiveSection] = useState("general");
|
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
|
|
||||||
// Track settings section view
|
const sectionParam = searchParams.get("section");
|
||||||
|
const activeSection = sectionParam && VALID_SECTIONS.has(sectionParam) ? sectionParam : DEFAULT_SECTION;
|
||||||
|
|
||||||
|
const handleSectionChange = useCallback(
|
||||||
|
(section: string) => {
|
||||||
|
router.replace(`/dashboard/${searchSpaceId}/settings?section=${section}`, { scroll: false });
|
||||||
|
},
|
||||||
|
[router, searchSpaceId]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
trackSettingsViewed(searchSpaceId, activeSection);
|
trackSettingsViewed(searchSpaceId, activeSection);
|
||||||
}, [searchSpaceId, activeSection]);
|
}, [searchSpaceId, activeSection]);
|
||||||
|
|
@ -344,7 +356,7 @@ export default function SettingsPage() {
|
||||||
<div className="flex h-full w-full overflow-hidden bg-background md:rounded-xl md:border md:shadow-sm">
|
<div className="flex h-full w-full overflow-hidden bg-background md:rounded-xl md:border md:shadow-sm">
|
||||||
<SettingsSidebar
|
<SettingsSidebar
|
||||||
activeSection={activeSection}
|
activeSection={activeSection}
|
||||||
onSectionChange={setActiveSection}
|
onSectionChange={handleSectionChange}
|
||||||
onBackToApp={handleBackToApp}
|
onBackToApp={handleBackToApp}
|
||||||
isOpen={isSidebarOpen}
|
isOpen={isSidebarOpen}
|
||||||
onClose={() => setIsSidebarOpen(false)}
|
onClose={() => setIsSidebarOpen(false)}
|
||||||
|
|
|
||||||
|
|
@ -731,7 +731,7 @@ function CreateInviteDialog({
|
||||||
</code>
|
</code>
|
||||||
<Button variant="outline" size="sm" onClick={copyLink} className="shrink-0">
|
<Button variant="outline" size="sm" onClick={copyLink} className="shrink-0">
|
||||||
{copiedLink ? (
|
{copiedLink ? (
|
||||||
<Check className="h-4 w-4 text-emerald-500" />
|
<Check className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
<Copy className="h-4 w-4" />
|
<Copy className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
|
|
@ -902,7 +902,7 @@ function AllInvitesDialog({
|
||||||
{invites.map((invite) => (
|
{invites.map((invite) => (
|
||||||
<div
|
<div
|
||||||
key={invite.id}
|
key={invite.id}
|
||||||
className="rounded-lg border border-border/40 p-3 space-y-2.5 transition-colors hover:bg-muted/30"
|
className="rounded-lg border border-border/40 p-3 space-y-2.5"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
|
|
@ -929,7 +929,7 @@ function AllInvitesDialog({
|
||||||
</div>
|
</div>
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 text-destructive hover:text-destructive">
|
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 text-muted-foreground hover:text-destructive">
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</AlertDialogTrigger>
|
||||||
|
|
@ -954,14 +954,16 @@ function AllInvitesDialog({
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 rounded-md bg-muted p-2">
|
<div className="flex items-center gap-2 rounded-md bg-muted p-2">
|
||||||
<code className="flex-1 min-w-0 text-sm select-all overflow-x-auto whitespace-nowrap">
|
<div className="flex-1 min-w-0 overflow-x-auto scrollbar-hide">
|
||||||
{typeof window !== "undefined"
|
<code className="text-sm select-all whitespace-nowrap">
|
||||||
? `${window.location.origin}/invite/${invite.invite_code}`
|
{typeof window !== "undefined"
|
||||||
: `/invite/${invite.invite_code}`}
|
? `${window.location.origin}/invite/${invite.invite_code}`
|
||||||
</code>
|
: `/invite/${invite.invite_code}`}
|
||||||
<Button variant="outline" size="sm" className="shrink-0" onClick={() => copyLink(invite)}>
|
</code>
|
||||||
|
</div>
|
||||||
|
<Button variant="ghost" size="sm" className="shrink-0" onClick={() => copyLink(invite)}>
|
||||||
{copiedId === invite.id ? (
|
{copiedId === invite.id ? (
|
||||||
<Check className="h-4 w-4 text-emerald-500" />
|
<Check className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
<Copy className="h-4 w-4" />
|
<Copy className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ export const ConnectorIndicator: FC<{ hideTrigger?: boolean }> = ({ hideTrigger
|
||||||
: "You need to configure a Document Summary LLM before adding connectors. This LLM is used to process and summarize documents from your connected sources."}
|
: "You need to configure a Document Summary LLM before adding connectors. This LLM is used to process and summarize documents from your connected sources."}
|
||||||
</p>
|
</p>
|
||||||
<Button asChild size="sm" variant="outline">
|
<Button asChild size="sm" variant="outline">
|
||||||
<Link href={`/dashboard/${searchSpaceId}/settings`}>
|
<Link href={`/dashboard/${searchSpaceId}/settings?section=models`}>
|
||||||
<Settings className="mr-2 h-4 w-4" />
|
<Settings className="mr-2 h-4 w-4" />
|
||||||
Go to Settings
|
Go to Settings
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ const DocumentUploadPopupContent: FC<{
|
||||||
: "You need to configure a Document Summary LLM before uploading files. This LLM is used to process and summarize your uploaded documents."}
|
: "You need to configure a Document Summary LLM before uploading files. This LLM is used to process and summarize your uploaded documents."}
|
||||||
</p>
|
</p>
|
||||||
<Button asChild size="sm" variant="outline">
|
<Button asChild size="sm" variant="outline">
|
||||||
<Link href={`/dashboard/${searchSpaceId}/settings`}>
|
<Link href={`/dashboard/${searchSpaceId}/settings?section=models`}>
|
||||||
<Settings className="mr-2 h-4 w-4" />
|
<Settings className="mr-2 h-4 w-4" />
|
||||||
Go to Settings
|
Go to Settings
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ export function LayoutDataProvider({
|
||||||
|
|
||||||
const handleSearchSpaceSettings = useCallback(
|
const handleSearchSpaceSettings = useCallback(
|
||||||
(space: SearchSpace) => {
|
(space: SearchSpace) => {
|
||||||
router.push(`/dashboard/${space.id}/settings`);
|
router.push(`/dashboard/${space.id}/settings?section=general`);
|
||||||
},
|
},
|
||||||
[router]
|
[router]
|
||||||
);
|
);
|
||||||
|
|
@ -478,7 +478,7 @@ export function LayoutDataProvider({
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSettings = useCallback(() => {
|
const handleSettings = useCallback(() => {
|
||||||
router.push(`/dashboard/${searchSpaceId}/settings`);
|
router.push(`/dashboard/${searchSpaceId}/settings?section=general`);
|
||||||
}, [router, searchSpaceId]);
|
}, [router, searchSpaceId]);
|
||||||
|
|
||||||
const handleManageMembers = useCallback(() => {
|
const handleManageMembers = useCallback(() => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
ArchiveIcon,
|
ArchiveIcon,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
PencilIcon,
|
PenLine,
|
||||||
RotateCcwIcon,
|
RotateCcwIcon,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
@ -74,7 +74,7 @@ export function ChatListItem({
|
||||||
onRename();
|
onRename();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PencilIcon className="mr-2 h-4 w-4" />
|
<PenLine className="mr-2 h-4 w-4" />
|
||||||
<span>{t("rename") || "Rename"}</span>
|
<span>{t("rename") || "Rename"}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.push(`/dashboard/${params.search_space_id}/settings`)}
|
onClick={() => router.push(`/dashboard/${params.search_space_id}/settings?section=public-links`)}
|
||||||
className="flex items-center justify-center h-8 w-8 rounded-md bg-muted/50 hover:bg-muted transition-colors"
|
className="flex items-center justify-center h-8 w-8 rounded-md bg-muted/50 hover:bg-muted transition-colors"
|
||||||
>
|
>
|
||||||
<Earth className="h-4 w-4 text-muted-foreground" />
|
<Earth className="h-4 w-4 text-muted-foreground" />
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,12 @@ export function PublicChatSnapshotRow({
|
||||||
memberMap,
|
memberMap,
|
||||||
}: PublicChatSnapshotRowProps) {
|
}: PublicChatSnapshotRowProps) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const copyTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
const copyTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
|
||||||
|
|
||||||
const handleCopyClick = useCallback(() => {
|
const handleCopyClick = useCallback(() => {
|
||||||
onCopy(snapshot);
|
onCopy(snapshot);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
clearTimeout(copyTimeoutRef.current);
|
if (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);
|
||||||
copyTimeoutRef.current = setTimeout(() => setCopied(false), 2000);
|
copyTimeoutRef.current = setTimeout(() => setCopied(false), 2000);
|
||||||
}, [onCopy, snapshot]);
|
}, [onCopy, snapshot]);
|
||||||
|
|
||||||
|
|
@ -117,12 +117,14 @@ export function PublicChatSnapshotRow({
|
||||||
|
|
||||||
{/* Public URL – selectable fallback for manual copy */}
|
{/* Public URL – selectable fallback for manual copy */}
|
||||||
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
|
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
|
||||||
<p
|
<div className="min-w-0 flex-1 overflow-x-auto scrollbar-hide">
|
||||||
className="min-w-0 flex-1 text-[10px] font-mono text-muted-foreground break-all select-all cursor-text"
|
<p
|
||||||
title={snapshot.public_url}
|
className="text-[10px] font-mono text-muted-foreground whitespace-nowrap select-all cursor-text"
|
||||||
>
|
title={snapshot.public_url}
|
||||||
{snapshot.public_url}
|
>
|
||||||
</p>
|
{snapshot.public_url}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue