diff --git a/src/api/constants.ts b/src/api/constants.ts index 6d7e5c2c..a05a26b9 100644 --- a/src/api/constants.ts +++ b/src/api/constants.ts @@ -16,4 +16,5 @@ export const ABOUT_URL = `${API_V1}/about`; // Users Management export const USERS_LIST_URL = `${API_V1}/user`; export const USER_URL = (username: string) => `${USERS_LIST_URL}/${username}`; -export const USER_ROLES_URL = (username: string) => `${USER_URL(username)}/role`; \ No newline at end of file +export const USER_ROLES_URL = (username: string) => `${USER_URL(username)}/role`; +export const USER_PASSWORD_URL = (username: string) => `${USER_URL(username)}/generate-new-password`; \ No newline at end of file diff --git a/src/api/users.ts b/src/api/users.ts index 17a20706..c96d0674 100644 --- a/src/api/users.ts +++ b/src/api/users.ts @@ -1,12 +1,12 @@ import { Axios } from "./axios"; -import { USERS_LIST_URL, USER_ROLES_URL, USER_URL } from "./constants"; +import { USERS_LIST_URL, USER_PASSWORD_URL, USER_ROLES_URL, USER_URL } from "./constants"; export const getUsers = () => { return Axios().get(USERS_LIST_URL); } -export const putUser = (username: string, roles?: object[]) => { - return Axios().put(USER_URL(username), roles ); +export const postUser = (username: string, roles?: object[]) => { + return Axios().post(USER_URL(username), roles ); } export const deleteUser = (username: string) => { @@ -19,4 +19,8 @@ export const putUserRoles = (username: string, roles: object[]) => { export const getUserRoles = (username: string) => { return Axios().get(USER_ROLES_URL(username)); +} + +export const postUserResetPassword = (username: string) => { + return Axios().post(USER_PASSWORD_URL(username)); } \ No newline at end of file diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index b3fa04ac..eca3cd66 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -118,6 +118,7 @@ const Navbar: FC = (props) => { } else if (userSepecficStreams && Boolean(userSepecficStreams.length)) { if (location.pathname === USERS_MANAGEMENT_ROUTE) { handleChangeWithoutRiderection(userSepecficStreams[0].name, location.pathname); + navigate(`/users`); } else { handleChange(userSepecficStreams[0].name); } diff --git a/src/hooks/useGetLogStreamList.tsx b/src/hooks/useGetLogStreamList.tsx index 494d9e29..2aeb27df 100644 --- a/src/hooks/useGetLogStreamList.tsx +++ b/src/hooks/useGetLogStreamList.tsx @@ -6,11 +6,17 @@ import { useEffect } from 'react'; import useMountedState from './useMountedState'; import { notifications } from '@mantine/notifications'; import { IconFileAlert, IconCheck } from '@tabler/icons-react'; +import { useLocalStorage } from '@mantine/hooks'; +import { useNavigate } from 'react-router-dom'; +import { LOGIN_ROUTE } from '@/constants/routes'; export const useGetLogStreamList = () => { const [data, setData] = useMountedState(null); const [error, setError] = useMountedState(null); const [loading, setLoading] = useMountedState(false); + const [, , removeCredentials] = useLocalStorage({ key: 'credentials' }); + const [, , removeUsername] = useLocalStorage({ key: 'username' }); + const navigate = useNavigate(); const getData = async () => { try { @@ -38,23 +44,45 @@ export const useGetLogStreamList = () => { color: 'green', title: 'Streams was loaded', message: 'Successfully Loaded!!', - icon: , + icon: , autoClose: 1000, }); } - if(streams && streams.length===0){ + if (streams && streams.length === 0) { notifications.update({ id: 'load-data', color: 'red', title: 'No Streams', message: 'No Streams Found in your account', - icon: , + icon: , autoClose: 2000, }); } break; } + case StatusCodes.UNAUTHORIZED: { + setError('Unauthorized'); + notifications.update({ + id: 'load-data', + color: 'red', + title: 'Error Occured', + message: 'Unauthorized', + icon: , + autoClose: 2000, + }); + + removeCredentials(); + removeUsername(); + navigate( + { + pathname: LOGIN_ROUTE, + }, + { replace: true }, + ); + + break; + } default: { setError('Failed to get log streams'); notifications.update({ @@ -89,5 +117,5 @@ export const useGetLogStreamList = () => { getData(); }, []); - return { data, error, loading, getData ,resetData}; + return { data, error, loading, getData, resetData }; }; diff --git a/src/hooks/useGetUserRoles.ts b/src/hooks/useGetUserRoles.ts index 45c47946..b6c35a73 100644 --- a/src/hooks/useGetUserRoles.ts +++ b/src/hooks/useGetUserRoles.ts @@ -1,11 +1,17 @@ import { StatusCodes } from 'http-status-codes'; import useMountedState from './useMountedState'; import { getUserRoles } from '@/api/users'; +import { useLocalStorage } from '@mantine/hooks'; +import { useNavigate } from 'react-router-dom'; +import { LOGIN_ROUTE } from '@/constants/routes'; export const useGetUserRole = () => { const [data, setData] = useMountedState(null); const [error, setError] = useMountedState(null); const [loading, setLoading] = useMountedState(false); + const [, , removeCredentials] = useLocalStorage({ key: 'credentials' }); + const [, , removeUsername] = useLocalStorage({ key: 'username' }); + const navigate = useNavigate(); const getRoles = async (userName:string) => { try { @@ -18,6 +24,19 @@ export const useGetUserRole = () => { setData(res.data); break; } + case StatusCodes.UNAUTHORIZED: { + setError('Unauthorized'); + removeCredentials(); + removeUsername(); + navigate( + { + pathname: LOGIN_ROUTE, + }, + { replace: true }, + ); + + break; + } default: { setError('Failed to get Roles'); console.error(res); diff --git a/src/hooks/usePostResetPassword.tsx b/src/hooks/usePostResetPassword.tsx new file mode 100644 index 00000000..37e77b83 --- /dev/null +++ b/src/hooks/usePostResetPassword.tsx @@ -0,0 +1,76 @@ +import { StatusCodes } from 'http-status-codes'; +import useMountedState from './useMountedState'; +import { postUserResetPassword } from '@/api/users'; +import { notifications } from '@mantine/notifications'; +import { IconCheck, IconFileAlert } from '@tabler/icons-react'; + +export const usePostUserResetPassword = () => { + const [data, setData] = useMountedState(null); + const [error, setError] = useMountedState(null); + const [loading, setLoading] = useMountedState(false); + + const resetPasswordUser = async (userName:string) => { + try { + setLoading(true); + notifications.show({ + id: 'load-data', + loading: true, + color: '#545BEB', + title: 'Changing Password', + message: 'Password will be Changed.', + autoClose: false, + withCloseButton: false, + }); + setError(null); + const res = await postUserResetPassword(userName); + + switch (res.status) { + case StatusCodes.OK: { + setData(res.data); + notifications.update({ + id: 'load-data', + color: 'green', + title: 'Password was Changed', + message: 'Successfully Changed!!', + icon: , + autoClose: 3000, + }); + break; + + } + default: { + setError(res.data); + console.error(res); + notifications.update({ + id: 'load-data', + color: 'red', + title: 'Error Occured', + message: res.data, + icon: , + autoClose: 2000, + }); + } + } + } catch(error) { + setError('Failed to get Create User'); + notifications.update({ + id: 'load-data', + color: 'red', + title: 'Error Occured', + message: 'Error Occured while Creating User', + icon: , + autoClose: 2000, + }); + console.error(error); + } finally { + setLoading(false); + } + }; + + const resetData = () => { + setData(null); + }; + + return { data, error, loading, resetPasswordUser, resetData }; +}; + diff --git a/src/hooks/usePutUser.tsx b/src/hooks/usePostUser.tsx similarity index 93% rename from src/hooks/usePutUser.tsx rename to src/hooks/usePostUser.tsx index 8a75270b..97468047 100644 --- a/src/hooks/usePutUser.tsx +++ b/src/hooks/usePostUser.tsx @@ -1,10 +1,10 @@ import { StatusCodes } from 'http-status-codes'; import useMountedState from './useMountedState'; -import { putUser } from '@/api/users'; +import { postUser } from '@/api/users'; import { notifications } from '@mantine/notifications'; import { IconCheck, IconFileAlert } from '@tabler/icons-react'; -export const usePutUser = () => { +export const usePostUser = () => { const [data, setData] = useMountedState(null); const [error, setError] = useMountedState(null); const [loading, setLoading] = useMountedState(false); @@ -22,7 +22,7 @@ export const usePutUser = () => { withCloseButton: false, }); setError(null); - const res = await putUser(userName,roles); + const res = await postUser(userName,roles); switch (res.status) { case StatusCodes.OK: { diff --git a/src/pages/Users/index.tsx b/src/pages/Users/index.tsx index 696968b4..159a1151 100644 --- a/src/pages/Users/index.tsx +++ b/src/pages/Users/index.tsx @@ -1,10 +1,10 @@ -import { Box, Button, Modal, ScrollArea, Select, Table, Text, TextInput} from '@mantine/core'; +import { Box, Button, Modal, ScrollArea, Select, Table, Text, TextInput } from '@mantine/core'; import { useDocumentTitle } from '@mantine/hooks'; import { FC, useEffect, useState } from 'react'; import { useGetUsers } from '@/hooks/useGetUsers'; import { useUsersStyles } from './styles'; -import { usePutUser } from '@/hooks/usePutUser'; +import { usePostUser } from '@/hooks/usePostUser'; import { Prism } from '@mantine/prism'; import RoleTd from './row'; import { useGetLogStreamList } from '@/hooks/useGetLogStreamList'; @@ -15,7 +15,6 @@ const Users: FC = () => { state: { subCreateUserModalTogle }, } = useHeaderContext(); - useEffect(() => { const listener = subCreateUserModalTogle.subscribe(setModalOpen); return () => { @@ -23,7 +22,6 @@ const Users: FC = () => { }; }, [subCreateUserModalTogle.get()]); - const [modalOpen, setModalOpen] = useState(false); const [createUserInput, setCreateUserInput] = useState(''); const [tagInput, setTagInput] = useState(''); @@ -39,7 +37,7 @@ const Users: FC = () => { loading: CreatedUserLoading, createUser, resetData: resetCreateUser, - } = usePutUser(); + } = usePostUser(); const { data: users, error: usersError, loading: usersLoading, getUsersList, resetData: usersReset } = useGetUsers(); const [tableRows, setTableRows] = useState([]); @@ -102,9 +100,7 @@ const Users: FC = () => { }); } if (selectedPrivilege === 'reader' || selectedPrivilege === 'writer') { - if ( - streams?.find((stream) => stream.name === SelectedStream) - ) { + if (streams?.find((stream) => stream.name === SelectedStream)) { if (tagInput !== '' && tagInput !== undefined && selectedPrivilege !== 'writer') { userRole?.push({ privilege: selectedPrivilege, @@ -127,7 +123,7 @@ const Users: FC = () => { }; const createVaildtion = () => { - if (users?.includes(createUserInput) || createUserInput.length <3) { + if (users?.includes(createUserInput) || createUserInput.length < 3) { return true; } if (selectedPrivilege !== '') { @@ -163,7 +159,7 @@ const Users: FC = () => { Reset Password - {tableRows} + {tableRows} = (props) => { const [streamSearchValue, setStreamSearchValue] = useState(''); const { putRole, data: putRoleData, resetData: resetPutRoleData } = usePutUserRole(); - const { data: CreatedUserResponse, error: CreatedUserError, loading: CreatedUserLoading, createUser } = usePutUser(); + const { data: newPassword, error: resetPasswordError, loading: resetPasswordLoading, resetPasswordUser ,resetData:resetNewPassword } = usePostUserResetPassword(); const { data: userRole, error: userRoleError, @@ -83,7 +82,7 @@ const RoleTd: FC = (props) => { const getBadge = (role: any, i: number, withAction: boolean) => { if (role.privilege === 'admin' || role.privilege === 'editor') { return ( - + {role.privilege} ); @@ -92,20 +91,20 @@ const RoleTd: FC = (props) => { if (role.privilege === 'reader') { if (role.resource?.tag) { return ( - + {role.privilege} of {role.resource?.stream === '*' ? 'All' : role.resource?.stream} with{' '} {role.resource?.tag} ); } return ( - + {role.privilege} of {role.resource?.stream === '*' ? 'All' : role.resource?.stream} ); } return ( - + {role.privilege} of {role.resource?.stream === '*' ? 'All' : role.resource?.stream} ); @@ -117,7 +116,11 @@ const RoleTd: FC = (props) => { }); return Badges; } else { - return No Role; + return ( + + No Role + + ); } }; @@ -139,7 +142,7 @@ const RoleTd: FC = (props) => { return role; } }); - putRole(Username,updatedRole ); + putRole(Username, updatedRole); closeDeleteRole(); setDeleteRoleIndex(0); }; @@ -161,7 +164,7 @@ const RoleTd: FC = (props) => { SelectedStream !== '' && SelectedStream !== undefined ) { - if (tagInput !== '' && tagInput !== undefined && selectedPrivilege !== 'writer' && tagInput!==null) { + if (tagInput !== '' && tagInput !== undefined && selectedPrivilege !== 'writer' && tagInput !== null) { userRole?.push({ privilege: selectedPrivilege, resource: { @@ -197,7 +200,6 @@ const RoleTd: FC = (props) => { setStreamSearchValue(''); }; const updateRoleVaildtion = () => { - if (selectedPrivilege === 'admin' || selectedPrivilege === 'editor') { if (userRole?.find((role: any) => role.privilege === selectedPrivilege)) { return true; @@ -210,7 +212,9 @@ const RoleTd: FC = (props) => { (role: any) => role.privilege === selectedPrivilege && role.resource?.stream === SelectedStream && - ( tagInput ? role.resource?.tag === tagInput : (role.resource?.tag === null || role.resource?.tag === undefined )), + (tagInput + ? role.resource?.tag === tagInput + : role.resource?.tag === null || role.resource?.tag === undefined), ) ) { return true; @@ -247,10 +251,11 @@ const RoleTd: FC = (props) => { const handleCloseResetPassword = () => { close(); setUserInput(''); + resetNewPassword(); }; const handleResetPassword = () => { - createUser(UserInput); + resetPasswordUser(UserInput); }; const { classes } = useUsersStyles(); @@ -374,11 +379,11 @@ const RoleTd: FC = (props) => { }} /> - {CreatedUserError ? ( - CreatedUserError - ) : CreatedUserLoading ? ( + {resetPasswordError ? ( + resetPasswordError + ) : resetPasswordLoading ? ( 'Loading' - ) : CreatedUserResponse ? ( + ) : newPassword ? ( <> Password (Warning this is the only time you are able to see Password) @@ -388,7 +393,7 @@ const RoleTd: FC = (props) => { language="markup" copyLabel="Copy password to clipboard" copiedLabel="Password copied to clipboard"> - {CreatedUserResponse} + {newPassword} ) : (