diff --git a/app/dashboard/chakra.config.ts b/app/dashboard/chakra.config.ts index b0c93e8..b69e327 100644 --- a/app/dashboard/chakra.config.ts +++ b/app/dashboard/chakra.config.ts @@ -6,6 +6,7 @@ export const theme = extendTheme({ }, colors: { 'light-border': '#d2d2d4', + 'dark-bg': '#1A202C', primary: { 50: '#9cb7f2', 100: '#88a9ef', diff --git a/app/dashboard/src/components/delete-modal/index.tsx b/app/dashboard/src/components/delete-modal/index.tsx index 449d3a8..a0a7733 100644 --- a/app/dashboard/src/components/delete-modal/index.tsx +++ b/app/dashboard/src/components/delete-modal/index.tsx @@ -14,7 +14,7 @@ import { } from '@chakra-ui/react'; import { FC } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { DeleteIcon } from 'components/dialog/Icons'; +import { DeleteIcon } from 'components/dialog'; import { Icon } from 'components/icon'; type DeleteModalProps = { diff --git a/app/dashboard/src/components/dialog/Icons.tsx b/app/dashboard/src/components/dialog/icons.tsx similarity index 100% rename from app/dashboard/src/components/dialog/Icons.tsx rename to app/dashboard/src/components/dialog/icons.tsx diff --git a/app/dashboard/src/components/dialog/index.ts b/app/dashboard/src/components/dialog/index.ts new file mode 100644 index 0000000..4946043 --- /dev/null +++ b/app/dashboard/src/components/dialog/index.ts @@ -0,0 +1,3 @@ +export * from './modal-footer'; +export * from './modal-header'; +export * from './icons'; diff --git a/app/dashboard/src/components/dialog/ModalFooter.tsx b/app/dashboard/src/components/dialog/modal-footer.tsx similarity index 100% rename from app/dashboard/src/components/dialog/ModalFooter.tsx rename to app/dashboard/src/components/dialog/modal-footer.tsx diff --git a/app/dashboard/src/components/dialog/ModalHeader.tsx b/app/dashboard/src/components/dialog/modal-header.tsx similarity index 100% rename from app/dashboard/src/components/dialog/ModalHeader.tsx rename to app/dashboard/src/components/dialog/modal-header.tsx diff --git a/app/dashboard/src/components/Footer.tsx b/app/dashboard/src/components/footer/index.tsx similarity index 100% rename from app/dashboard/src/components/Footer.tsx rename to app/dashboard/src/components/footer/index.tsx diff --git a/app/dashboard/src/components/header/github-button.tsx b/app/dashboard/src/components/header/github-button.tsx new file mode 100644 index 0000000..55d1547 --- /dev/null +++ b/app/dashboard/src/components/header/github-button.tsx @@ -0,0 +1,20 @@ + +import { REPO_URL } from 'constants/Project'; +import { useColorMode } from '@chakra-ui/react'; +import GitHubButton from 'react-github-btn'; + +export const GithubMenuButton = () => { + const { colorMode } = useColorMode(); + const gBtnColor = colorMode === 'dark' ? 'dark_dimmed' : colorMode; + return ( + + Star + + ) +} diff --git a/app/dashboard/src/components/header/index.tsx b/app/dashboard/src/components/header/index.tsx index 1877895..7ecc824 100644 --- a/app/dashboard/src/components/header/index.tsx +++ b/app/dashboard/src/components/header/index.tsx @@ -1,90 +1,24 @@ import { Box, - chakra, HStack, - IconButton, - Menu, - MenuButton, - MenuItem, - MenuList, Text, - useColorMode, } from '@chakra-ui/react'; -import { - ArrowLeftStartOnRectangleIcon, - Bars3Icon, - Cog6ToothIcon, - CurrencyDollarIcon, -} from '@heroicons/react/24/outline'; -import { pages } from 'constants/Pages'; -import { DONATION_URL, REPO_URL } from 'constants/Project'; -import { useNodes, useDashboard } from 'stores'; -import differenceInDays from 'date-fns/differenceInDays'; -import isValid from 'date-fns/isValid'; -import { FC, ReactNode, useState } from 'react'; -import GitHubButton from 'react-github-btn'; +import { useDashboard } from 'stores'; +import { FC, ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router-dom'; -import { Language } from '../Language'; -import { ThemeToggle } from '../theme-toggler'; +import { Language } from 'components/language'; +import { ThemeToggle } from 'components/theme-toggler'; type HeaderProps = { actions?: ReactNode; }; -const iconProps = { - baseStyle: { - w: 4, - h: 4, - }, -}; - -const CoreSettingsIcon = chakra(Cog6ToothIcon, iconProps); -const SettingsIcon = chakra(Bars3Icon, iconProps); -const LogoutIcon = chakra(ArrowLeftStartOnRectangleIcon, iconProps); -const DonationIcon = chakra(CurrencyDollarIcon, iconProps); -const NotificationCircle = chakra(Box, { - baseStyle: { - bg: 'yellow.500', - w: '2', - h: '2', - rounded: 'full', - position: 'absolute', - }, -}); -const NOTIFICATION_KEY = 'marzban-menu-notification'; - -export const shouldShowDonation = (): boolean => { - const date = localStorage.getItem(NOTIFICATION_KEY); - if (!date) return true; - try { - if (date && isValid(parseInt(date))) { - if (differenceInDays(new Date(), new Date(parseInt(date))) >= 7) - return true; - return false; - } - return true; - } catch (err) { - return true; - } -}; +import { GithubMenuButton } from './github-button'; +import { HeaderMenu } from './menu'; export const Header: FC = () => { - const { - activePage, - } = useDashboard(); + const { activePage, } = useDashboard(); const { t } = useTranslation(); - const { colorMode } = useColorMode(); - const [showDonationNotif, setShowDonationNotif] = useState( - shouldShowDonation() - ); - const gBtnColor = colorMode === 'dark' ? 'dark_dimmed' : colorMode; - - const handleOnClose = () => { - localStorage.setItem(NOTIFICATION_KEY, new Date().getTime().toString()); - setShowDonationNotif(false); - }; - return ( = () => { direction: 'ltr', }, }} - position="relative" + bg='white' + _dark={{ bg: 'dark-bg' }} + p="6" + borderBottom={'1px solid gray.600'} + position="sticky" + zIndex={999} + top={0} > - {t(pages[activePage]?.name)} + {t(activePage.name)} - {showDonationNotif && ( - - )} - + - - - - - } - position="relative" - > - - {/* NOTE: Host settings trigger */} - {/* } */} - {/* onClick={onEditingHosts.bind(null, true)} */} - {/* > */} - {/* {t('header.hostSettings')} */} - {/* */} - - } - position="relative" - onClick={handleOnClose} - > - {t('header.donation')}{' '} - {showDonationNotif && ( - - )} - - - - }> - {t('header.logout')} - - - - - - { - useNodes.setState({ isEditingCore: true }); - }} - > - - + - - - Star - - + diff --git a/app/dashboard/src/components/header/menu.tsx b/app/dashboard/src/components/header/menu.tsx new file mode 100644 index 0000000..fd90111 --- /dev/null +++ b/app/dashboard/src/components/header/menu.tsx @@ -0,0 +1,62 @@ +import { + IconButton, + Menu, + MenuButton, + MenuItem, + chakra, + MenuList, +} from '@chakra-ui/react'; +import { DONATION_URL } from 'constants/Project' +import { + ArrowLeftStartOnRectangleIcon, + Bars3Icon, + CurrencyDollarIcon, +} from '@heroicons/react/24/outline'; +import { FC } from 'react'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +const iconProps = { + baseStyle: { + w: 4, + h: 4, + }, +}; + +const SettingsIcon = chakra(Bars3Icon, iconProps); +const LogoutIcon = chakra(ArrowLeftStartOnRectangleIcon, iconProps); +const DonationIcon = chakra(CurrencyDollarIcon, iconProps); + +type HeaderMenuProps = {}; + +export const HeaderMenu: FC = () => { + const { t } = useTranslation('header'); + return ( + + } + position="relative" + > + + + } + position="relative" + > + {t('donation')}{' '} + + + + }> + {t('logout')} + + + + + ) +} diff --git a/app/dashboard/src/components/Input.tsx b/app/dashboard/src/components/input/index.tsx similarity index 100% rename from app/dashboard/src/components/Input.tsx rename to app/dashboard/src/components/input/index.tsx diff --git a/app/dashboard/src/components/Language.tsx b/app/dashboard/src/components/language/index.tsx similarity index 100% rename from app/dashboard/src/components/Language.tsx rename to app/dashboard/src/components/language/index.tsx diff --git a/app/dashboard/src/components/OnlineBadge.tsx b/app/dashboard/src/components/online-badge/index.tsx similarity index 100% rename from app/dashboard/src/components/OnlineBadge.tsx rename to app/dashboard/src/components/online-badge/index.tsx diff --git a/app/dashboard/src/components/OnlineStatus.tsx b/app/dashboard/src/components/online-status/index.tsx similarity index 100% rename from app/dashboard/src/components/OnlineStatus.tsx rename to app/dashboard/src/components/online-status/index.tsx diff --git a/app/dashboard/src/components/page/index.tsx b/app/dashboard/src/components/page/index.tsx index fb665d9..63d979c 100644 --- a/app/dashboard/src/components/page/index.tsx +++ b/app/dashboard/src/components/page/index.tsx @@ -1,7 +1,21 @@ import { Box, VStack } from '@chakra-ui/react'; -import { FC, PropsWithChildren } from 'react'; +import { FC, PropsWithChildren, useEffect } from 'react'; +import { useDashboard } from 'stores'; +import { PageType } from 'types'; + +const usePage = (page: PageType) => { + useEffect(() => { + useDashboard.getState().activatePage(page); + }, []); +}; + +type PageProp = { + page: PageType; +} + +export const Page: FC> = ({ page, children }) => { + usePage(page); -export const Page: FC = ({ children }) => { return ( diff --git a/app/dashboard/src/components/sidebar/index.tsx b/app/dashboard/src/components/sidebar/index.tsx index 22f0879..5f1234a 100644 --- a/app/dashboard/src/components/sidebar/index.tsx +++ b/app/dashboard/src/components/sidebar/index.tsx @@ -1,10 +1,10 @@ import { HamburgerIcon } from '@chakra-ui/icons'; import { chakra, Flex } from '@chakra-ui/react' -import { pages } from 'constants/Pages'; +import { pages } from 'stores'; import { FC } from 'react'; import { Logo } from './logo'; import { SidebarGroup } from './sidebar-group'; -import { SidebarItem, SideBarItemProps } from './sidebar-item'; +import { SidebarItem } from './sidebar-item'; export const SidebarMenuIcon = chakra(HamburgerIcon, { baseStyle: { @@ -30,8 +30,12 @@ export const Sidebar: FC = () => { > - {pages.map((item: SideBarItemProps, index: number) => - )} + {Object.entries(pages).map(([pageKey, page]) => + + )} diff --git a/app/dashboard/src/components/sidebar/sidebar-item.tsx b/app/dashboard/src/components/sidebar/sidebar-item.tsx index 36dc6b6..5ec53d6 100644 --- a/app/dashboard/src/components/sidebar/sidebar-item.tsx +++ b/app/dashboard/src/components/sidebar/sidebar-item.tsx @@ -8,19 +8,16 @@ import { FC, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { PageType } from 'types'; -export type SideBarItemProps = Pick< - PageType, - | 'name' - | 'path' - | 'itemIcon' -> +export type SideBarItemProps = { + page: PageType, +} -export const SidebarItem: FC = ({ itemIcon: ItemIcon, name, path, index }) => { +export const SidebarItem: FC = ({ page }) => { const { t } = useTranslation(); const { activePage, activatePage, isCollapsed } = useDashboard(); - const [active, activate] = useState(index === activePage); + const [active, activate] = useState(page === activePage); - useEffect(() => { activate(index === activePage) }, [active, activePage]); + useEffect(() => { activate(page === activePage) }, [active, activePage]); const darkStyle = { bg: active ? 'primary.900' : 'transparent' }; const hoverStyle = { @@ -31,21 +28,20 @@ export const SidebarItem: FC = ({ itemIcon }, transition: 'background-color 0.9s', }; - + const ItemIcon = page.itemIcon; return ( { - activatePage(index); + activatePage(page); }} > @@ -56,7 +52,7 @@ export const SidebarItem: FC = ({ itemIcon color={active ? 'gray.100' : 'gray.700'} _dark={{ color: 'gray.100' }} fontSize="xl" display={isCollapsed ? 'none' : 'block'}> - {t(name)} + {t(page.name)} diff --git a/app/dashboard/src/components/StatusBadge.tsx b/app/dashboard/src/components/status-badge/index.tsx similarity index 100% rename from app/dashboard/src/components/StatusBadge.tsx rename to app/dashboard/src/components/status-badge/index.tsx diff --git a/app/dashboard/src/components/Textarea.tsx b/app/dashboard/src/components/textarea/index.tsx similarity index 100% rename from app/dashboard/src/components/Textarea.tsx rename to app/dashboard/src/components/textarea/index.tsx diff --git a/app/dashboard/src/components/UsageFilter.tsx b/app/dashboard/src/components/usage-filter/index.tsx similarity index 100% rename from app/dashboard/src/components/UsageFilter.tsx rename to app/dashboard/src/components/usage-filter/index.tsx diff --git a/app/dashboard/src/constants/Pages.tsx b/app/dashboard/src/constants/Pages.tsx deleted file mode 100644 index a7f15b1..0000000 --- a/app/dashboard/src/constants/Pages.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { PageType } from 'types'; -import { chakra } from '@chakra-ui/react'; -import { CubeIcon, HomeIcon, ServerIcon, ServerStackIcon, UsersIcon } from '@heroicons/react/24/outline'; -import { IconBaseStyle } from 'constants/IconBaseStyle'; - -const UsersSidebarItemIcon = chakra(UsersIcon, IconBaseStyle); -const ServicesSidebarItemIcon = chakra(ServerStackIcon, IconBaseStyle); -const NodesSidebarItemIcon = chakra(CubeIcon, IconBaseStyle); -const HostSidebarItemIcon = chakra(ServerIcon, IconBaseStyle); -const HomeSidebarItemIcon = chakra(HomeIcon, IconBaseStyle); - -export const pages: PageType[] = [ - { - name: 'home', - path: '/', - itemIcon: HomeSidebarItemIcon, - }, - { - name: 'users', - path: '/users', - itemIcon: UsersSidebarItemIcon, - }, - { - name: 'services', - path: '/services', - itemIcon: ServicesSidebarItemIcon, - }, - { - name: 'nodes', - path: '/nodes', - itemIcon: NodesSidebarItemIcon, - }, - { - name: 'hosts', - path: '/hosts', - itemIcon: HostSidebarItemIcon, - }, -]; diff --git a/app/dashboard/src/layouts/console-layout.tsx b/app/dashboard/src/layouts/console-layout.tsx index be59e79..dabf1a5 100644 --- a/app/dashboard/src/layouts/console-layout.tsx +++ b/app/dashboard/src/layouts/console-layout.tsx @@ -1,6 +1,6 @@ import { Box } from '@chakra-ui/react'; import { PanelGroup, Panel, PanelResizeHandle, ImperativePanelHandle } from 'react-resizable-panels'; -import { Footer } from 'components/Footer'; +import { Footer } from 'components/footer'; import { Header } from 'components/header'; import { Sidebar } from 'components/sidebar'; import { Suspense, useRef } from 'react'; @@ -18,9 +18,9 @@ export const ConsoleLayout = () => { - +
- + diff --git a/app/dashboard/src/pages/home/index.tsx b/app/dashboard/src/pages/home/index.tsx index c8b5634..d9cf7f9 100644 --- a/app/dashboard/src/pages/home/index.tsx +++ b/app/dashboard/src/pages/home/index.tsx @@ -2,10 +2,11 @@ import Page from 'components/page'; import { FC } from 'react'; import { Statistics } from './statistics'; +import { pages } from 'stores'; export const Home: FC = () => { return ( - + ); diff --git a/app/dashboard/src/pages/hosts/dialog/index.tsx b/app/dashboard/src/pages/hosts/dialog/index.tsx index 25e7398..47c9132 100644 --- a/app/dashboard/src/pages/hosts/dialog/index.tsx +++ b/app/dashboard/src/pages/hosts/dialog/index.tsx @@ -36,9 +36,8 @@ import { getDefaultValues } from './default-values'; import { useForm } from 'react-hook-form'; import 'slick-carousel/slick/slick-theme.css'; import 'slick-carousel/slick/slick.css'; -import { AddIcon, DeleteIcon } from 'components/dialog/Icons'; -import { Input as CustomInput } from 'components/Input'; -import { DialogModalHeader, } from 'components/dialog/ModalHeader'; +import { DialogModalHeader, AddIcon, DeleteIcon } from 'components/dialog'; +import { Input as CustomInput } from 'components/input'; import { EditIcon } from 'components/table'; import { InfoIcon } from './icons'; import { diff --git a/app/dashboard/src/pages/hosts/index.tsx b/app/dashboard/src/pages/hosts/index.tsx index eb7101b..65d8c53 100644 --- a/app/dashboard/src/pages/hosts/index.tsx +++ b/app/dashboard/src/pages/hosts/index.tsx @@ -1,9 +1,10 @@ import { HostsDialog } from './dialog'; import Page from 'components/page'; +import { pages } from 'stores'; const HostsPage = () => { return ( - + ) diff --git a/app/dashboard/src/pages/login/index.tsx b/app/dashboard/src/pages/login/index.tsx index f770ae7..114ed41 100644 --- a/app/dashboard/src/pages/login/index.tsx +++ b/app/dashboard/src/pages/login/index.tsx @@ -16,12 +16,12 @@ import { FC, useEffect, useState } from 'react'; import { FieldValues, useForm } from 'react-hook-form'; import { useLocation, useNavigate } from 'react-router-dom'; import { z } from 'zod'; -import { Footer } from 'components/Footer'; -import { Input } from 'components/Input'; +import { Footer } from 'components/footer'; +import { Input } from 'components/input'; import { fetch } from 'service/http'; import { ReactComponent as Logo } from 'assets/logo.svg'; import { useTranslation } from 'react-i18next'; -import { Language } from 'components/Language'; +import { Language } from 'components/language'; import { useAdmin } from 'stores'; const schema = z.object({ diff --git a/app/dashboard/src/pages/nodes/dialog.tsx b/app/dashboard/src/pages/nodes/dialog.tsx index d6507db..c435760 100644 --- a/app/dashboard/src/pages/nodes/dialog.tsx +++ b/app/dashboard/src/pages/nodes/dialog.tsx @@ -28,9 +28,8 @@ import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import 'slick-carousel/slick/slick-theme.css'; import 'slick-carousel/slick/slick.css'; -import { DeleteIcon, AddIcon, EditIcon } from 'components/dialog/Icons'; -import { Input } from 'components/Input'; -import { DialogModalHeader } from 'components/dialog/ModalHeader'; +import { DialogModalHeader, DeleteIcon, AddIcon, EditIcon } from 'components/dialog'; +import { Input } from 'components/input'; const CustomInput = chakra(Input, { baseStyle: { diff --git a/app/dashboard/src/pages/nodes/index.tsx b/app/dashboard/src/pages/nodes/index.tsx index 2919d0c..f8abaeb 100644 --- a/app/dashboard/src/pages/nodes/index.tsx +++ b/app/dashboard/src/pages/nodes/index.tsx @@ -1,23 +1,17 @@ -import { FC, useEffect } from 'react'; +import { FC } from 'react'; import { CoreSettingsModal } from './modals/core-settings-modal'; import { NodesDialog } from './dialog'; import { NodesUsage } from './usage'; import { NodesTable } from './table'; import { NodesFilters } from './filters'; import { DeleteNodeModal } from './modals/delete-modal'; -import { useNodes, useDashboard } from 'stores'; -import { pages } from 'constants/Pages'; +import { pages } from 'stores'; import Page from 'components/page'; const NodesPage: FC = () => { - useEffect(() => { - useDashboard.getState().activatePage(pages.findIndex((page) => page.path === '/nodes')); - useNodes.getState().refetchNodes(); - useNodes.getState().refetchCertificate(); - }, []); return ( - + diff --git a/app/dashboard/src/pages/nodes/table.tsx b/app/dashboard/src/pages/nodes/table.tsx index 0ee9dbe..c18e1ff 100644 --- a/app/dashboard/src/pages/nodes/table.tsx +++ b/app/dashboard/src/pages/nodes/table.tsx @@ -23,9 +23,9 @@ import { import classNames from 'classnames'; import { t } from 'i18next'; -import { FC, Fragment, useEffect, useState } from 'react'; +import { FC, Fragment, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { StatusBadge } from 'components/StatusBadge'; +import { StatusBadge } from 'components/status-badge'; import { Pagination, EmptySection, @@ -43,6 +43,7 @@ const iconProps = { h: 4, }, }; + const CoreSettingsIcon = chakra(Cog6ToothIcon, iconProps); type ExpandedIndex = number | number[]; @@ -53,9 +54,10 @@ export const NodesTable: FC = (props) => { const { nodesFilters: filters, nodes, - onEditingNode: onEditingNodes, - onAddingNode: onCreateNodes, + onEditingNode, + onAddingNode, onFilterChange, + refetchNodes, } = useNodes(); const total = nodes.length; @@ -64,18 +66,8 @@ export const NodesTable: FC = (props) => { const [selectedRow, setSelectedRow] = useState( undefined ); - const marginTop = useBreakpointValue({ base: 120, lg: 72 }) || 72; - const [top, setTop] = useState(`${marginTop}px`); - const useTable = useBreakpointValue({ base: false, md: true }); - useEffect(() => { - const calcTop = () => { - const el = document.querySelectorAll('#filters')[0] as HTMLElement; - setTop(`${el.offsetHeight}px`); - }; - window.addEventListener('scroll', calcTop); - () => window.removeEventListener('scroll', calcTop); - }, []); + const useTable = useBreakpointValue({ base: false, md: true }); // TODO: Find a different mechancism to to detect ftiler // const isFiltered = users.length !== total; @@ -106,8 +98,15 @@ export const NodesTable: FC = (props) => { setSelectedRow(index === selectedRow ? undefined : index); }; - const StatusSortSelectNodes = () => (); + const StatusSortSelectNodes = () => ( + + ); + refetchNodes(); return ( = (props) => { = (props) => { = (props) => { = (props) => { = (props) => { {/* Action buttons (certificate, core settings)*/} = (props) => { }} onClick={(e) => { e.stopPropagation(); - onEditingNodes(node); + onEditingNode(node); }} > @@ -312,8 +306,6 @@ export const NodesTable: FC = (props) => { = (props) => { = (props) => { = (props) => { = (props) => { - + {useTable && nodes?.map((node, i) => { return ( @@ -393,7 +377,7 @@ export const NodesTable: FC = (props) => { className={classNames('interactive', { 'last-row': i === nodes.length - 1, })} - onClick={() => onEditingNodes(node)} + onClick={() => onEditingNode(node)} >
@@ -425,7 +409,7 @@ export const NodesTable: FC = (props) => { noObjectT="nodesTable.noNode" noObjectMatchedT='nodesTable.noNodeMatched' createObjectT="addNode" - onCreateObject={onCreateNodes} + onCreateObject={onAddingNode} /> diff --git a/app/dashboard/src/pages/nodes/usage.tsx b/app/dashboard/src/pages/nodes/usage.tsx index 3678b60..757eeda 100644 --- a/app/dashboard/src/pages/nodes/usage.tsx +++ b/app/dashboard/src/pages/nodes/usage.tsx @@ -13,7 +13,7 @@ import { FC, Suspense, useEffect, useState } from 'react'; import ReactApexChart from 'react-apexcharts'; import { useTranslation } from 'react-i18next'; import { Icon } from 'components/icon'; -import { UsageFilter, createUsageConfig } from 'components/UsageFilter'; +import { UsageFilter, createUsageConfig } from 'components/usage-filter'; import { StatisticsCardWide } from 'components/statistics-card'; const UsageIcon = chakra(ChartPieIcon, { diff --git a/app/dashboard/src/pages/routes.tsx b/app/dashboard/src/pages/routes.tsx index cd95a40..663b68b 100644 --- a/app/dashboard/src/pages/routes.tsx +++ b/app/dashboard/src/pages/routes.tsx @@ -1,6 +1,7 @@ import { lazy } from 'react'; import { createHashRouter } from 'react-router-dom'; import { fetchAdminLoader } from 'service/http'; +import { pages } from 'stores'; const ConsoleLayout = lazy(() => import('layouts/console-layout')); const Home = lazy(() => import('./home')); @@ -18,23 +19,23 @@ export const router = createHashRouter([ element: , children: [ { - path: '/', + path: pages.home.path, element: , }, { - path: '/users', + path: pages.users.path, element: , }, { - path: '/services', + path: pages.services.path, element: , }, { - path: '/nodes', + path: pages.nodes.path, element: , }, { - path: '/hosts', + path: pages.hosts.path, element: , }, ], diff --git a/app/dashboard/src/pages/services/dialog/index.tsx b/app/dashboard/src/pages/services/dialog/index.tsx index 63580d1..3f6e0c8 100644 --- a/app/dashboard/src/pages/services/dialog/index.tsx +++ b/app/dashboard/src/pages/services/dialog/index.tsx @@ -20,9 +20,13 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { FC, useEffect, useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { AddIcon, DeleteIcon, EditIcon } from 'components/dialog/Icons'; -import { DialogModalHeader } from 'components/dialog/ModalHeader'; -import { DialogModalFooter } from 'components/dialog/ModalFooter'; +import { + AddIcon, + DialogModalHeader, + DialogModalFooter, + DeleteIcon, + EditIcon +} from 'components/dialog'; import { getDefaultValues } from './default-values'; import { schema, FormType } from './form-schema'; import { InboundsField } from './inbounds-field'; diff --git a/app/dashboard/src/pages/services/index.tsx b/app/dashboard/src/pages/services/index.tsx index 1a8a5ff..105566c 100644 --- a/app/dashboard/src/pages/services/index.tsx +++ b/app/dashboard/src/pages/services/index.tsx @@ -1,21 +1,16 @@ -import { FC, useEffect } from 'react'; +import { FC } from 'react'; import { ServicesFilters } from './filters'; import { ServicesTable } from './table'; -import { pages } from 'constants/Pages'; +import { pages } from 'stores'; import { ServiceDialog } from './dialog'; -import { useDashboard, useServices } from 'stores'; import { DeleteServiceModal } from './modals/delete-modal'; import Page from 'components/page'; const ServicesPage: FC = () => { - useEffect(() => { - useDashboard.getState().activatePage(pages.findIndex((page) => page.path === '/services')); - useServices.getState().refetchServices(); - }, []); return ( - + diff --git a/app/dashboard/src/pages/services/table.tsx b/app/dashboard/src/pages/services/table.tsx index 08e6fb5..fc81ba9 100644 --- a/app/dashboard/src/pages/services/table.tsx +++ b/app/dashboard/src/pages/services/table.tsx @@ -31,6 +31,7 @@ export const ServicesTable: FC = (props) => { onEditingService, onCreateService, onFilterChange, + refetchServices, } = useServices(); const { t } = useTranslation(); @@ -62,8 +63,9 @@ export const ServicesTable: FC = (props) => { }); }; + refetchServices(); return ( - + diff --git a/app/dashboard/src/pages/users/dialog/data-limit-field.tsx b/app/dashboard/src/pages/users/dialog/data-limit-field.tsx index cd261b3..d225b53 100644 --- a/app/dashboard/src/pages/users/dialog/data-limit-field.tsx +++ b/app/dashboard/src/pages/users/dialog/data-limit-field.tsx @@ -1,5 +1,5 @@ import { FormControl, FormLabel } from '@chakra-ui/react'; -import { Input } from 'components/Input'; +import { Input } from 'components/input'; import { TFunction } from 'i18next'; import { Controller } from 'react-hook-form'; diff --git a/app/dashboard/src/pages/users/dialog/expire-date-field.tsx b/app/dashboard/src/pages/users/dialog/expire-date-field.tsx index 3175a74..7019df8 100644 --- a/app/dashboard/src/pages/users/dialog/expire-date-field.tsx +++ b/app/dashboard/src/pages/users/dialog/expire-date-field.tsx @@ -8,7 +8,7 @@ import { i18n, TFunction } from 'i18next'; import ReactDatePicker from 'react-datepicker'; import { Controller } from 'react-hook-form'; import { relativeExpiryDate } from 'utils/dateFormatter'; -import { Input } from 'components/Input'; +import { Input } from 'components/input'; interface ExpireDateFieldProps { form: any; diff --git a/app/dashboard/src/pages/users/dialog/index.tsx b/app/dashboard/src/pages/users/dialog/index.tsx index 0cb254d..6ca47dc 100644 --- a/app/dashboard/src/pages/users/dialog/index.tsx +++ b/app/dashboard/src/pages/users/dialog/index.tsx @@ -32,11 +32,14 @@ import { UserCreate, Service } from 'types'; -import { UsageFilter, createUsageConfig } from 'components/UsageFilter'; -import { AddIcon, EditIcon } from 'components/dialog/Icons'; -import { DialogModalHeader } from 'components/dialog/ModalHeader'; -import { DialogModalFooter } from 'components/dialog/ModalFooter'; -import { DeleteIcon } from 'components/dialog/Icons'; +import { UsageFilter, createUsageConfig } from 'components/usage-filter'; +import { + AddIcon, + EditIcon, + DialogModalHeader, + DialogModalFooter, + DeleteIcon +} from 'components/dialog'; import { UserUsageIcon } from './icons'; import { getDefaultValues } from './default-values'; import { schema, FormType } from './form-schema'; diff --git a/app/dashboard/src/pages/users/index.tsx b/app/dashboard/src/pages/users/index.tsx index e906cc8..7e4d74c 100644 --- a/app/dashboard/src/pages/users/index.tsx +++ b/app/dashboard/src/pages/users/index.tsx @@ -1,7 +1,5 @@ import { UserDialog } from './dialog'; import { UsersTable } from './table'; -import { useUsers, useDashboard } from 'stores'; -import { useEffect } from 'react'; import { UsersFilters } from './filters'; import { ResetAllUsageModal, @@ -10,17 +8,12 @@ import { ResetUserUsageModal, RevokeSubscriptionModal } from './modals'; -import { pages } from 'constants/Pages'; +import { pages } from 'stores'; import Page from 'components/page'; - const UsersPage = () => { - useEffect(() => { - useDashboard.getState().activatePage(pages.findIndex((page) => page.path === '/users')); - useUsers.getState().refetchUsers(); - }, []); return ( - + diff --git a/app/dashboard/src/pages/users/table/index.tsx b/app/dashboard/src/pages/users/table/index.tsx index f32fd3b..257ebbe 100644 --- a/app/dashboard/src/pages/users/table/index.tsx +++ b/app/dashboard/src/pages/users/table/index.tsx @@ -27,9 +27,9 @@ import { FC, Fragment, useEffect, useState } from 'react'; import CopyToClipboard from 'react-copy-to-clipboard'; import { useTranslation } from 'react-i18next'; import { User } from 'types'; -import { OnlineBadge } from 'components/OnlineBadge'; -import { OnlineStatus } from 'components/OnlineStatus'; -import { StatusBadge } from 'components/StatusBadge'; +import { OnlineBadge } from 'components/online-badge'; +import { OnlineStatus } from 'components/online-status'; +import { StatusBadge } from 'components/status-badge'; import { Pagination, EmptySection, @@ -54,6 +54,7 @@ export const UsersTable: FC = (props) => { usersFilters: filters, users: { users }, users: { total }, + refetchUsers, onEditingUser, onCreateUser, onFilterChange, @@ -103,7 +104,7 @@ export const UsersTable: FC = (props) => { const toggleAccordion = (index: number) => { setSelectedRow(index === selectedRow ? undefined : index); }; - + refetchUsers(); return ( void; expandSidebar: () => void; - activatePage: (pageId: PageId) => void; + activatePage: (pageId: PageType) => void; }; export const useDashboard = create( subscribeWithSelector((set,) => ({ version: null, loading: true, - activePage: 0, + activePage: pages.home, isCollapsed: false, expandSidebar: () => { set({ isCollapsed: false }) @@ -25,8 +62,8 @@ export const useDashboard = create( collapseSidebar: () => { set({ isCollapsed: true }) }, - activatePage: (pageId: number) => { - set({ activePage: pageId }); + activatePage: (page) => { + set({ activePage: page }); }, })) ); diff --git a/app/dashboard/src/types/page.ts b/app/dashboard/src/types/page.ts index 4b35bf3..6559857 100644 --- a/app/dashboard/src/types/page.ts +++ b/app/dashboard/src/types/page.ts @@ -1,7 +1,15 @@ import { ChakraIcon } from 'types' -export interface PageType { +export type PageType = { path: string; name: string; itemIcon: ChakraIcon; } + +export interface Pages { + home: PageType; + users: PageType; + services: PageType; + nodes: PageType; + hosts: PageType; +}