diff --git a/frontend/public/locales/en-US/dashboard.json b/frontend/public/locales/en-US/dashboard.json
index 2bfc7485..01442d79 100644
--- a/frontend/public/locales/en-US/dashboard.json
+++ b/frontend/public/locales/en-US/dashboard.json
@@ -170,6 +170,9 @@
"customNavItems": "Custom sidebar items",
"customNavItemsDes": "You can add custom items to the sidebar, and users will be redirected to the corresponding link when clicked.",
"navItemUrl": "Link",
+ "navItemVisibility": "Visibility",
+ "navScopePublic": "Everyone",
+ "navScopeUser": "Logged-in users",
"iconifyNamePlaceholder": "Iconify icon identifier, e.g. fluent:home-24-regular",
"imageUrl": "Image URL",
"iconifyName": "Iconify icon name",
diff --git a/frontend/public/locales/zh-CN/dashboard.json b/frontend/public/locales/zh-CN/dashboard.json
index 960e4612..e983d6cc 100644
--- a/frontend/public/locales/zh-CN/dashboard.json
+++ b/frontend/public/locales/zh-CN/dashboard.json
@@ -170,6 +170,9 @@
"customNavItems": "自定义侧边导航栏",
"customNavItemsDes": "你可以在左侧导航栏中添加自定义的条目,用户点击后会跳转到对应的链接。",
"navItemUrl": "链接",
+ "navItemVisibility": "可见范围",
+ "navScopePublic": "所有人",
+ "navScopeUser": "登录用户",
"iconifyNamePlaceholder": "Iconify 图标标识,比如:fluent:home-24-regular",
"imageUrl": "图像 URL",
"iconifyName": "Iconify 图标名",
diff --git a/frontend/src/api/site.ts b/frontend/src/api/site.ts
index 0b1497e4..b185bc5e 100644
--- a/frontend/src/api/site.ts
+++ b/frontend/src/api/site.ts
@@ -65,6 +65,7 @@ export interface CustomNavItem {
name: string;
url: string;
icon: string;
+ scope?: "public" | "user";
}
export interface CustomHTML {
diff --git a/frontend/src/component/Admin/Settings/Appearance/CustomNavItems.tsx b/frontend/src/component/Admin/Settings/Appearance/CustomNavItems.tsx
index 6aa863aa..bb146200 100644
--- a/frontend/src/component/Admin/Settings/Appearance/CustomNavItems.tsx
+++ b/frontend/src/component/Admin/Settings/Appearance/CustomNavItems.tsx
@@ -2,6 +2,7 @@ import { Icon } from "@iconify/react/dist/iconify.js";
import {
Box,
IconButton,
+ MenuItem,
Table,
TableBody,
TableCell,
@@ -18,6 +19,7 @@ import { useTranslation } from "react-i18next";
import { CustomNavItem } from "../../../../api/site";
import {
DenseFilledTextField,
+ DenseSelect,
NoWrapCell,
NoWrapTableCell,
SecondaryButton,
@@ -49,6 +51,7 @@ interface DraggableNavItemRowProps {
inputCache: { [key: number]: { [field: string]: string | undefined } };
onInputChange: (index: number, field: string, value: string) => void;
onInputBlur: (index: number, field: keyof CustomNavItem) => void;
+ onFieldChange: (index: number, field: keyof CustomNavItem, value: string) => void;
IconPreview: React.ComponentType<{ iconName: string }>;
t: any;
style?: React.CSSProperties;
@@ -69,6 +72,7 @@ const DraggableNavItemRow = React.memo(
inputCache,
onInputChange,
onInputBlur,
+ onFieldChange,
IconPreview,
t,
style,
@@ -144,6 +148,17 @@ const DraggableNavItemRow = React.memo(
placeholder="https://example.com"
/>
+
+ onFieldChange(index, "scope", e.target.value as string)}
+ >
+
+
+
+
onDelete(index)}>
@@ -327,6 +342,7 @@ const CustomNavItems = ({ value, onChange }: CustomNavItemsProps) => {
{t("settings.iconifyName")}
{t("settings.displayName")}
{t("settings.navItemUrl")}
+ {t("settings.navItemVisibility")}
@@ -349,6 +365,7 @@ const CustomNavItems = ({ value, onChange }: CustomNavItemsProps) => {
inputCache={inputCache}
onInputChange={handleInputChange}
onInputBlur={handleInputBlur}
+ onFieldChange={handleFieldChange}
IconPreview={IconPreview}
t={t}
/>
@@ -356,7 +373,7 @@ const CustomNavItems = ({ value, onChange }: CustomNavItemsProps) => {
})}
{items.length === 0 && (
-
+
{t("application:setting.listEmpty")}
diff --git a/frontend/src/component/Frame/NavBar/PageNavigation.tsx b/frontend/src/component/Frame/NavBar/PageNavigation.tsx
index 1b6b9134..5457ad32 100644
--- a/frontend/src/component/Frame/NavBar/PageNavigation.tsx
+++ b/frontend/src/component/Frame/NavBar/PageNavigation.tsx
@@ -256,16 +256,18 @@ const PageNavigation = () => {
)}
{customNavItems && customNavItems.length > 0 && (
- {customNavItems.map((item) => (
-
- ))}
+ {customNavItems
+ .filter((item) => isLogin || item.scope !== "user")
+ .map((item) => (
+
+ ))}
)}
{isLogin && isAdmin && (
diff --git a/pkg/setting/types.go b/pkg/setting/types.go
index 67d3aece..3bedac74 100644
--- a/pkg/setting/types.go
+++ b/pkg/setting/types.go
@@ -244,6 +244,9 @@ type CustomNavItem struct {
Icon string `json:"icon"`
Name string `json:"name"`
URL string `json:"url"`
+ // Scope limits display: "" or "public" shows to everyone, "user" only
+ // to logged-in accounts (#3578).
+ Scope string `json:"scope,omitempty"`
}
type CustomHTML struct {