From ac95e4e7dea5e4698eef68e2c3d2988b957ae7f2 Mon Sep 17 00:00:00 2001
From: pwillis77
Date: Thu, 12 Sep 2024 12:40:36 +0000
Subject: [PATCH 1/2] feat(NET-1545): add tour to user management page
---
.../modals/add-user-modal/AddUserModal.tsx | 5 +-
.../invite-user-modal/InviteUserModal.tsx | 17 +-
src/pages/users/CreateNetworkRolePage.tsx | 74 ++-
src/pages/users/CreateUserGroupPage.tsx | 73 ++-
src/pages/users/GroupsPage.tsx | 40 +-
src/pages/users/RolesPage.tsx | 39 +-
src/pages/users/UsersPage.tsx | 473 ++++++++++++++----
7 files changed, 594 insertions(+), 127 deletions(-)
diff --git a/src/components/modals/add-user-modal/AddUserModal.tsx b/src/components/modals/add-user-modal/AddUserModal.tsx
index 2d9b9186..b6425235 100644
--- a/src/components/modals/add-user-modal/AddUserModal.tsx
+++ b/src/components/modals/add-user-modal/AddUserModal.tsx
@@ -33,7 +33,7 @@ interface AddUserModalProps {
addUserButtonRef?: Ref;
addUserNameInputRef?: Ref;
addUserPasswordInputRef?: Ref;
- addUserSetAsAdminCheckboxRef?: Ref;
+ createUserModalPlatformAccessLevelRef?: Ref;
}
type CreateUserForm = User & {
@@ -62,6 +62,7 @@ export default function AddUserModal({
onCancel,
addUserNameInputRef,
addUserPasswordInputRef,
+ createUserModalPlatformAccessLevelRef,
}: AddUserModalProps) {
const [form] = Form.useForm();
const [notify, notifyCtx] = notification.useNotification();
@@ -343,7 +344,7 @@ export default function AddUserModal({
-
+
any;
onCancel?: (e: MouseEvent) => void;
onClose?: () => void;
+ inviteUserModalPlatformAccessLevelRef: Ref;
+ inviteUserModalEmailAddressesInputRef: Ref;
}
interface UserInviteForm {
@@ -70,7 +72,14 @@ const groupsTabKey = 'groups';
const customRolesTabKey = 'custom-roles';
const defaultTabKey = groupsTabKey;
-export default function InviteUserModal({ isOpen, onInviteFinish, onClose, onCancel }: InviteUserModalProps) {
+export default function InviteUserModal({
+ isOpen,
+ onInviteFinish,
+ onClose,
+ onCancel,
+ inviteUserModalPlatformAccessLevelRef,
+ inviteUserModalEmailAddressesInputRef,
+}: InviteUserModalProps) {
const [form] = Form.useForm();
const [notify, notifyCtx] = notification.useNotification();
const { isServerEE } = useServerLicense();
@@ -372,7 +381,7 @@ export default function InviteUserModal({ isOpen, onInviteFinish, onClose, onCan
<>
-
+
-
+
-
+
Role Permissions
@@ -583,14 +635,20 @@ export default function CreateNetworkRolePage(props: PageProps) {
}}
>
-
-
{/* misc */}
+ setIsTourOpen(false)} />
{notifyCtx}
);
diff --git a/src/pages/users/CreateUserGroupPage.tsx b/src/pages/users/CreateUserGroupPage.tsx
index 5013c04c..e97545a8 100644
--- a/src/pages/users/CreateUserGroupPage.tsx
+++ b/src/pages/users/CreateUserGroupPage.tsx
@@ -1,5 +1,5 @@
import { useStore } from '@/store/store';
-import { PlusOutlined } from '@ant-design/icons';
+import { InfoCircleOutlined, PlusOutlined } from '@ant-design/icons';
import {
Button,
Card,
@@ -14,9 +14,11 @@ import {
Table,
TableColumnProps,
theme,
+ Tour,
+ TourProps,
Typography,
} from 'antd';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { PageProps } from '../../models/Page';
import './UsersPage.scss';
import { extractErrorMsg } from '@/utils/ServiceUtils';
@@ -65,6 +67,13 @@ export default function CreateUserGroupPage(props: PageProps) {
const [groupMembers, setGroupMembers] = useState([]);
const [isCreateNetworkRoleModalOpen, setIsCreateNetworkRoleModalOpen] = useState(false);
const [currentNetworkId, setCurrentNetworkId] = useState('');
+ const [isTourOpen, setIsTourOpen] = useState(false);
+
+ const groupNameRef = useRef(null);
+ const groupDescRef = useRef(null);
+ const groupNetworkAccessRef = useRef(null);
+ const groupMembersRef = useRef(null);
+ const createGroupButtonRef = useRef(null);
const filteredMembers = useMemo(() => {
return groupMembers.filter((m) => m.username?.toLowerCase().includes(membersSearch.trim().toLowerCase()));
@@ -199,6 +208,39 @@ export default function CreateUserGroupPage(props: PageProps) {
const platformRoleVal = Form.useWatch('platformRole', metadataForm);
+ const createUserGroupTourSteps: TourProps['steps'] = [
+ {
+ title: 'Group Name',
+ description: 'Set group name',
+ target: () => groupNameRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Group Description',
+ description: 'Set group description',
+ target: () => groupDescRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Associated Network Roles',
+ description: 'Set the network roles for this group',
+ target: () => groupNetworkAccessRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Group Members',
+ description: 'Add group members',
+ target: () => groupMembersRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Create Group',
+ description: 'Click to create group',
+ target: () => createGroupButtonRef.current,
+ placement: 'bottom',
+ },
+ ];
+
useEffect(() => {
setGroupMembers([]);
}, [platformRoleVal]);
@@ -216,7 +258,7 @@ export default function CreateUserGroupPage(props: PageProps) {
{/* top bar */}
-
+
View All Groups
@@ -228,6 +270,16 @@ export default function CreateUserGroupPage(props: PageProps) {
+
+ {
+ setIsTourOpen(true);
+ }}
+ >
+ Start Tour
+
+
@@ -236,7 +288,7 @@ export default function CreateUserGroupPage(props: PageProps) {
-
+
-
+
-
+
Create Group
@@ -351,6 +407,7 @@ export default function CreateUserGroupPage(props: PageProps) {
{/* misc */}
+ setIsTourOpen(false)} />
{notifyCtx}
{/* modals */}
diff --git a/src/pages/users/GroupsPage.tsx b/src/pages/users/GroupsPage.tsx
index e411b701..05589745 100644
--- a/src/pages/users/GroupsPage.tsx
+++ b/src/pages/users/GroupsPage.tsx
@@ -4,7 +4,14 @@ import { AppRoutes } from '@/routes';
import { UsersService } from '@/services/UsersService';
import { getUserGroupRoute, resolveAppRoute } from '@/utils/RouteUtils';
import { useServerLicense } from '@/utils/Utils';
-import { DeleteOutlined, MoreOutlined, PlusOutlined, QuestionCircleOutlined, SearchOutlined } from '@ant-design/icons';
+import {
+ DeleteOutlined,
+ InfoCircleOutlined,
+ MoreOutlined,
+ PlusOutlined,
+ QuestionCircleOutlined,
+ SearchOutlined,
+} from '@ant-design/icons';
import {
Button,
Card,
@@ -19,16 +26,29 @@ import {
Typography,
notification,
} from 'antd';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import { RefObject, useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface GroupPageProps {
users: User[];
triggerDataRefresh?: () => void;
+ setIsTourOpen: (isOpen: boolean) => void;
+ groupsHelpButtonRef: RefObject;
+ groupsTableRef: RefObject;
+ groupsSearchInputRef: RefObject;
+ groupsCreateGroupButtonRef: RefObject;
}
-export default function GroupsPage({ users, triggerDataRefresh }: GroupPageProps) {
+export default function GroupsPage({
+ users,
+ triggerDataRefresh,
+ setIsTourOpen,
+ groupsHelpButtonRef,
+ groupsTableRef,
+ groupsSearchInputRef,
+ groupsCreateGroupButtonRef,
+}: GroupPageProps) {
const [notify, notifyCtx] = notification.useNotification();
const navigate = useNavigate();
const { isServerEE } = useServerLicense();
@@ -221,7 +241,7 @@ export default function GroupsPage({ users, triggerDataRefresh }: GroupPageProps
{!isEmpty && (
<>
-
+
}
+ ref={groupsHelpButtonRef}
/>
+ {
+ setIsTourOpen(true);
+ }}
+ style={{ marginRight: '0.5rem' }}
+ >
+ Start Tour
+
{
navigate(resolveAppRoute(AppRoutes.CREATE_GROUP_ROUTE));
}}
+ ref={groupsCreateGroupButtonRef}
>
Create Group
@@ -262,6 +293,7 @@ export default function GroupsPage({ users, triggerDataRefresh }: GroupPageProps
rowKey="id"
size="small"
scroll={{ x: true }}
+ ref={groupsTableRef}
// rowClassName={(role) => {
// return role.id === selectedRole?.id ? 'selected-row' : '';
// }}
diff --git a/src/pages/users/RolesPage.tsx b/src/pages/users/RolesPage.tsx
index ae6d3878..ab25bd0e 100644
--- a/src/pages/users/RolesPage.tsx
+++ b/src/pages/users/RolesPage.tsx
@@ -5,7 +5,14 @@ import { UsersService } from '@/services/UsersService';
import { getNetworkRoleRoute, getPlatformRoleRoute, resolveAppRoute } from '@/utils/RouteUtils';
import { deriveUserRoleType } from '@/utils/UserMgmtUtils';
import { useServerLicense } from '@/utils/Utils';
-import { DeleteOutlined, MoreOutlined, PlusOutlined, QuestionCircleOutlined, SearchOutlined } from '@ant-design/icons';
+import {
+ DeleteOutlined,
+ InfoCircleOutlined,
+ MoreOutlined,
+ PlusOutlined,
+ QuestionCircleOutlined,
+ SearchOutlined,
+} from '@ant-design/icons';
import {
Button,
Card,
@@ -20,15 +27,27 @@ import {
Typography,
notification,
} from 'antd';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import { RefObject, useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface RolesPageProps {
triggerDataRefresh?: () => void;
+ setIsTourOpen: (isOpen: boolean) => void;
+ networkRolesHelpButtonRef: RefObject;
+ networkRolesTableRef: RefObject;
+ networkRolesSearchInputRef: RefObject;
+ networkRolesCreateRoleButtonRef: RefObject;
}
-export default function RolesPage({ triggerDataRefresh }: RolesPageProps) {
+export default function RolesPage({
+ triggerDataRefresh,
+ setIsTourOpen,
+ networkRolesCreateRoleButtonRef,
+ networkRolesHelpButtonRef,
+ networkRolesSearchInputRef,
+ networkRolesTableRef,
+}: RolesPageProps) {
const [notify, notifyCtx] = notification.useNotification();
const navigate = useNavigate();
const { isServerEE } = useServerLicense();
@@ -246,7 +265,7 @@ export default function RolesPage({ triggerDataRefresh }: RolesPageProps) {
{!isEmpty && (
<>
-
+
}
+ ref={networkRolesHelpButtonRef}
/>
+ {
+ setIsTourOpen(true);
+ }}
+ style={{ marginRight: '0.5rem' }}
+ >
+ Start Tour
+
{/* {
navigate(resolveAppRoute(AppRoutes.CREATE_NETWORK_ROLE_ROUTE));
}}
+ ref={networkRolesCreateRoleButtonRef}
>
Create Network Role
@@ -326,6 +356,7 @@ export default function RolesPage({ triggerDataRefresh }: RolesPageProps) {
},
};
}}
+ ref={networkRolesTableRef}
// rowSelection={{
// type: 'radio',
// hideSelectAll: true,
diff --git a/src/pages/users/UsersPage.tsx b/src/pages/users/UsersPage.tsx
index 13e89d82..d0368df7 100644
--- a/src/pages/users/UsersPage.tsx
+++ b/src/pages/users/UsersPage.tsx
@@ -3,6 +3,7 @@ import {
CheckOutlined,
CopyOutlined,
DeleteOutlined,
+ InfoCircleOutlined,
MoreOutlined,
PlusOutlined,
QuestionCircleOutlined,
@@ -26,6 +27,8 @@ import {
TableColumnsType,
Tabs,
TabsProps,
+ Tour,
+ TourProps,
Typography,
} from 'antd';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -46,6 +49,7 @@ import UserDetailsModal from '@/components/modals/user-details-modal/UserDetails
import InviteUserModal from '@/components/modals/invite-user-modal/InviteUserModal';
import { useNavigate } from 'react-router-dom';
import { AppRoutes } from '@/routes';
+import { set } from 'lodash';
const USERS_DOCS_URL = 'https://docs.netmaker.io/pro/pro-users.html';
@@ -74,8 +78,7 @@ export default function UsersPage(props: PageProps) {
const [isUpdateUserModalOpen, setIsUpdateUserModalOpen] = useState(false);
const [isTransferSuperAdminRightsModalOpen, setIsTransferSuperAdminRightsModalOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
- // const [isTourOpen, setIsTourOpen] = useState(false);
- // const [tourStep, setTourStep] = useState(0);
+ const [isTourOpen, setIsTourOpen] = useState(false);
const [invites, setInvites] = useState([]);
const [isLoadingInvites, setIsLoadingInvites] = useState(true);
const [userInvitesSearch, setUserInvitesSearch] = useState('');
@@ -84,6 +87,7 @@ export default function UsersPage(props: PageProps) {
const [pendingUsers, setPendingUsers] = useState([]);
const [isLoadingPendingUsers, setIsLoadingPendingUsers] = useState(true);
const [pendingUsersSearch, setPendingUsersSearch] = useState('');
+ const [currentTourStep, setCurrentTourStep] = useState(0);
const usersTableRef = useRef(null);
const addUserButtonRef = useRef(null);
@@ -91,7 +95,31 @@ export default function UsersPage(props: PageProps) {
const addUserPasswordInputRef = useRef(null);
const addUserSetAsAdminCheckboxRef = useRef(null);
const denyAllUsersButtonRef = useRef(null);
+ const reloadUsersButtonRef = useRef(null);
+ const usersHelpButtonRef = useRef(null);
+ const searchUsersInputRef = useRef(null);
+ const inviteUserModalEmailAddressesInputRef = useRef(null);
+ const inviteUserModalPlatformAccessLevelRef = useRef(null);
+ const createUserModalPlatformAccessLevelRef = useRef(null);
+ const createUserModalCreateUserButtonRef = useRef(null);
+ const networkRolesHelpButtonRef = useRef(null);
+ const networkRolesTableRef = useRef(null);
+ const networkRolesSearchInputRef = useRef(null);
+ const networkRolesCreateRoleButtonRef = useRef(null);
+ const groupsHelpButtonRef = useRef(null);
+ const groupsTableRef = useRef(null);
+ const groupsSearchInputRef = useRef(null);
+ const groupsCreateGroupButtonRef = useRef(null);
+ const invitesHelpButtonRef = useRef(null);
+ const invitesTableRef = useRef(null);
+ const invitesSearchInputRef = useRef(null);
+ const invitesCreateInviteButtonRef = useRef(null);
+ const invitesClearAllInvitesButtonRef = useRef(null);
+ const pendingUsersHelpButtonRef = useRef(null);
const pendingUsersTableRef = useRef(null);
+ const pendingUsersDenyAllUsersButtonRef = useRef(null);
+ const pendingUsersSearchInputRef = useRef(null);
+ const reloadPendingUsersButtonRef = useRef(null);
const loadUsers = useCallback(
async (showLoading = true) => {
@@ -581,12 +609,244 @@ export default function UsersPage(props: PageProps) {
}
};
+ // tours
+ const nextTourStep = useCallback(() => {
+ setCurrentTourStep(currentTourStep + 1);
+ }, [currentTourStep]);
+
+ const prevTourStep = useCallback(() => {
+ setCurrentTourStep(currentTourStep - 1);
+ }, [currentTourStep]);
+
+ const handleTourOnChange = useCallback(
+ (current: number) => {
+ setCurrentTourStep(current);
+ },
+ [setCurrentTourStep],
+ );
+
+ const usersTabTourSteps: TourProps['steps'] = useMemo(
+ () => [
+ {
+ title: 'Users',
+ description: 'View users and their roles, you can also edit or delete users and transfer super admin rights',
+ target: () => usersTableRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Search Users',
+ description: 'Search for users by username',
+ target: () => searchUsersInputRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Get Help',
+ description: 'Click here to view the documentation for users',
+ target: () => usersHelpButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Reload Users',
+ description: 'Click here to reload users',
+ target: () => reloadUsersButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Add a User',
+ description: 'Click here to add a user either by creating a new user or inviting a user',
+ target: () => addUserButtonRef.current,
+ placement: 'bottom',
+ onNext: () => {
+ setIsAddUserModalOpen(true);
+ nextTourStep();
+ },
+ },
+ {
+ title: 'Username',
+ description: 'Enter a username for the user',
+ target: () => addUserNameInputRef.current,
+ placement: 'bottom',
+ onPrev: () => {
+ setIsAddUserModalOpen(false);
+ prevTourStep();
+ },
+ },
+ {
+ title: 'Password',
+ description: 'Enter a password for the user',
+ target: () => addUserPasswordInputRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Set user user platform access level',
+ description: 'Set the platform access level for the user',
+ target: () => createUserModalPlatformAccessLevelRef.current,
+ placement: 'bottom',
+ onNext: () => {
+ setIsAddUserModalOpen(false);
+ setIsInviteModalOpen(true);
+ nextTourStep();
+ },
+ },
+ {
+ title: 'Invite a User',
+ description: 'Enter email addresses to invite users',
+ target: () => inviteUserModalEmailAddressesInputRef.current,
+ placement: 'bottom',
+ onPrev: () => {
+ setIsAddUserModalOpen(true);
+ setIsInviteModalOpen(false);
+ prevTourStep();
+ },
+ },
+ {
+ title: 'Set user platform access level',
+ description: 'Set the platform access level for the users',
+ target: () => inviteUserModalPlatformAccessLevelRef.current,
+ placement: 'bottom',
+ },
+ ],
+ [nextTourStep, prevTourStep],
+ );
+
+ const networkRolesTabTourSteps: TourProps['steps'] = [
+ {
+ title: 'Network Roles',
+ description: 'View and manage network roles',
+ target: () => networkRolesTableRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Search Network Roles',
+ description: 'Search for network roles by name',
+ target: () => networkRolesSearchInputRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Get Help',
+ description: 'Click here to view the documentation for network roles',
+ target: () => networkRolesHelpButtonRef.current,
+ placement: 'bottom',
+ },
+ // {
+ // title: 'Reload Network Roles',
+ // description: 'Click here to reload network roles',
+ // target: () => reloadUsersButtonRef.current,
+ // placement: 'bottom',
+ // },
+ {
+ title: 'Create a Network Role',
+ description: 'Click here to create a new network role',
+ target: () => networkRolesCreateRoleButtonRef.current,
+ placement: 'bottom',
+ },
+ ];
+
+ const groupsTabTourSteps: TourProps['steps'] = [
+ {
+ title: 'Groups',
+ description: 'View and manage groups',
+ target: () => groupsTableRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Search Groups',
+ description: 'Search for groups by name',
+ target: () => groupsSearchInputRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Get Help',
+ description: 'Click here to view the documentation for groups',
+ target: () => groupsHelpButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Create a Group',
+ description: 'Click here to create a new group',
+ target: () => groupsCreateGroupButtonRef.current,
+ placement: 'bottom',
+ },
+ ];
+
+ const invitesTabTourSteps: TourProps['steps'] = [
+ {
+ title: 'Invites',
+ description: 'View and manage user invites',
+ target: () => invitesTableRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Search Invites',
+ description: 'Search for invites by email',
+ target: () => invitesSearchInputRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Get Help',
+ description: 'Click here to view the documentation for invites',
+ target: () => invitesHelpButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Reload Invites',
+ description: 'Click here to reload invites',
+ target: () => reloadUsersButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Create an Invite',
+ description: 'Click here to create a new invite',
+ target: () => invitesCreateInviteButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Clear All Invites',
+ description: 'Click here to clear all invites',
+ target: () => invitesClearAllInvitesButtonRef.current,
+ placement: 'bottom',
+ },
+ ];
+
+ const pendingUsersTabTourSteps: TourProps['steps'] = [
+ {
+ title: 'Pending Users',
+ description: 'View and manage pending users',
+ target: () => pendingUsersTableRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Search Pending Users',
+ description: 'Search for pending users by username',
+ target: () => pendingUsersSearchInputRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Get Help',
+ description: 'Click here to view the documentation for pending users',
+ target: () => pendingUsersHelpButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Reload Pending Users',
+ description: 'Click here to reload pending users',
+ target: () => reloadPendingUsersButtonRef.current,
+ placement: 'bottom',
+ },
+ {
+ title: 'Deny All Users',
+ description: 'Click here to deny all pending users',
+ target: () => pendingUsersDenyAllUsersButtonRef.current,
+ placement: 'bottom',
+ },
+ ];
+
// ui components
const getUsersContent = useCallback(() => {
return (
<>
-
+
}
style={{ marginRight: '0.5rem' }}
+ ref={usersHelpButtonRef}
/>
- {/* {
setIsTourOpen(true);
- setTourStep(0);
}}
style={{ marginRight: '0.5rem' }}
>
Start Tour
- */}
- loadUsers()} style={{ marginRight: '0.5rem' }}>
+
+ loadUsers()}
+ style={{ marginRight: '0.5rem' }}
+ ref={reloadUsersButtonRef}
+ >
Reload users
{isServerEE && ( // we dont have CE on SaaS
@@ -641,7 +906,12 @@ export default function UsersPage(props: PageProps) {
),
}}
>
-
+
Add a User
@@ -684,7 +954,7 @@ export default function UsersPage(props: PageProps) {
return (
<>
-
+
}
+ ref={invitesHelpButtonRef}
/>
- {/* {
setIsTourOpen(true);
- setTourStep(5);
}}
style={{ marginRight: '0.5em' }}
>
Start Tour
- */}
+
loadInvites()} style={{ marginRight: '0.5em' }}>
Reload invites
Clear All Invites
-
+
Invite User(s)
@@ -761,7 +1037,7 @@ export default function UsersPage(props: PageProps) {
return (
<>
-
+
- {/* {
setIsTourOpen(true);
- setTourStep(5);
}}
style={{ marginRight: '0.5em' }}
>
Start Tour
- */}
- loadPendingUsers()} style={{ marginRight: '0.5em' }}>
+
+ loadPendingUsers()}
+ style={{ marginRight: '0.5em' }}
+ ref={reloadPendingUsersButtonRef}
+ >
Reload users
-
+
Deny all users
}
+ ref={pendingUsersHelpButtonRef}
/>
@@ -841,12 +1127,31 @@ export default function UsersPage(props: PageProps) {
{
key: UsersPageTabs.rolesTabKey,
label: 'Network Roles',
- children: ,
+ children: (
+
+ ),
},
{
key: UsersPageTabs.groupsTabKey,
label: 'Groups',
- children: ,
+ children: (
+
+ ),
},
{
key: UsersPageTabs.invitesTabKey,
@@ -874,75 +1179,22 @@ export default function UsersPage(props: PageProps) {
users,
]);
- // const userTourSteps: TourProps['steps'] = [
- // {
- // title: 'Users',
- // description: 'View users and their roles, you can also edit or delete users and transfer super admin rights',
- // target: () => usersTableRef.current,
- // placement: 'bottom',
- // },
- // {
- // title: 'Add a User',
- // description: 'Click here to add a user',
- // target: () => addUserButtonRef.current,
- // placement: 'bottom',
- // },
- // {
- // title: 'Username',
- // description: 'Enter a username for the user',
- // target: () => addUserNameInputRef.current,
- // placement: 'bottom',
- // },
- // {
- // title: 'Password',
- // description: 'Enter a password for the user',
- // target: () => addUserPasswordInputRef.current,
- // placement: 'bottom',
- // },
- // {
- // title: 'Set as Admin',
- // description: 'Check this box to set the user as admin',
- // target: () => addUserSetAsAdminCheckboxRef.current,
- // placement: 'bottom',
- // },
- // {
- // title: 'Review pending users',
- // description:
- // 'An admin can allow or deny access to accounts that try accessing the server via SSO from this table.',
- // target: () => pendingUsersTableRef.current,
- // placement: 'bottom',
- // },
- // {
- // title: 'Deny all pending users',
- // description: 'A quick way to deny access to all pending users.',
- // target: () => denyAllUsersButtonRef.current,
- // placement: 'bottom',
- // },
- // ];
-
- // const handleTourOnChange = (current: number) => {
- // switch (current) {
- // case 1:
- // setIsAddUserModalOpen(false);
- // break;
- // case 2:
- // setIsAddUserModalOpen(true);
- // break;
- // case 4:
- // setIsAddUserModalOpen(true);
- // setActiveTab(UsersPageTabs.usersTabKey);
- // break;
- // case 5:
- // setIsAddUserModalOpen(false);
- // setActiveTab(UsersPageTabs.invitesTabKey);
- // break;
- // default:
- // break;
- // }
- // setTimeout(() => {
- // setTourStep(current);
- // }, 200);
- // };
+ const tourSteps2 = useMemo(() => {
+ switch (activeTab) {
+ case UsersPageTabs.usersTabKey:
+ return usersTabTourSteps;
+ case UsersPageTabs.rolesTabKey:
+ return networkRolesTabTourSteps;
+ case UsersPageTabs.groupsTabKey:
+ return groupsTabTourSteps;
+ case UsersPageTabs.invitesTabKey:
+ return invitesTabTourSteps;
+ case UsersPageTabs.pendingUsers:
+ return pendingUsersTabTourSteps;
+ default:
+ return usersTabTourSteps;
+ }
+ }, [activeTab, currentTourStep]);
useEffect(() => {
loadUsers();
@@ -1054,7 +1306,32 @@ export default function UsersPage(props: PageProps) {
items={usersTabs}
activeKey={activeTab}
onChange={(tabKey: string) => {
- setActiveTab(tabKey);
+ switch (tabKey) {
+ case UsersPageTabs.usersTabKey:
+ setActiveTab(tabKey);
+ setCurrentTourStep(0);
+ break;
+ case UsersPageTabs.rolesTabKey:
+ setActiveTab(tabKey);
+ setCurrentTourStep(0);
+ break;
+ case UsersPageTabs.groupsTabKey:
+ setActiveTab(tabKey);
+ setCurrentTourStep(0);
+ break;
+ case UsersPageTabs.invitesTabKey:
+ setActiveTab(tabKey);
+ setCurrentTourStep(0);
+ break;
+ case UsersPageTabs.pendingUsers:
+ setActiveTab(tabKey);
+ setCurrentTourStep(0);
+ break;
+ default:
+ setActiveTab(tabKey);
+ setCurrentTourStep(0);
+ break;
+ }
}}
/>
@@ -1063,13 +1340,13 @@ export default function UsersPage(props: PageProps) {
)}
- {/* setIsTourOpen(false)}
+ current={currentTourStep}
onChange={handleTourOnChange}
- current={tourStep}
- /> */}
+ />
{/* misc */}
{notifyCtx}
@@ -1085,7 +1362,7 @@ export default function UsersPage(props: PageProps) {
addUserButtonRef={addUserButtonRef}
addUserNameInputRef={addUserNameInputRef}
addUserPasswordInputRef={addUserPasswordInputRef}
- addUserSetAsAdminCheckboxRef={addUserSetAsAdminCheckboxRef}
+ createUserModalPlatformAccessLevelRef={createUserModalPlatformAccessLevelRef}
/>
{selectedUser && (
)}
From 1849535f683309270993b120d66c010e0ed324e1 Mon Sep 17 00:00:00 2001
From: pwillis77
Date: Mon, 16 Sep 2024 11:51:11 +0000
Subject: [PATCH 2/2] fix: for QA testing
---
src/constants/LinkAndImageConstants.ts | 9 +++++++++
src/pages/users/GroupsPage.tsx | 2 +-
src/pages/users/RolesPage.tsx | 2 +-
src/pages/users/UsersPage.tsx | 14 +++++++++++---
4 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/src/constants/LinkAndImageConstants.ts b/src/constants/LinkAndImageConstants.ts
index 86a4ddec..5aefa567 100644
--- a/src/constants/LinkAndImageConstants.ts
+++ b/src/constants/LinkAndImageConstants.ts
@@ -11,6 +11,12 @@ const ROUTE_LOCAL_NETWORK_TRAFFIC_LINK = 'https://docs.netmaker.io/'; /// NEED T
const HOW_TO_ADD_USERS_TO_NETWORK = 'https://www.netmaker.io/resources/how-to-add-users-to-netmaker-saas';
const USER_MGMT_DOCS_URL = 'https://docs.netmaker.io/#';
const UI_DOCS_URL = 'https://docs.v2.netmaker.io/guide/references/user-interface';
+const USER_MGMT_DOCS_NETWORK_ROLES_URL =
+ 'https://docs.v2.netmaker.io/guide/netmaker-professional/users-in-netmaker-professional#network-roles';
+const USER_MGMT_DOCS_USER_GROUPS_URL =
+ 'https://docs.v2.netmaker.io/guide/netmaker-professional/users-in-netmaker-professional#user-groups';
+const USER_MGMT_DOCS_INVITES_URL =
+ 'https://docs.v2.netmaker.io/guide/netmaker-professional/users-in-netmaker-professional#adding-users__user-invite';
const EGRESS_IMG = '/egress.webp';
const RAG_IMG = '/rag.webp';
@@ -29,6 +35,9 @@ export const ExternalLinks = {
HOW_TO_ADD_USERS_TO_NETWORK,
USER_MGMT_DOCS_URL,
UI_DOCS_URL,
+ USER_MGMT_DOCS_NETWORK_ROLES_URL,
+ USER_MGMT_DOCS_USER_GROUPS_URL,
+ USER_MGMT_DOCS_INVITES_URL,
};
export const AppImages = {
diff --git a/src/pages/users/GroupsPage.tsx b/src/pages/users/GroupsPage.tsx
index 05589745..b19e70c8 100644
--- a/src/pages/users/GroupsPage.tsx
+++ b/src/pages/users/GroupsPage.tsx
@@ -257,7 +257,7 @@ export default function GroupsPage({
title="Go to user management documentation"
size="large"
style={{ marginRight: '1rem' }}
- href={ExternalLinks.USER_MGMT_DOCS_URL}
+ href={ExternalLinks.USER_MGMT_DOCS_USER_GROUPS_URL}
target="_blank"
referrerPolicy="no-referrer"
icon={}
diff --git a/src/pages/users/RolesPage.tsx b/src/pages/users/RolesPage.tsx
index ab25bd0e..2f07cbec 100644
--- a/src/pages/users/RolesPage.tsx
+++ b/src/pages/users/RolesPage.tsx
@@ -281,7 +281,7 @@ export default function RolesPage({
title="Go to user management documentation"
size="large"
style={{ marginRight: '1rem' }}
- href={ExternalLinks.USER_MGMT_DOCS_URL}
+ href={ExternalLinks.USER_MGMT_DOCS_NETWORK_ROLES_URL}
target="_blank"
referrerPolicy="no-referrer"
icon={}
diff --git a/src/pages/users/UsersPage.tsx b/src/pages/users/UsersPage.tsx
index d0368df7..1585aeb2 100644
--- a/src/pages/users/UsersPage.tsx
+++ b/src/pages/users/UsersPage.tsx
@@ -50,6 +50,7 @@ import InviteUserModal from '@/components/modals/invite-user-modal/InviteUserMod
import { useNavigate } from 'react-router-dom';
import { AppRoutes } from '@/routes';
import { set } from 'lodash';
+import { ExternalLinks } from '@/constants/LinkAndImageConstants';
const USERS_DOCS_URL = 'https://docs.netmaker.io/pro/pro-users.html';
@@ -111,6 +112,7 @@ export default function UsersPage(props: PageProps) {
const groupsSearchInputRef = useRef(null);
const groupsCreateGroupButtonRef = useRef(null);
const invitesHelpButtonRef = useRef(null);
+ const invitesReloadButtonRef = useRef(null);
const invitesTableRef = useRef(null);
const invitesSearchInputRef = useRef(null);
const invitesCreateInviteButtonRef = useRef(null);
@@ -791,7 +793,7 @@ export default function UsersPage(props: PageProps) {
{
title: 'Reload Invites',
description: 'Click here to reload invites',
- target: () => reloadUsersButtonRef.current,
+ target: () => invitesReloadButtonRef.current,
placement: 'bottom',
},
{
@@ -968,9 +970,10 @@ export default function UsersPage(props: PageProps) {
}
+ style={{ marginRight: '0.5em' }}
ref={invitesHelpButtonRef}
/>
Start Tour
- loadInvites()} style={{ marginRight: '0.5em' }}>
+ loadInvites()}
+ style={{ marginRight: '0.5em' }}
+ ref={invitesReloadButtonRef}
+ >
Reload invites