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)} + > + {t("settings.navScopePublic")} + {t("settings.navScopeUser")} + + 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 {