diff --git a/app/dashboard/package-lock.json b/app/dashboard/package-lock.json
index 78e7874..76d77e3 100644
--- a/app/dashboard/package-lock.json
+++ b/app/dashboard/package-lock.json
@@ -40,6 +40,7 @@
"react-json-editor-ajrm": "^2.5.14",
"react-loading-skeleton": "^3.1.0",
"react-query": "^3.39.3",
+ "react-resizable-panels": "^2.0.9",
"react-router-dom": "^6.4.3",
"react-slick": "^0.29.0",
"react-use-websocket": "^4.3.1",
@@ -8431,6 +8432,15 @@
}
}
},
+ "node_modules/react-resizable-panels": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.0.9.tgz",
+ "integrity": "sha512-ZylBvs7oG7Y/INWw3oYGolqgpFvoPW8MPeg9l1fURDeKpxrmUuCHBUmPj47BdZ11MODImu3kZYXG85rbySab7w==",
+ "peerDependencies": {
+ "react": "^16.14.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-router": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.3.tgz",
diff --git a/app/dashboard/package.json b/app/dashboard/package.json
index 4d989ce..da5fe22 100644
--- a/app/dashboard/package.json
+++ b/app/dashboard/package.json
@@ -43,6 +43,7 @@
"react-json-editor-ajrm": "^2.5.14",
"react-loading-skeleton": "^3.1.0",
"react-query": "^3.39.3",
+ "react-resizable-panels": "^2.0.9",
"react-router-dom": "^6.4.3",
"react-slick": "^0.29.0",
"react-use-websocket": "^4.3.1",
diff --git a/app/dashboard/public/locales/en.json b/app/dashboard/public/locales/en.json
index b73a0b4..766c039 100644
--- a/app/dashboard/public/locales/en.json
+++ b/app/dashboard/public/locales/en.json
@@ -17,6 +17,12 @@
"services": "Services",
"inbound": "Inbound",
"inbounds": "Inbounds",
+ "sidebar": {
+ "groups": {
+ "management": "Management",
+ "settings": "Settings"
+ }
+ },
"login": {
"login": "Login",
"loginYourAccount": "Login to your account",
diff --git a/app/dashboard/src/App.tsx b/app/dashboard/src/App.tsx
index 7dfc343..1fbd734 100644
--- a/app/dashboard/src/App.tsx
+++ b/app/dashboard/src/App.tsx
@@ -1,53 +1,15 @@
-import { Grid, GridItem } from '@chakra-ui/react';
-import { Footer } from 'components/Footer';
-import { Header } from 'components/Header';
-import { pages, PageRoute } from 'constants/Pages';
-import { Sidebar } from 'components/Sidebar';
-import Login from 'pages/Login';
import 'react-datepicker/dist/react-datepicker.css';
import 'react-loading-skeleton/dist/skeleton.css';
-import { Navigate, Route, Routes } from 'react-router-dom';
-import { fetchAdminLoader } from 'components/modules/Router';
-import { useAdmin } from 'contexts/AdminContext';
+import { RouterProvider } from 'react-router-dom';
+import { FC, Suspense } from 'react';
+import { router } from 'pages/routes';
-function App() {
- const { isLoggedIn } = useAdmin();
+const App: FC = () => {
return (
-
- }
- />
-
-
-
-
-
-
-
-
-
-
-
- {pages.map((route: PageRoute, i) =>
- } loader={fetchAdminLoader} key={i} />
-
- )}
-
-
-
-
-
-
- {!isLoggedIn && }
+
+
+
);
}
diff --git a/app/dashboard/src/components/DeleteServiceModal.tsx b/app/dashboard/src/components/DeleteServiceModal.tsx
deleted file mode 100644
index 3932e15..0000000
--- a/app/dashboard/src/components/DeleteServiceModal.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import {
- Button,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
- Spinner,
- Text,
- useToast,
-} from '@chakra-ui/react';
-import { useServices } from 'contexts/ServicesContext';
-import { FC, useState } from 'react';
-import { Trans, useTranslation } from 'react-i18next';
-import { DeleteIcon } from './Dialog/Icons';
-import { Icon } from './Icon';
-
-export const DeleteServiceModal: FC = () => {
- const { deleteService, refetchServices, deletingService, onDeletingService } = useServices();
- const { t } = useTranslation();
- const toast = useToast();
- const [loading, setLoading] = useState(false);
- const onClose = () => {
- onDeletingService(null);
- };
-
- const onDelete = () => {
- if (deletingService) {
- setLoading(true);
- deleteService(deletingService)
- .then(() => {
- refetchServices();
- toast({
- title: t('deleteService.deleteSuccess', { name: deletingService.name }),
- status: 'success',
- isClosable: true,
- position: 'top',
- duration: 3000,
- });
- })
- .then(onClose)
- .finally(setLoading.bind(null, false));
- }
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- {t('deleteService.title')}
-
- {deletingService && (
-
- }}>
- {t('deleteService.prompt', { name: deletingService.name })}
-
-
- )}
-
-
-
-
-
-
-
- );
-};
diff --git a/app/dashboard/src/components/DeleteUserModal.tsx b/app/dashboard/src/components/DeleteUserModal.tsx
deleted file mode 100644
index 12e6014..0000000
--- a/app/dashboard/src/components/DeleteUserModal.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { DeleteIcon } from 'components/Dialog/Icons';
-import {
- Button,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
- Spinner,
- Text,
- useToast,
-} from '@chakra-ui/react';
-import { FC, useState } from 'react';
-import { Trans, useTranslation } from 'react-i18next';
-import { Icon } from './Icon';
-import { useUsers } from 'contexts/UsersContext';
-
-export type DeleteUserModalProps = {
- deleteCallback?: () => void;
-};
-
-export const DeleteUserModal: FC = () => {
- const [loading, setLoading] = useState(false);
- const { deletingUser: user, onDeletingUser, deleteUser } = useUsers();
- const { t } = useTranslation();
- const toast = useToast();
- const onClose = () => {
- onDeletingUser(null);
- };
- const onDelete = () => {
- if (user) {
- setLoading(true);
- deleteUser(user)
- .then(() => {
- toast({
- title: t('deleteUser.deleteSuccess', { username: user.username }),
- status: 'success',
- isClosable: true,
- position: 'top',
- duration: 3000,
- });
- })
- .then(onClose)
- .finally(setLoading.bind(null, false));
- }
- };
- return (
-
-
-
-
-
-
-
-
-
-
-
- {t('deleteUser.title')}
-
- {user && (
-
- }}>
- {t('deleteUser.prompt', { username: user.username })}
-
-
- )}
-
-
-
- : undefined}
- >
- {t('delete')}
-
-
-
-
- );
-};
diff --git a/app/dashboard/src/components/Footer.tsx b/app/dashboard/src/components/Footer.tsx
index 6ee4317..3dcd9fd 100644
--- a/app/dashboard/src/components/Footer.tsx
+++ b/app/dashboard/src/components/Footer.tsx
@@ -20,7 +20,7 @@ export const Footer: FC = (props) => {
{version ? ` (v${version}), ` : ', '}
Made in{' '}
- khodedawosh
+ khodedawsh
diff --git a/app/dashboard/src/components/NodesUsage.tsx b/app/dashboard/src/components/NodesUsage.tsx
deleted file mode 100644
index c4fa983..0000000
--- a/app/dashboard/src/components/NodesUsage.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import {
- Box,
- CircularProgress,
- HStack,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
- Text,
- VStack,
- chakra,
- useColorMode,
-} from '@chakra-ui/react';
-import { ChartPieIcon } from '@heroicons/react/24/outline';
-import { FilterUsageType } from 'types/Filter';
-import { useNodes } from 'contexts/NodesContext';
-import dayjs from 'dayjs';
-import { FC, Suspense, useEffect, useState } from 'react';
-import ReactApexChart from 'react-apexcharts';
-import { useTranslation } from 'react-i18next';
-import { Icon } from './Icon';
-import { UsageFilter, createUsageConfig } from './UsageFilter';
-
-const UsageIcon = chakra(ChartPieIcon, {
- baseStyle: {
- w: 5,
- h: 5,
- },
-});
-
-export type NodesUsageProps = {};
-
-export const NodesUsage: FC = () => {
- const { isShowingNodesUsage, onShowingNodesUsage } = useNodes();
- const { fetchNodesUsage } = useNodes();
- const { t } = useTranslation();
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [loading, _] = useState(false);
- const { colorMode } = useColorMode();
-
- const usageTitle = t('userDialog.total');
- const [usage, setUsage] = useState(createUsageConfig(colorMode, usageTitle));
- const [usageFilter, setUsageFilter] = useState('1m');
- const fetchUsageWithFilter = (query: FilterUsageType) => {
- fetchNodesUsage(query).then((data: any) => {
- const labels = [];
- const series = [];
- for (const key in data.usages) {
- const entry = data.usages[key];
- series.push(entry.uplink + entry.downlink);
- labels.push(entry.node_name);
- }
- setUsage(createUsageConfig(colorMode, usageTitle, series, labels));
- });
- };
-
- useEffect(() => {
- if (isShowingNodesUsage) {
- fetchUsageWithFilter({
- start: dayjs().utc().subtract(30, 'day').format('YYYY-MM-DDTHH:00:00'),
- });
- }
- }, [isShowingNodesUsage]);
-
- const onClose = () => {
- onShowingNodesUsage(false);
- setUsageFilter('1m');
- };
-
- const disabled = loading;
-
- return (
-
-
-
-
-
-
-
-
-
- {t('header.nodesUsage')}
-
-
-
-
-
-
- {
- setUsageFilter(filter);
- fetchUsageWithFilter(query);
- }}
- />
-
- }>
-
-
-
-
-
-
-
-
- );
-};
diff --git a/app/dashboard/src/components/Sidebar/index.tsx b/app/dashboard/src/components/Sidebar/index.tsx
deleted file mode 100644
index a3de888..0000000
--- a/app/dashboard/src/components/Sidebar/index.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { HamburgerIcon } from '@chakra-ui/icons';
-import { chakra, Image, Flex, VStack, Text } from '@chakra-ui/react'
-import { useDashboard } from 'contexts/DashboardContext';
-import { Link as ReactRouterLink } from 'react-router-dom'
-import { Link as ChakraLink } from '@chakra-ui/react'
-import { pages } from 'constants/Pages';
-import { PageType } from 'types/Page';
-import { Icon } from 'components/Icon';
-import { FC, useEffect, useState } from 'react';
-import { useTranslation } from 'react-i18next';
-
-export type SideBarItemProps = Pick<
- PageType,
- | 'name'
- | 'path'
- | 'itemIcon'
->
-
-export const SidebarMenuIcon = chakra(HamburgerIcon, {
- baseStyle: {
- w: 5,
- h: 5,
- },
-});
-
-export const SidebarItem: FC = ({ itemIcon: ItemIcon, name, path, index, }) => {
- const { t } = useTranslation();
- const { activePage, activatePage } = useDashboard();
- const [active, activate] = useState(index === activePage)
-
- useEffect(() => { activate(index === activePage) }, [active, activePage]);
-
- const darkStyle = { bg: active ? 'primary.900' : 'transparent' };
- const hoverStyle = {
- borderRadius: 10,
- bg: active ? undefined : 'gray.200',
- _dark: {
- bg: 'gray.700'
- },
- transition: 'background-color 0.9s',
- };
-
- return (
- {
- activatePage(index);
- }}
- >
-
-
-
-
-
- {t(name)}
-
-
-
- );
-}
-
-export const Sidebar = () => {
- return (
-
- )
-}
diff --git a/app/dashboard/src/components/DeleteNodeModal.tsx b/app/dashboard/src/components/delete-modal/index.tsx
similarity index 55%
rename from app/dashboard/src/components/DeleteNodeModal.tsx
rename to app/dashboard/src/components/delete-modal/index.tsx
index 566c8bd..449d3a8 100644
--- a/app/dashboard/src/components/DeleteNodeModal.tsx
+++ b/app/dashboard/src/components/delete-modal/index.tsx
@@ -1,3 +1,5 @@
+
+
import {
Button,
Modal,
@@ -9,44 +11,35 @@ import {
ModalOverlay,
Spinner,
Text,
- useToast,
} from '@chakra-ui/react';
-import { useNodes } from 'contexts/NodesContext';
-import { FC, useState } from 'react';
+import { FC } from 'react';
import { Trans, useTranslation } from 'react-i18next';
-import { DeleteIcon } from './Dialog/Icons';
-import { Icon } from './Icon';
+import { DeleteIcon } from 'components/dialog/Icons';
+import { Icon } from 'components/icon';
-export const DeleteNodeModal: FC = () => {
- const { deleteNode, refetchNodes, deletingNode, onDeletingNode } = useNodes();
- const { t } = useTranslation();
- const toast = useToast();
- const [loading, setLoading] = useState(false);
- const onClose = () => {
- onDeletingNode(undefined);
- };
+type DeleteModalProps = {
+ onDelete: () => void,
+ deleteObjectPromptT: string,
+ deleteObjectTitleT: string,
+ deletingObjectName?: string,
+ isOpen: boolean,
+ onClose: () => void,
+ loading: boolean,
+}
- const onDelete = () => {
- if (deletingNode) {
- setLoading(true);
- deleteNode(deletingNode)
- .then(() => {
- refetchNodes();
- toast({
- title: t('deleteNode.deleteSuccess', { name: deletingNode.name }),
- status: 'success',
- isClosable: true,
- position: 'top',
- duration: 3000,
- });
- })
- .then(onClose)
- .finally(setLoading.bind(null, false));
- }
- };
+export const DeleteModal: FC = ({
+ onDelete,
+ isOpen,
+ onClose,
+ deletingObjectName,
+ loading,
+ deleteObjectTitleT,
+ deleteObjectPromptT
+}) => {
+ const { t } = useTranslation();
return (
-
+
@@ -57,9 +50,9 @@ export const DeleteNodeModal: FC = () => {
- {t('deleteNode.title')}
+ {t(deleteObjectTitleT)}
- {deletingNode && (
+ {deletingObjectName && (
{
color="gray.600"
>
}}>
- {t('deleteNode.prompt', { name: deletingNode.name })}
+ {t(deleteObjectPromptT, { name: deletingObjectName })}
)}
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/ModalFooter.tsx b/app/dashboard/src/components/dialog/ModalFooter.tsx
similarity index 100%
rename from app/dashboard/src/components/Dialog/ModalFooter.tsx
rename to app/dashboard/src/components/dialog/ModalFooter.tsx
diff --git a/app/dashboard/src/components/Dialog/ModalHeader.tsx b/app/dashboard/src/components/dialog/ModalHeader.tsx
similarity index 94%
rename from app/dashboard/src/components/Dialog/ModalHeader.tsx
rename to app/dashboard/src/components/dialog/ModalHeader.tsx
index 683bef6..1ee02c5 100644
--- a/app/dashboard/src/components/Dialog/ModalHeader.tsx
+++ b/app/dashboard/src/components/dialog/ModalHeader.tsx
@@ -1,7 +1,7 @@
import { Text, HStack, ModalHeader } from '@chakra-ui/react';
import { ChakraIcon } from 'types/ChakraIcon';
-import { Icon } from '../Icon';
+import { Icon } from '../icon';
export const DialogModalHeader = ({ HeaderIcon, title }: { HeaderIcon: ChakraIcon, title: string }) => {
return (
diff --git a/app/dashboard/src/components/Header.tsx b/app/dashboard/src/components/header/index.tsx
similarity index 70%
rename from app/dashboard/src/components/Header.tsx
rename to app/dashboard/src/components/header/index.tsx
index e9364d0..4f09473 100644
--- a/app/dashboard/src/components/Header.tsx
+++ b/app/dashboard/src/components/header/index.tsx
@@ -13,28 +13,21 @@ import {
import {
ArrowLeftStartOnRectangleIcon,
Bars3Icon,
- ChartPieIcon,
Cog6ToothIcon,
CurrencyDollarIcon,
- DocumentMinusIcon,
- LinkIcon,
- MoonIcon,
- SunIcon,
} from '@heroicons/react/24/outline';
import { pages } from 'constants/Pages';
import { DONATION_URL, REPO_URL } from 'constants/Project';
import { useDashboard } from 'contexts/DashboardContext';
-import { useHosts } from 'contexts/HostsContext';
import { useNodes } from 'contexts/NodesContext';
-import { useUsers } from 'contexts/UsersContext';
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 { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
-import { updateThemeColor } from 'utils/themeColor';
-import { Language } from './Language';
+import { Language } from '../Language';
+import { ThemeToggle } from '../theme-toggler';
type HeaderProps = {
actions?: ReactNode;
@@ -46,15 +39,10 @@ const iconProps = {
},
};
-const DarkIcon = chakra(MoonIcon, iconProps);
-const LightIcon = chakra(SunIcon, iconProps);
const CoreSettingsIcon = chakra(Cog6ToothIcon, iconProps);
const SettingsIcon = chakra(Bars3Icon, iconProps);
const LogoutIcon = chakra(ArrowLeftStartOnRectangleIcon, iconProps);
const DonationIcon = chakra(CurrencyDollarIcon, iconProps);
-const HostsIcon = chakra(LinkIcon, iconProps);
-const NodesUsageIcon = chakra(ChartPieIcon, iconProps);
-const ResetUsageIcon = chakra(DocumentMinusIcon, iconProps);
const NotificationCircle = chakra(Box, {
baseStyle: {
bg: 'yellow.500',
@@ -86,17 +74,8 @@ export const Header: FC = () => {
const {
activePage,
} = useDashboard();
- const {
- onResetAllUsage,
- } = useUsers();
- const {
- onEditingHosts,
- } = useHosts();
- const {
- onShowingNodesUsage,
- } = useNodes();
const { t } = useTranslation();
- const { colorMode, toggleColorMode } = useColorMode();
+ const { colorMode } = useColorMode();
const [showDonationNotif, setShowDonationNotif] = useState(
shouldShowDonation()
);
@@ -139,30 +118,15 @@ export const Header: FC = () => {
position="relative"
>
- }
- onClick={onEditingHosts.bind(null, true)}
- >
- {t('header.hostSettings')}
-
- }
- onClick={onShowingNodesUsage.bind(null, true)}
- >
- {t('header.nodesUsage')}
-
- }
- onClick={onResetAllUsage.bind(null, true)}
- >
- {t('resetAllUsage')}
-
+ {/* NOTE: Host settings trigger */}
+ {/* } */}
+ {/* onClick={onEditingHosts.bind(null, true)} */}
+ {/* > */}
+ {/* {t('header.hostSettings')} */}
+ {/* */}