diff --git a/frontend/desktop/src/components/SecondaryLinks/index.tsx b/frontend/desktop/src/components/SecondaryLinks/index.tsx new file mode 100644 index 000000000..73f7278aa --- /dev/null +++ b/frontend/desktop/src/components/SecondaryLinks/index.tsx @@ -0,0 +1,244 @@ +import { useConfigStore } from '@/stores/config'; +import useAppStore from '@/stores/app'; +import { useGuideModalStore } from '@/stores/guideModal'; +import { + Box, + Center, + Divider, + Flex, + Text, + useBreakpointValue, + Menu, + MenuButton, + MenuList, + MenuItem +} from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; +import { useQuery } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; +import { getAmount } from '@/api/auth'; +import Decimal from 'decimal.js'; +import { CurrencySymbol } from '@sealos/ui'; +import { formatMoney } from '@/utils/format'; +import useSessionStore from '@/stores/session'; +import { MoreHorizontal } from 'lucide-react'; + +const baseItemStyle = { + minW: '36px', + h: '40px', + fontSize: '14px', + fontWeight: '500', + color: 'primary', + _hover: { + background: 'secondary' + } +}; + +export default function SecondaryLinks() { + const { layoutConfig } = useConfigStore(); + const { t } = useTranslation(); + const { openGuideModal, setInitGuide } = useGuideModalStore(); + const { openDesktopApp } = useAppStore(); + const { session } = useSessionStore(); + const user = session?.user; + + const isCollapsed = useBreakpointValue({ + base: true, + lg: false + }); + + const openWorkOrderApp = () => { + openDesktopApp({ + appKey: 'system-workorder', + pathname: '/' + }); + }; + + const openCostCenterApp = () => { + openDesktopApp({ + appKey: 'system-costcenter', + pathname: '/' + }); + }; + + const { data } = useQuery({ + queryKey: ['getAmount', { userId: user?.userCrUid }], + queryFn: getAmount, + enabled: !!user, + staleTime: 60 * 1000 + }); + + const balance = useMemo(() => { + let realBalance = new Decimal(data?.data?.balance || 0); + if (data?.data?.deductionBalance) { + realBalance = realBalance.minus(new Decimal(data.data.deductionBalance)); + } + return realBalance.toNumber(); + }, [data]); + + const handleGuideClick = () => { + openGuideModal(); + setInitGuide(false); + }; + + const handleDocsClick = () => { + if (layoutConfig?.common.docsUrl) { + window.open(layoutConfig.common.docsUrl); + } + }; + + if (!isCollapsed) { + return ( + + {layoutConfig?.version === 'cn' && ( +
+ {t('common:balance')} + + + {formatMoney(balance).toFixed(2)} +
+ )} + +
+ {t('common:guide')} +
+ + {layoutConfig?.common.docsUrl && ( +
+ {t('common:doc')} +
+ )} + + {layoutConfig?.version === 'cn' && ( +
+ {t('v2:support')} +
+ )} +
+ ); + } + + return ( + + + + + + {layoutConfig?.version === 'cn' && ( + <> + + + {t('common:balance')} + + + {formatMoney(balance).toFixed(2)} + + + + + + )} + + + {t('common:guide')} + + + {layoutConfig?.common.docsUrl && ( + + {t('common:doc')} + + )} + + {layoutConfig?.version === 'cn' && ( + + {t('v2:support')} + + )} + + + ); +} diff --git a/frontend/desktop/src/components/account/index.tsx b/frontend/desktop/src/components/account/index.tsx index 47a6c7dbb..4cd9a1122 100644 --- a/frontend/desktop/src/components/account/index.tsx +++ b/frontend/desktop/src/components/account/index.tsx @@ -18,10 +18,10 @@ import { useColorMode, useDisclosure } from '@chakra-ui/react'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import RegionToggle from '../region/RegionToggle'; import WorkspaceToggle from '../team/WorkspaceToggle'; import useAppStore from '@/stores/app'; @@ -38,11 +38,9 @@ import { } from 'lucide-react'; import AccountCenter from './AccountCenter'; import { useLanguageSwitcher } from '@/hooks/useLanguageSwitcher'; -import { getAmount } from '@/api/auth'; -import Decimal from 'decimal.js'; -import { CopyIcon, CurrencySymbol } from '@sealos/ui'; +import { CopyIcon } from '@sealos/ui'; import { useGuideModalStore } from '@/stores/guideModal'; -import { formatMoney } from '@/utils/format'; +import SecondaryLinks from '../SecondaryLinks'; const baseItemStyle = { minW: '36px', @@ -72,10 +70,6 @@ export default function Account() { const { toggleLanguage, currentLanguage } = useLanguageSwitcher(); const onAmount = useCallback((amount: number) => setNotificationAmount(amount), []); const [showNsId, setShowNsId] = useState(false); - const linksCollapsed = useBreakpointValue({ - base: true, - md: false - }); const logout = (e: React.MouseEvent) => { e.preventDefault(); @@ -85,19 +79,6 @@ export default function Account() { setToken(''); }; - const openWorkOrderApp = () => { - const workorder = installedApps.find((t) => t.key === 'system-workorder'); - if (!workorder) return; - openApp(workorder); - }; - - const openCostCenterApp = () => { - openDesktopApp({ - appKey: 'system-costcenter', - pathname: '/' - }); - }; - const openReferralApp = () => { openDesktopApp({ appKey: 'system-invite', @@ -105,21 +86,6 @@ export default function Account() { }); }; - const { data } = useQuery({ - queryKey: ['getAmount', { userId: user?.userCrUid }], - queryFn: getAmount, - enabled: !!user, - staleTime: 60 * 1000 - }); - - const balance = useMemo(() => { - let realBalance = new Decimal(data?.data?.balance || 0); - if (data?.data?.deductionBalance) { - realBalance = realBalance.minus(new Decimal(data.data.deductionBalance)); - } - return realBalance.toNumber(); - }, [data]); - useEffect(() => { if (initGuide) { openGuideModal(); @@ -127,9 +93,9 @@ export default function Account() { }, [initGuide, openGuideModal]); return ( - - - + + +
- {!linksCollapsed && ( - - {layoutConfig?.version === 'cn' && ( -
- {t('common:balance')} - - - {formatMoney(balance).toFixed(2)} -
- )} - -
{ - openGuideModal(); - setInitGuide(false); - }} - > - {t('common:guide')} -
- - {layoutConfig?.common.docsUrl && ( -
window.open(layoutConfig?.common?.docsUrl)} - > - {t('common:doc')} -
- )} - - {layoutConfig?.version === 'cn' && ( -
- {t('v2:support')} -
- )} -
- )} - */} + +
*/} - - -
- -
- - {t('common:billing')} - -
-
+ {layoutConfig?.version === 'cn' && (
{children} @@ -147,29 +159,27 @@ const AppGridPagingContainer = ({ children, gridGap, appHeight, + columns, totalPages, currentPage, handleNavigation, pageGap, - onChange + onChange, + dragContainerProps }: { children: ReactNode; + dragContainerProps?: FlexProps; gridGap: number; appHeight: number; + columns: number; totalPages: number; currentPage: number; handleNavigation: boolean; pageGap: number; onChange: (currentPage: number, pageSize: number) => void; }) => { - const gridWrapperRef = useRef(null); - - const columns = - useBreakpointValue({ - base: 2, - sm: 3, - lg: 5 - }) ?? 2; + const dragContainerRef = useRef(null); + const gridContainerRef = useRef(null); const [pageWidth, setPageWidth] = useState(0); const [itemsPerPage, setItemsPerPage] = useState(8); @@ -185,18 +195,19 @@ const AppGridPagingContainer = ({ : (pageWidth + pageGap) * clampedCurrentPage - dragDelta; const calculateItemsPerPage = useCallback(() => { - if (!gridWrapperRef.current) return 8; + if (!gridContainerRef.current) return 8; - const height = gridWrapperRef.current.clientHeight; - const rows = Math.floor((height + gridGap) / (appHeight + gridGap)); + const height = gridContainerRef.current.clientHeight; + // At least 1 row in a grid. + const rows = Math.max(1, Math.floor((height + gridGap) / (appHeight + gridGap))); return rows * columns; }, [columns, gridGap, appHeight]); const calculatePageWidth = useCallback(() => { - if (!gridWrapperRef.current) return 0; + if (!gridContainerRef.current) return 0; - return (gridWrapperRef.current.scrollWidth + pageGap) / totalPages - pageGap; + return (gridContainerRef.current.scrollWidth + pageGap) / totalPages - pageGap; }, [totalPages, pageGap]); // Calculate items per page and scroll position on initial render and on screen size changes @@ -266,11 +277,11 @@ const AppGridPagingContainer = ({ // Pointer drag pagination useEffect(() => { - const wrapper = gridWrapperRef.current; + const wrapper = dragContainerRef.current; if (!wrapper) return; const handlePointerDown = (e: PointerEvent) => { - if (!gridWrapperRef.current) return; + if (!dragContainerRef.current) return; setDragStartX(e.screenX); setDragStartY(e.screenY); @@ -332,17 +343,19 @@ const AppGridPagingContainer = ({ ]); return ( - - {children} + + + {children} + ); }; @@ -477,12 +490,12 @@ const PageSwitcher = ({ onChange: (target: number) => void; }) => { return ( - + {Array.from({ length: pages }).map((_, index) => ( (null); const modalContentRef = useRef(null); + // min = 1 line app name + // max = 2 lines app name const appHeight = useBreakpointValue({ - base: 114, - md: 146 - }) ?? 114; + base: 112, + md: 126 + }) ?? 112; + const columns = + useBreakpointValue({ + base: 2, + sm: 3, + lg: 5 + }) ?? 2; const gridGap = 10; const [itemsPerPageInGrid, setItemsPerPageInGrid] = useState(0); @@ -801,6 +822,7 @@ export default function Apps() { {desktopPages.map((page, pageIndex) => ( - + {page.map((app, index) => ( - + { setCurrentPageInFolder(currentPage); setItemsPerPageInFolder(pageSize); - setTotalPagesInFolder(Math.ceil(moreApps.length / pageSize)); + setTotalPagesInFolder(Math.ceil(folderApps.length / pageSize)); }} > {folderPages.map((page, pageIndex) => ( - + {namespace?.id && ( ))} diff --git a/frontend/desktop/src/components/v2/PhoneCheck.tsx b/frontend/desktop/src/components/v2/PhoneCheck.tsx index 34603e46b..019b32872 100644 --- a/frontend/desktop/src/components/v2/PhoneCheck.tsx +++ b/frontend/desktop/src/components/v2/PhoneCheck.tsx @@ -289,7 +289,7 @@ export default function PhoneCheckComponent() { mr={{ base: '4px', lg: '8px' }} boxSize={{ base: '40px', lg: '56px' }} fontSize={{ base: '16px', lg: '20px' }} - borderRadius={{ base: '8px', lg: '12px' }} + borderRadius={'12px'} /> ))}