From 4c0ce9df0e3ada0a77e947113b586972aa2d6bd2 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 18 Dec 2023 15:37:36 +0530 Subject: [PATCH 01/39] TP-51166 | base implementation , rendering team list implemented --- src/Pages/TeamRevamp/Team.module.scss | 7 ++ .../TeamDetails/TeamDetails.module.scss | 0 src/Pages/TeamRevamp/TeamDetails/index.tsx | 8 ++ .../TeamRevamp/TeamList/TeamList.module.scss | 45 +++++++++++ src/Pages/TeamRevamp/TeamList/index.tsx | 79 +++++++++++++++++++ src/Pages/TeamRevamp/index.tsx | 64 +++++++++++++++ src/Pages/TeamRevamp/partials/Hooks.tsx | 0 src/Pages/TeamRevamp/partials/constants.tsx | 70 ++++++++++++++++ src/assets/TeamIcon.tsx | 33 ++++++++ src/components/LeftNav/index.tsx | 9 ++- src/router.tsx | 6 ++ src/slices/team1Slice.tsx | 25 ++++++ src/store/index.tsx | 2 + 13 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 src/Pages/TeamRevamp/Team.module.scss create mode 100644 src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss create mode 100644 src/Pages/TeamRevamp/TeamDetails/index.tsx create mode 100644 src/Pages/TeamRevamp/TeamList/TeamList.module.scss create mode 100644 src/Pages/TeamRevamp/TeamList/index.tsx create mode 100644 src/Pages/TeamRevamp/index.tsx create mode 100644 src/Pages/TeamRevamp/partials/Hooks.tsx create mode 100644 src/Pages/TeamRevamp/partials/constants.tsx create mode 100644 src/assets/TeamIcon.tsx create mode 100644 src/slices/team1Slice.tsx diff --git a/src/Pages/TeamRevamp/Team.module.scss b/src/Pages/TeamRevamp/Team.module.scss new file mode 100644 index 0000000..92005dc --- /dev/null +++ b/src/Pages/TeamRevamp/Team.module.scss @@ -0,0 +1,7 @@ +.Team-wrapper { + margin: 24px; +} + +.wrapper-class { + display: flex; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx new file mode 100644 index 0000000..adebe11 --- /dev/null +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -0,0 +1,8 @@ +const TeamDetails = () => { + return ( +
+

TeamDetails

+
+ ); +}; +export default TeamDetails; diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss new file mode 100644 index 0000000..d0484bb --- /dev/null +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -0,0 +1,45 @@ +.Team-div { + padding-top: 8px; + padding-bottom: 8px; + border-radius: 8px; + border: 1px solid var(--Grayscale-Border, #e8e8e8); + background: var(--Grayscale-Background-Primary, #fff); + box-shadow: 0px 6px 10px 0px rgba(0, 0, 0, 0.08), + 0px 1px 18px 0px rgba(0, 0, 0, 0.06), 0px 3px 5px 0px rgba(0, 0, 0, 0.1); +} +.search-wrapper { + display: flex; +} +.create-team-btn { + width: 36px; + height: 36px; + padding: 8px; + margin-top: 8px; + margin-left: 8px; +} +.search-input { + width: 234px; + height: 36px; + margin-left: 12px; +} +.select-picker-wrapper { + min-width: 300px; + height: 414px !important; + border: none !important; + box-shadow: none !important; +} +.icon-wrapper { + display: flex; + gap: 8px; +} +.custom-options { + display: flex; + justify-content: space-between; + font-size: small; + margin-bottom: 10px; + padding: 4px; + color: var(--navi-color-gray-c2); +} +.custom-options:hover { + color: var(--navi-color-blue-base); +} diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx new file mode 100644 index 0000000..a783dc6 --- /dev/null +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -0,0 +1,79 @@ +import { RootState } from '@src/store'; +import { useSelector } from 'react-redux'; +import { SelectPicker } from '@navi/web-ui/lib/components'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; +import { BorderedInput, Button } from '@navi/web-ui/lib/primitives'; +import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; +import { useState } from 'react'; +import styles from './TeamList.module.scss'; +import PersonIcon from '@src/assets/PersonIcon'; + +const CutomeTeamOptions = option => { + return ( +
+
{option.label}
+
+
10
+
+ +
+
+
+ ); +}; + +const TeamList = () => { + const data = useSelector((state: RootState) => state.team1.data); + const [input, setInput] = useState(''); + const [selectedTeam, setSelectedTeam] = useState(); + + const options = data.map((item: any) => ({ + label: item.name, + value: item.id, + })); + + const handleInputChange = (e: any) => { + setInput(e.target.value); + }; + const createHandler = () => { + console.log('create team'); + }; + const handleSelectionChange = (val: SelectPickerOptionProps) => { + setSelectedTeam(val); + }; + + return ( +
+

TeamList

+
+
+ } + onChange={handleInputChange} + containerClassName={styles['search-input']} + value={selectedTeam ? selectedTeam.label : ''} + /> + +
+ +
+
+ ); +}; + +export default TeamList; diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx new file mode 100644 index 0000000..dbc69ab --- /dev/null +++ b/src/Pages/TeamRevamp/index.tsx @@ -0,0 +1,64 @@ +import { FC, useEffect, useState, useRef } from 'react'; +import Typography from '@navi/web-ui/lib/primitives/Typography'; +import { FETCH_TEAM_DATA } from '@src/Pages/TeamRevamp/partials/constants'; +import { toast } from '@navi/web-ui/lib/primitives/Toast'; +import FallbackComponent from '@src/components/Fallback'; +import { ApiService } from '@src/services/api'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; + +import styles from './Team.module.scss'; +import Button from '@navi/web-ui/lib/primitives/Button'; +import { AddIcon } from '@navi/web-ui/lib/icons'; +import TeamDetails from './TeamDetails'; +import { useDispatch, useSelector } from 'react-redux'; +import { setTeamData } from '@src/slices/team1Slice'; + +import TeamList from './TeamList'; +import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; + +const TeamRevamp: FC = () => { + const dispatch = useDispatch(); + const [isLoading, setIsLoading] = useState(true); + + const startTeamSearch = (): void => { + const endPoint = FETCH_TEAM_DATA; + setIsLoading(true); + ApiService.get(endPoint) + .then(response => { + setIsLoading(false); + const teams = response?.data?.data ?? []; + dispatch(setTeamData(teams)); + }) + .catch(error => { + const toastMessage = `${ + error?.response?.data?.error?.message + ? `${error?.response?.data?.error?.message}` + : '' + }`; + setIsLoading(false); + toast.error(toastMessage); + }); + }; + + useEffect(() => { + startTeamSearch(); + }, []); + + if (isLoading) { + return ; + } + return ( + +
+ Teams +
+ + +
+
+
+ ); +}; + +export default TeamRevamp; diff --git a/src/Pages/TeamRevamp/partials/Hooks.tsx b/src/Pages/TeamRevamp/partials/Hooks.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/Pages/TeamRevamp/partials/constants.tsx b/src/Pages/TeamRevamp/partials/constants.tsx new file mode 100644 index 0000000..c8f5aa7 --- /dev/null +++ b/src/Pages/TeamRevamp/partials/constants.tsx @@ -0,0 +1,70 @@ +import { createURL } from '@src/services/globalUtils'; + +const URL_PREFIX = createURL('/houston'); + +export const FETCH_TEAM_DATA = `${URL_PREFIX}/teams`; +export const FETCH_ALL_BOTS_DATA = `${URL_PREFIX}/bots`; +export const CREATE_TEAM = `${URL_PREFIX}/teams/add`; + +export const FETCH_SINGLE_TEAM_DATA = (payload: string): string => { + return `${URL_PREFIX}/teams/${payload}`; +}; + +export const UPDATE_TEAM_DATA = (): string => { + return `${URL_PREFIX}/teams`; +}; + +export const REMOVE_TEAM_MEMBER = (teamId: string, userId: string): string => { + return `${URL_PREFIX}/teams/${teamId}/members/${userId}`; +}; + +export const MAKE_MANAGER = (teamId: string, userId: string): string => { + return `${URL_PREFIX}/teams/${teamId}/manager/${userId}`; +}; + +export const regularExpression = /^[a-zA-Z][a-zA-Z0-9_ -]{1,48}[a-zA-Z0-9]$/; + +export const emailRegularExpression = /^[a-zA-Z]+\.[a-zA-Z]+@navi\.com$/; + +export interface TeamsData { + id: string; + name: string; + slackUserIds: Array; + lastUpdatedAt: string; + expand: boolean; +} + +export interface BotsData { + id: string; + name: string; +} + +export interface TeamFormProps { + teamId: string; + isExpanded: boolean; + setLastUpdatedAt: (value: string) => void; +} + +export const slackUserOptions = [ + { + label: 'Email-id', + value: 'WorkEmailIds', + }, + { + label: 'Slack user-id', + value: 'SlackUserIds', + }, +]; + +export const userInputPlaceholders = { + 'Email-id': 'email ids', + 'Slack user-id': 'slack user ids', +}; +export interface CreateTeamProps { + startTeamSearch: (id: number) => void; +} + +export interface PickerOptionProps { + label: string; + value: string; +} diff --git a/src/assets/TeamIcon.tsx b/src/assets/TeamIcon.tsx new file mode 100644 index 0000000..7907d0d --- /dev/null +++ b/src/assets/TeamIcon.tsx @@ -0,0 +1,33 @@ +import { IconProps } from '@navi/web-ui/lib/icons/types'; +import { FC } from 'react'; + +const TeamIcon: FC = () => { + return ( + + + + + + + + + ); +}; + +export default TeamIcon; diff --git a/src/components/LeftNav/index.tsx b/src/components/LeftNav/index.tsx index f8a7e0a..510305c 100644 --- a/src/components/LeftNav/index.tsx +++ b/src/components/LeftNav/index.tsx @@ -10,7 +10,7 @@ import LeadIcon from '@navi/web-ui/lib/icons/LeadIcon'; import { NavItemType } from '@navi/web-ui/lib/components/Navbar/types'; import { Typography } from '@navi/web-ui/lib/primitives'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; - +import TeamIcon from '../../assets/TeamIcon'; import Dialog from '../Dialog'; import styles from './LeftNav.module.scss'; import Footer from '../Footer'; @@ -68,6 +68,13 @@ const LeftNav: React.FC = ({ children }) => { Icon: GroupIcon, handleNavigation: () => navigate('/metrics'), }, + { + itemType: 'simpleNavItem', + label: 'TEAM1', + route: '/Team-Revamp', + Icon: TeamIcon, + handleNavigation: () => navigate('/Team-Revamp'), + }, ]; const returnUserData = () => { diff --git a/src/router.tsx b/src/router.tsx index 3aa4847..0e51486 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -5,6 +5,7 @@ const Incident = lazy(() => import('./Pages/Incidents/index')); const Team = lazy(() => import('./Pages/Team/index')); const Severity = lazy(() => import('./Pages/Severity/index')); const Tableau = lazy(() => import('./Pages/Tableau/index')); +const TeamRevamp = lazy(() => import('./Pages/TeamRevamp/index')); import { CustomRouteObject } from './types'; const routes: CustomRouteObject[] = [ @@ -33,6 +34,11 @@ const routes: CustomRouteObject[] = [ path: '/metrics', element: , }, + { + id: 'TEAM', + path: '/Team-Revamp', + element: , + }, ]; export default routes; diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx new file mode 100644 index 0000000..26312c1 --- /dev/null +++ b/src/slices/team1Slice.tsx @@ -0,0 +1,25 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { TeamsData } from '@src/Pages/Team/constants'; +export interface TeamState { + data: Array; + isLoading: boolean; + modalOpen: boolean; +} +export interface RootState { + team: TeamState; +} +const team1Slice = createSlice({ + name: 'team1', + initialState: { + data: Array(), + }, + + reducers: { + setTeamData: (state, action) => { + state.data = action.payload; + }, + }, +}); + +export const { setTeamData } = team1Slice.actions; +export default team1Slice.reducer; diff --git a/src/store/index.tsx b/src/store/index.tsx index 508e351..5fac750 100644 --- a/src/store/index.tsx +++ b/src/store/index.tsx @@ -2,11 +2,13 @@ import { configureStore } from '@reduxjs/toolkit'; import teamReducer from '../slices/teamSlice'; import severityReducer from '../slices/sevSlice'; import dashboardReducer from '../slices/dashboardSlice'; +import teamReducer1 from '../slices/team1Slice'; const store = configureStore({ reducer: { team: teamReducer, severity: severityReducer, dashboard: dashboardReducer, + team1: teamReducer1, }, }); From a106a414129914ee7cc3766ac70ab56a6c35e4d2 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 18 Dec 2023 19:37:19 +0530 Subject: [PATCH 02/39] TP-51166 | redux thunk and create Team component implemented --- src/Pages/TeamRevamp/TeamList/index.tsx | 13 +- src/Pages/TeamRevamp/index.tsx | 35 ++--- .../CreateTeam/CreateTeam.module.scss | 14 ++ .../partials/CreateTeam/CreateTeam.tsx | 142 ++++++++++++++++++ src/Pages/TeamRevamp/partials/Hooks.tsx | 0 src/Pages/TeamRevamp/partials/constants.tsx | 3 - src/slices/team1Slice.tsx | 58 +++++-- 7 files changed, 221 insertions(+), 44 deletions(-) create mode 100644 src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.module.scss create mode 100644 src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx delete mode 100644 src/Pages/TeamRevamp/partials/Hooks.tsx diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index a783dc6..ab5fc61 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -1,5 +1,5 @@ import { RootState } from '@src/store'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; import { BorderedInput, Button } from '@navi/web-ui/lib/primitives'; @@ -7,7 +7,9 @@ import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; import { useState } from 'react'; import styles from './TeamList.module.scss'; import PersonIcon from '@src/assets/PersonIcon'; - +import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; +import startTeamSearch from '@src/Pages/TeamRevamp/index'; +import { setModalOpen } from '@src/slices/team1Slice'; const CutomeTeamOptions = option => { return (
@@ -23,8 +25,10 @@ const CutomeTeamOptions = option => { }; const TeamList = () => { - const data = useSelector((state: RootState) => state.team1.data); + const dispatch = useDispatch(); + const data = useSelector((state: RootState) => state.team1.teams.data); const [input, setInput] = useState(''); + const [selectedTeam, setSelectedTeam] = useState(); const options = data.map((item: any) => ({ @@ -36,7 +40,7 @@ const TeamList = () => { setInput(e.target.value); }; const createHandler = () => { - console.log('create team'); + dispatch(setModalOpen(true)); }; const handleSelectionChange = (val: SelectPickerOptionProps) => { setSelectedTeam(val); @@ -71,6 +75,7 @@ const TeamList = () => { wrapperClasses={styles['select-picker-wrapper']} customOptionTemplate={CutomeTeamOptions} /> +
); diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index dbc69ab..66be5a5 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -12,40 +12,25 @@ import Button from '@navi/web-ui/lib/primitives/Button'; import { AddIcon } from '@navi/web-ui/lib/icons'; import TeamDetails from './TeamDetails'; import { useDispatch, useSelector } from 'react-redux'; -import { setTeamData } from '@src/slices/team1Slice'; +import { + fetchTeams, + selectSearchTeamData, + setTeamData, +} from '@src/slices/team1Slice'; import TeamList from './TeamList'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; +import { AppDispatch } from '@src/store'; const TeamRevamp: FC = () => { - const dispatch = useDispatch(); - const [isLoading, setIsLoading] = useState(true); - - const startTeamSearch = (): void => { - const endPoint = FETCH_TEAM_DATA; - setIsLoading(true); - ApiService.get(endPoint) - .then(response => { - setIsLoading(false); - const teams = response?.data?.data ?? []; - dispatch(setTeamData(teams)); - }) - .catch(error => { - const toastMessage = `${ - error?.response?.data?.error?.message - ? `${error?.response?.data?.error?.message}` - : '' - }`; - setIsLoading(false); - toast.error(toastMessage); - }); - }; + const dispatch = useDispatch(); + const { isPending } = useSelector(selectSearchTeamData); useEffect(() => { - startTeamSearch(); + dispatch(fetchTeams()); }, []); - if (isLoading) { + if (isPending) { return ; } return ( diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.module.scss b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.module.scss new file mode 100644 index 0000000..fb08d8f --- /dev/null +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.module.scss @@ -0,0 +1,14 @@ +.team-wrapper { + padding: 0px 24px 0px 24px; +} +@mixin team-form-wrapper { + height: medium; + margin-bottom: 20px; + gap: 0 8px; +} +.email-wrapper { + @include team-form-wrapper; +} +.input-wrapper { + @include team-form-wrapper; +} diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx new file mode 100644 index 0000000..2f90aee --- /dev/null +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -0,0 +1,142 @@ +import { useState } from 'react'; +import { Typography } from '@navi/web-ui/lib/primitives'; +import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; +import { BorderedInput, ModalDialog } from '@navi/web-ui/lib/primitives'; +import { toast } from '@navi/web-ui/lib/primitives/Toast'; +import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; +import { ApiService } from '@src/services/api'; +import { + regularExpression, + emailRegularExpression, +} from '@src/Pages/TeamRevamp/partials/constants'; +import { useDispatch, useSelector } from 'react-redux'; +import { + fetchTeams, + selectSearchTeamData, + setModalOpen, +} from '@src/slices/team1Slice'; +import { AppDispatch } from '@src/store'; +import styles from './CreateTeam.module.scss'; +const CreateTeam: React.FC = () => { + const dispatch = useDispatch(); + const [teamName, setTeamName] = useState(''); + const [teamNameError, setTeamNameError] = useState(''); + const [emailError, setEmailError] = useState(''); + const [email, setEmail] = useState(''); + const { modalOpen } = useSelector(selectSearchTeamData); + const validateTeamName = (value: string): void => { + value = value.trim(); + if (!regularExpression.test(value)) { + setTeamNameError( + 'Min. 3 characters required. Use spaces, ‘_’, or ‘-’ only', + ); + } else { + setTeamNameError(''); + } + }; + + const validateEmail = (value: string): void => { + const validEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; + if (validEmail.test(value)) { + if (!emailRegularExpression.test(value)) { + setEmailError('Please enter a Navi email ID'); + } else { + setEmailError(''); + } + } + }; + + const handleTeamNameChange = (e: React.ChangeEvent) => { + const inputValue = e.target.value; + setTeamName(inputValue); + validateTeamName(inputValue); + }; + + const handleEmailChange = (e: React.ChangeEvent) => { + const inputValue = e.target.value; + setEmail(inputValue); + validateEmail(inputValue); + }; + + const addTeamHandler = (): void => { + ApiService.post(CREATE_TEAM, { name: teamName, manager_email: email }) + .then((response: any) => { + const toastMessage = `${response?.data?.data?.message}`; + toast.success(toastMessage); + dispatch(setModalOpen(false)); + setTeamName(''); + setEmail(''); + dispatch(fetchTeams()); + }) + .catch(error => { + const toastMessage = `${ + error?.response?.data?.error?.message + ? `${error?.response?.data?.error?.message}` + : '' + }`; + toast.error('kjnkj'); + }); + }; + + const clearErrors = () => { + dispatch(setModalOpen(false)); + setTeamNameError(''); + setEmailError(''); + setTeamName(''); + setEmail(''); + }; + + return ( +
+ {modalOpen && ( + +
+ + } + placeholder="E.g. NaviPay_Operations" + /> + +
+ +
+ + } + /> + +
+
+ )} +
+ ); +}; +export default CreateTeam; diff --git a/src/Pages/TeamRevamp/partials/Hooks.tsx b/src/Pages/TeamRevamp/partials/Hooks.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/Pages/TeamRevamp/partials/constants.tsx b/src/Pages/TeamRevamp/partials/constants.tsx index c8f5aa7..61ae711 100644 --- a/src/Pages/TeamRevamp/partials/constants.tsx +++ b/src/Pages/TeamRevamp/partials/constants.tsx @@ -60,9 +60,6 @@ export const userInputPlaceholders = { 'Email-id': 'email ids', 'Slack user-id': 'slack user ids', }; -export interface CreateTeamProps { - startTeamSearch: (id: number) => void; -} export interface PickerOptionProps { label: string; diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index 26312c1..0141d1d 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -1,25 +1,59 @@ -import { createSlice } from '@reduxjs/toolkit'; -import { TeamsData } from '@src/Pages/Team/constants'; +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; +import { FETCH_TEAM_DATA, TeamsData } from '@src/Pages/Team/constants'; +import { ApiService } from '@src/services/api'; +import { RootState } from '@src/store'; export interface TeamState { data: Array; - isLoading: boolean; + isPending: boolean; modalOpen: boolean; } -export interface RootState { - team: TeamState; -} + +const initialState = { + teams: { + data: [], + error: '', + isPending: false, + modalOpen: false, + }, +}; + +export const fetchTeams = createAsyncThunk('team/fetchTeams', async () => { + const endPoint = FETCH_TEAM_DATA; + return await ApiService.get(endPoint); +}); + const team1Slice = createSlice({ name: 'team1', - initialState: { - data: Array(), - }, - + initialState, reducers: { setTeamData: (state, action) => { - state.data = action.payload; + state.teams.data = action.payload; }, + setModalOpen: (state, action) => { + state.teams.modalOpen = action.payload; + }, + }, + + extraReducers: builder => { + builder.addCase(fetchTeams.fulfilled, (state, action) => { + state.teams.data = action.payload.data.data; + state.teams.isPending = false; + state.teams.error = ''; + }); + builder.addCase(fetchTeams.rejected, (state, action) => { + state.teams.error = action.error.message ?? 'Someting went wrong'; + state.teams.data = []; + state.teams.isPending = false; + }); + builder.addCase(fetchTeams.pending, (state, action) => { + state.teams.isPending = true; + state.teams.data = []; + state.teams.error = ''; + }); }, }); -export const { setTeamData } = team1Slice.actions; +export const selectSearchTeamData = (state: RootState) => state.team1.teams; + +export const { setTeamData, setModalOpen } = team1Slice.actions; export default team1Slice.reducer; From 0b6e16b46df6f046e138cee19e061c0754a337cb Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 19 Dec 2023 19:08:46 +0530 Subject: [PATCH 03/39] TP-51166 | added default selected and rendering member details --- .../TeamDetails/TeamDetails.module.scss | 7 +++ src/Pages/TeamRevamp/TeamDetails/index.tsx | 46 ++++++++++++++- src/Pages/TeamRevamp/TeamList/index.tsx | 30 ++++++---- src/Pages/TeamRevamp/index.tsx | 12 +--- src/Pages/TeamRevamp/partials/constants.tsx | 19 ++++++ src/slices/team1Slice.tsx | 59 ++++++++++++++++++- 6 files changed, 147 insertions(+), 26 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index e69de29..1a57160 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -0,0 +1,7 @@ +.member-details-wrapper { + display: inline-grid; + grid-template-columns: 0.5fr 0.5fr; +} +.team-details-wrapper { + margin: 55px; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index adebe11..1558d64 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,8 +1,50 @@ +import { selectSearchTeamData } from '@src/slices/team1Slice'; +import { useSelector } from 'react-redux'; +import styles from './TeamDetails.module.scss'; +import PersonIcon from '@src/assets/PersonIcon'; + +import { AppDispatch } from '@src/store'; + const TeamDetails = () => { + const { TeamDetails } = useSelector(selectSearchTeamData); + + const teamMembers = TeamDetails?.participants?.map((item: any) => ({ + name: item.name, + email: item.email, + })); + + const manager = TeamDetails?.participants?.find( + (item: any) => item.id === TeamDetails?.managerId, + ); + return ( -
-

TeamDetails

+
+
+
Slack Details
+
+
+
+
+ +
+ Member Details
+ {teamMembers?.map((item: any) => ( +
+
+
+ +
+
+
{item.name}
+
{item.email}
+ {manager?.name === item.name ? 'Manager' : 'Member'} +
+
+
+ ))} +
); }; + export default TeamDetails; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index ab5fc61..4f66be3 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -1,4 +1,4 @@ -import { RootState } from '@src/store'; +import { AppDispatch, RootState } from '@src/store'; import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; @@ -8,14 +8,15 @@ import { useState } from 'react'; import styles from './TeamList.module.scss'; import PersonIcon from '@src/assets/PersonIcon'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; -import startTeamSearch from '@src/Pages/TeamRevamp/index'; -import { setModalOpen } from '@src/slices/team1Slice'; + +import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; + const CutomeTeamOptions = option => { return (
{option.label}
-
10
+
{10}
@@ -25,10 +26,12 @@ const CutomeTeamOptions = option => { }; const TeamList = () => { - const dispatch = useDispatch(); + const dispatch = useDispatch(); const data = useSelector((state: RootState) => state.team1.teams.data); + const defaultTeam = useSelector( + (state: RootState) => state.team1.teams.data[0], + ); const [input, setInput] = useState(''); - const [selectedTeam, setSelectedTeam] = useState(); const options = data.map((item: any) => ({ @@ -36,14 +39,18 @@ const TeamList = () => { value: item.id, })); - const handleInputChange = (e: any) => { - setInput(e.target.value); + const handleInputChange = (event: React.ChangeEvent) => { + setInput(event.target.value); }; const createHandler = () => { dispatch(setModalOpen(true)); }; - const handleSelectionChange = (val: SelectPickerOptionProps) => { - setSelectedTeam(val); + const fetchTeamData = (val: string) => { + dispatch(fetchTeamDetails(val)); + }; + const handleSelectionChange = (event: SelectPickerOptionProps) => { + setSelectedTeam(event); + fetchTeamData(event.value.toString()); }; return ( @@ -55,7 +62,7 @@ const TeamList = () => { LeftInputAdornment={} onChange={handleInputChange} containerClassName={styles['search-input']} - value={selectedTeam ? selectedTeam.label : ''} + value={selectedTeam?.label ?? defaultTeam?.name ?? ''} />
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 66be5a5..4365e34 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -12,11 +12,7 @@ import Button from '@navi/web-ui/lib/primitives/Button'; import { AddIcon } from '@navi/web-ui/lib/icons'; import TeamDetails from './TeamDetails'; import { useDispatch, useSelector } from 'react-redux'; -import { - fetchTeams, - selectSearchTeamData, - setTeamData, -} from '@src/slices/team1Slice'; +import { fetchDefaultTeamDetails, fetchTeams } from '@src/slices/team1Slice'; import TeamList from './TeamList'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; @@ -24,15 +20,11 @@ import { AppDispatch } from '@src/store'; const TeamRevamp: FC = () => { const dispatch = useDispatch(); - const { isPending } = useSelector(selectSearchTeamData); - useEffect(() => { dispatch(fetchTeams()); + dispatch(fetchDefaultTeamDetails()); }, []); - if (isPending) { - return ; - } return (
diff --git a/src/Pages/TeamRevamp/partials/constants.tsx b/src/Pages/TeamRevamp/partials/constants.tsx index 61ae711..100425e 100644 --- a/src/Pages/TeamRevamp/partials/constants.tsx +++ b/src/Pages/TeamRevamp/partials/constants.tsx @@ -65,3 +65,22 @@ export interface PickerOptionProps { label: string; value: string; } + +interface Participant { + id: string; + name: string; + email: string; + image: string; +} +export interface TeamsDetail { + id?: number; + lastUpdatedAt?: string; + managerId?: string; + name?: string; + oncall?: {}; + participants?: Array; + pse_oncall?: {}; + slackUserIds?: string[]; + webhookSlackChannelId?: string; + webhookSlackChannelName?: string; +} diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index 0141d1d..58a5557 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -1,19 +1,32 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import { FETCH_TEAM_DATA, TeamsData } from '@src/Pages/Team/constants'; +import TeamDetails from '@src/Pages/TeamRevamp/TeamDetails'; +import { + FETCH_SINGLE_TEAM_DATA, + FETCH_TEAM_DATA, + TeamsData, + TeamsDetail, +} from '@src/Pages/TeamRevamp/partials/constants'; import { ApiService } from '@src/services/api'; import { RootState } from '@src/store'; export interface TeamState { data: Array; isPending: boolean; modalOpen: boolean; + TeamDetails: TeamsDetail; + error: string; } -const initialState = { +type TeamInitialState = { + teams: TeamState; +}; + +const initialState: TeamInitialState = { teams: { data: [], error: '', isPending: false, modalOpen: false, + TeamDetails: {}, }, }; @@ -22,6 +35,20 @@ export const fetchTeams = createAsyncThunk('team/fetchTeams', async () => { return await ApiService.get(endPoint); }); +export const fetchTeamDetails = createAsyncThunk( + 'team/fetchTeamDetails', + async (teamId: string) => { + const endPoint = FETCH_SINGLE_TEAM_DATA(teamId); + return await ApiService.get(endPoint); + }, +); +export const fetchDefaultTeamDetails = createAsyncThunk( + 'team/fetchDefaultTeamDetails', + async () => { + const endPoint = FETCH_SINGLE_TEAM_DATA('1'); + return await ApiService.get(endPoint); + }, +); const team1Slice = createSlice({ name: 'team1', initialState, @@ -32,6 +59,12 @@ const team1Slice = createSlice({ setModalOpen: (state, action) => { state.teams.modalOpen = action.payload; }, + setTeamDetails: (state, action) => { + state.teams.TeamDetails = action.payload; + }, + setDefaultDetails: (state, action) => { + state.teams.TeamDetails = action.payload; + }, }, extraReducers: builder => { @@ -50,10 +83,30 @@ const team1Slice = createSlice({ state.teams.data = []; state.teams.error = ''; }); + builder.addCase(fetchTeamDetails.fulfilled, (state, action) => { + state.teams.TeamDetails = action.payload.data.data; + state.teams.isPending = false; + state.teams.error = ''; + }); + builder.addCase(fetchTeamDetails.rejected, (state, action) => { + state.teams.error = action.error.message ?? 'Someting went wrong'; + state.teams.TeamDetails = {}; + state.teams.isPending = false; + }); + builder.addCase(fetchTeamDetails.pending, (state, action) => { + state.teams.isPending = true; + state.teams.TeamDetails = {}; + state.teams.error = ''; + }); + builder.addCase(fetchDefaultTeamDetails.fulfilled, (state, action) => { + state.teams.TeamDetails = action.payload.data.data; + state.teams.isPending = false; + state.teams.error = ''; + }); }, }); export const selectSearchTeamData = (state: RootState) => state.team1.teams; -export const { setTeamData, setModalOpen } = team1Slice.actions; +export const { setTeamData, setModalOpen, setTeamDetails } = team1Slice.actions; export default team1Slice.reducer; From 2a9225d6116001b2b9ebb87a95b9840dedad43f0 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Wed, 20 Dec 2023 15:10:38 +0530 Subject: [PATCH 04/39] TP-51166 | minor type and imports corrections --- src/Pages/TeamRevamp/Team.module.scss | 2 +- src/Pages/TeamRevamp/TeamDetails/index.tsx | 13 +++---- .../TeamRevamp/TeamList/TeamList.module.scss | 6 +-- src/Pages/TeamRevamp/TeamList/index.tsx | 34 ++++++++++++----- src/Pages/TeamRevamp/index.tsx | 37 +++++++++---------- .../partials/CreateTeam/CreateTeam.tsx | 25 ++++++++----- src/Pages/TeamRevamp/partials/constants.tsx | 10 ++--- src/slices/team1Slice.tsx | 33 +++++++++-------- 8 files changed, 89 insertions(+), 71 deletions(-) diff --git a/src/Pages/TeamRevamp/Team.module.scss b/src/Pages/TeamRevamp/Team.module.scss index 92005dc..a703eea 100644 --- a/src/Pages/TeamRevamp/Team.module.scss +++ b/src/Pages/TeamRevamp/Team.module.scss @@ -1,4 +1,4 @@ -.Team-wrapper { +.team-wrapper { margin: 24px; } diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 1558d64..f3ad5ac 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,20 +1,17 @@ -import { selectSearchTeamData } from '@src/slices/team1Slice'; import { useSelector } from 'react-redux'; +import { selectSearchTeamData } from '@src/slices/team1Slice'; import styles from './TeamDetails.module.scss'; import PersonIcon from '@src/assets/PersonIcon'; -import { AppDispatch } from '@src/store'; - const TeamDetails = () => { - const { TeamDetails } = useSelector(selectSearchTeamData); - - const teamMembers = TeamDetails?.participants?.map((item: any) => ({ + const { teamDetails } = useSelector(selectSearchTeamData); + const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, email: item.email, })); - const manager = TeamDetails?.participants?.find( - (item: any) => item.id === TeamDetails?.managerId, + const manager = teamDetails?.participants?.find( + (item: any) => item.id === teamDetails?.managerId, ); return ( diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index d0484bb..b0f8247 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -1,9 +1,9 @@ -.Team-div { +.team-div { padding-top: 8px; padding-bottom: 8px; border-radius: 8px; - border: 1px solid var(--Grayscale-Border, #e8e8e8); - background: var(--Grayscale-Background-Primary, #fff); + border: 1px solid var(--navi-color-gray-border); + background: var(--navi-color-gray-bg-primary); box-shadow: 0px 6px 10px 0px rgba(0, 0, 0, 0.08), 0px 1px 18px 0px rgba(0, 0, 0, 0.06), 0px 3px 5px 0px rgba(0, 0, 0, 0.1); } diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 4f66be3..002a0c4 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -1,15 +1,14 @@ +import { useState } from 'react'; import { AppDispatch, RootState } from '@src/store'; import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; import { BorderedInput, Button } from '@navi/web-ui/lib/primitives'; import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; -import { useState } from 'react'; -import styles from './TeamList.module.scss'; import PersonIcon from '@src/assets/PersonIcon'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; - import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; +import styles from './TeamList.module.scss'; const CutomeTeamOptions = option => { return ( @@ -27,17 +26,22 @@ const CutomeTeamOptions = option => { const TeamList = () => { const dispatch = useDispatch(); - const data = useSelector((state: RootState) => state.team1.teams.data); + const teamList = useSelector((state: RootState) => state.team1.teams.data); + const defaultTeam = useSelector( (state: RootState) => state.team1.teams.data[0], ); + + console.log('defaultTeam', defaultTeam); const [input, setInput] = useState(''); const [selectedTeam, setSelectedTeam] = useState(); - const options = data.map((item: any) => ({ - label: item.name, - value: item.id, - })); + const options = Array.isArray(teamList) + ? teamList.map(item => ({ + label: item.name, + value: item.id, + })) + : []; const handleInputChange = (event: React.ChangeEvent) => { setInput(event.target.value); @@ -56,13 +60,14 @@ const TeamList = () => { return (

TeamList

-
+
} onChange={handleInputChange} containerClassName={styles['search-input']} value={selectedTeam?.label ?? defaultTeam?.name ?? ''} + // value={selectedTeam?.label ?? ''} />
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 4365e34..6f4018f 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -1,33 +1,32 @@ -import { FC, useEffect, useState, useRef } from 'react'; -import Typography from '@navi/web-ui/lib/primitives/Typography'; -import { FETCH_TEAM_DATA } from '@src/Pages/TeamRevamp/partials/constants'; -import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import FallbackComponent from '@src/components/Fallback'; -import { ApiService } from '@src/services/api'; -import useClickStream from '@src/services/clickStream'; -import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; - -import styles from './Team.module.scss'; -import Button from '@navi/web-ui/lib/primitives/Button'; -import { AddIcon } from '@navi/web-ui/lib/icons'; -import TeamDetails from './TeamDetails'; +import { FC, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { fetchDefaultTeamDetails, fetchTeams } from '@src/slices/team1Slice'; - -import TeamList from './TeamList'; +import Typography from '@navi/web-ui/lib/primitives/Typography'; +import { AppDispatch, RootState } from '@src/store'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; -import { AppDispatch } from '@src/store'; +import TeamDetails from './TeamDetails'; +import { + TeamState, + fetchDefaultTeamDetails, + fetchTeamDetails, + fetchTeams, +} from '@src/slices/team1Slice'; +import styles from './Team.module.scss'; +import TeamList from './TeamList'; const TeamRevamp: FC = () => { const dispatch = useDispatch(); + const data = useSelector((state: RootState) => state.team1.teams.data[0]); + useEffect(() => { dispatch(fetchTeams()); - dispatch(fetchDefaultTeamDetails()); }, []); + useEffect(() => { + dispatch(fetchTeamDetails(data?.id.toString())); + }, [[data?.id]]); return ( -
+
Teams
diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index 2f90aee..ed5bac2 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -1,29 +1,33 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { Typography } from '@navi/web-ui/lib/primitives'; import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { BorderedInput, ModalDialog } from '@navi/web-ui/lib/primitives'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; +import { AppDispatch } from '@src/store'; import { ApiService } from '@src/services/api'; import { - regularExpression, - emailRegularExpression, -} from '@src/Pages/TeamRevamp/partials/constants'; -import { useDispatch, useSelector } from 'react-redux'; -import { + fetchDefaultTeamDetails, + fetchTeamDetails, fetchTeams, selectSearchTeamData, setModalOpen, } from '@src/slices/team1Slice'; -import { AppDispatch } from '@src/store'; +import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; +import { + regularExpression, + emailRegularExpression, +} from '@src/Pages/TeamRevamp/partials/constants'; import styles from './CreateTeam.module.scss'; + const CreateTeam: React.FC = () => { const dispatch = useDispatch(); + const { modalOpen } = useSelector(selectSearchTeamData); const [teamName, setTeamName] = useState(''); const [teamNameError, setTeamNameError] = useState(''); const [emailError, setEmailError] = useState(''); const [email, setEmail] = useState(''); - const { modalOpen } = useSelector(selectSearchTeamData); + const validateTeamName = (value: string): void => { value = value.trim(); if (!regularExpression.test(value)) { @@ -67,6 +71,7 @@ const CreateTeam: React.FC = () => { setTeamName(''); setEmail(''); dispatch(fetchTeams()); + dispatch(fetchTeamDetails(response?.data?.data?.id.toString())); }) .catch(error => { const toastMessage = `${ @@ -74,7 +79,7 @@ const CreateTeam: React.FC = () => { ? `${error?.response?.data?.error?.message}` : '' }`; - toast.error('kjnkj'); + toast.error(toastMessage); }); }; diff --git a/src/Pages/TeamRevamp/partials/constants.tsx b/src/Pages/TeamRevamp/partials/constants.tsx index 100425e..f5cea4c 100644 --- a/src/Pages/TeamRevamp/partials/constants.tsx +++ b/src/Pages/TeamRevamp/partials/constants.tsx @@ -27,11 +27,11 @@ export const regularExpression = /^[a-zA-Z][a-zA-Z0-9_ -]{1,48}[a-zA-Z0-9]$/; export const emailRegularExpression = /^[a-zA-Z]+\.[a-zA-Z]+@navi\.com$/; export interface TeamsData { - id: string; - name: string; - slackUserIds: Array; - lastUpdatedAt: string; - expand: boolean; + id?: string; + name?: string; + slackUserIds?: Array; + lastUpdatedAt?: string; + expand?: boolean; } export interface BotsData { diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index 58a5557..ae665e7 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -9,10 +9,10 @@ import { import { ApiService } from '@src/services/api'; import { RootState } from '@src/store'; export interface TeamState { - data: Array; + data: TeamsData; isPending: boolean; modalOpen: boolean; - TeamDetails: TeamsDetail; + teamDetails: TeamsDetail; error: string; } @@ -22,11 +22,11 @@ type TeamInitialState = { const initialState: TeamInitialState = { teams: { - data: [], + data: {}, error: '', isPending: false, modalOpen: false, - TeamDetails: {}, + teamDetails: {}, }, }; @@ -44,11 +44,12 @@ export const fetchTeamDetails = createAsyncThunk( ); export const fetchDefaultTeamDetails = createAsyncThunk( 'team/fetchDefaultTeamDetails', - async () => { - const endPoint = FETCH_SINGLE_TEAM_DATA('1'); + async (id: string) => { + const endPoint = FETCH_SINGLE_TEAM_DATA(id); return await ApiService.get(endPoint); }, ); + const team1Slice = createSlice({ name: 'team1', initialState, @@ -60,10 +61,10 @@ const team1Slice = createSlice({ state.teams.modalOpen = action.payload; }, setTeamDetails: (state, action) => { - state.teams.TeamDetails = action.payload; + state.teams.teamDetails = action.payload; }, setDefaultDetails: (state, action) => { - state.teams.TeamDetails = action.payload; + state.teams.teamDetails = action.payload; }, }, @@ -74,32 +75,32 @@ const team1Slice = createSlice({ state.teams.error = ''; }); builder.addCase(fetchTeams.rejected, (state, action) => { - state.teams.error = action.error.message ?? 'Someting went wrong'; - state.teams.data = []; + state.teams.error = action.error.message ?? 'Something went wrong'; + state.teams.data = {}; state.teams.isPending = false; }); builder.addCase(fetchTeams.pending, (state, action) => { state.teams.isPending = true; - state.teams.data = []; + state.teams.data = {}; state.teams.error = ''; }); builder.addCase(fetchTeamDetails.fulfilled, (state, action) => { - state.teams.TeamDetails = action.payload.data.data; + state.teams.teamDetails = action.payload.data.data; state.teams.isPending = false; state.teams.error = ''; }); builder.addCase(fetchTeamDetails.rejected, (state, action) => { - state.teams.error = action.error.message ?? 'Someting went wrong'; - state.teams.TeamDetails = {}; + state.teams.error = action.error.message ?? 'Something went wrong'; + state.teams.teamDetails = {}; state.teams.isPending = false; }); builder.addCase(fetchTeamDetails.pending, (state, action) => { state.teams.isPending = true; - state.teams.TeamDetails = {}; + state.teams.teamDetails = {}; state.teams.error = ''; }); builder.addCase(fetchDefaultTeamDetails.fulfilled, (state, action) => { - state.teams.TeamDetails = action.payload.data.data; + state.teams.teamDetails = action.payload.data.data; state.teams.isPending = false; state.teams.error = ''; }); From 123c0b9fe59f8f18d1d94fec3dae0c4cb19c65c5 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Thu, 21 Dec 2023 16:16:01 +0530 Subject: [PATCH 05/39] TP-51166 | added slack details and update details --- .../TeamDetails/TeamDetails.module.scss | 31 +++ src/Pages/TeamRevamp/TeamDetails/index.tsx | 243 +++++++++++++++++- .../TeamRevamp/TeamList/TeamList.module.scss | 2 +- src/Pages/TeamRevamp/TeamList/index.tsx | 16 +- src/Pages/TeamRevamp/index.tsx | 16 +- src/Pages/TeamRevamp/partials/Bots.tsx | 23 ++ src/Pages/TeamRevamp/partials/Hooks.tsx | 0 src/Pages/TeamRevamp/partials/constants.tsx | 5 +- 8 files changed, 298 insertions(+), 38 deletions(-) create mode 100644 src/Pages/TeamRevamp/partials/Bots.tsx create mode 100644 src/Pages/TeamRevamp/partials/Hooks.tsx diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 1a57160..ca8b964 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -5,3 +5,34 @@ .team-details-wrapper { margin: 55px; } +.team-input-wrapper { + width: (259px); + height: (36px); +} +.add-member { + margin-top: 55px; + margin-bottom: 24px; +} +.member-div { + margin: 24px 24px 24px 0px; + color: var(--Grayscale-Content-3, #969696); +} +.team-name { + margin: 24px 24px 24px 0px; + color: var(--Grayscale-Content-1, #1c1c1c); +} +.slack-details-wrapper { + display: inline-flex; + gap: 24px; +} +.oncall-selector { + width: 259px; + height: 36px; + align-items: center; +} + +.oncall-selector { + width: 259px; + height: 36px; + align-items: center; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index f3ad5ac..a38ad0f 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,10 +1,35 @@ -import { useSelector } from 'react-redux'; -import { selectSearchTeamData } from '@src/slices/team1Slice'; -import styles from './TeamDetails.module.scss'; +import { useDispatch, useSelector } from 'react-redux'; +import { + fetchTeamDetails, + fetchTeams, + selectSearchTeamData, +} from '@src/slices/team1Slice'; + import PersonIcon from '@src/assets/PersonIcon'; +import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; +import DrawerStyles from '@src/Pages/Incidents/DrawerMode/DrawerMode.module.scss'; +import { useEffect, useState } from 'react'; +import { UPDATE_TEAM_DATA } from '../partials/constants'; +import { ApiService } from '@src/services/api'; +import { toast } from '@navi/web-ui/lib/primitives/Toast'; +import { AppDispatch } from '@src/store'; +import { ConfigProvider, Select, ThemeConfig } from 'antd'; +import { getBots } from '../partials/Bots'; +import styles from './TeamDetails.module.scss'; +import SlackIcon from '@src/assets/SlackIcon'; const TeamDetails = () => { const { teamDetails } = useSelector(selectSearchTeamData); + const teamId = teamDetails?.id; + const dispatch = useDispatch(); + const [emailIds, setEmailIds] = useState(''); + const [oncallHandle, setOncallHandle] = useState( + undefined, + ); + const [slackChannelId, setSlackChannelId] = useState(''); + const [psecOncallHandle, setPsecOncallHandle] = useState( + undefined, + ); const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, email: item.email, @@ -13,18 +38,216 @@ const TeamDetails = () => { const manager = teamDetails?.participants?.find( (item: any) => item.id === teamDetails?.managerId, ); + const handleAddMember = (e: any) => { + setEmailIds(e.target.value); + }; + const addMemberHandler = (): void => { + const endPoint = UPDATE_TEAM_DATA(); + const finalSlackData = emailIds?.includes(',') + ? emailIds.split(',').map(item => item?.trim()) + : [emailIds]; + ApiService.post(endPoint, { + id: teamId, + workEmailIds: finalSlackData, + }) + .then(response => { + toast.success(response?.data?.data); + dispatch(fetchTeams()); + dispatch(fetchTeamDetails(teamId?.toString() || '')); + }) + .catch(error => { + const toastMessage = `${ + error?.response?.data?.error?.message + ? `${error?.response?.data?.error?.message}` + : '' + }`; + toast.error(toastMessage); + }); + }; + const onKeyPressClickHandler = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + addMemberHandler(); + } + setEmailIds(''); + }; + const handleslackChannelId = (e: any) => { + setSlackChannelId(e.target.value); + }; + + const filterOption = (input: string, option): boolean => + (option?.label ?? '').toLowerCase().includes(input.toLowerCase()); + + const dropdownTheme: ThemeConfig = { + token: { + colorTextPlaceholder: 'var(--navi-color-navigation-blue-c2)', + borderRadius: 8, + colorBorder: 'var(--navi-bordered-input-input-wrapper-border-default)', + fontSize: 14, + }, + }; + const returnSlackChannel = () => { + return teamDetails?.webhookSlackChannelName != 'not found' && + teamDetails?.webhookSlackChannelId ? ( + + ) : ( + + - + + ); + }; + const submitHandler = (): void => { + const endPoint = UPDATE_TEAM_DATA(); + ApiService.post(endPoint, { + id: teamId, + webhook_slack_channel: slackChannelId, + on_call_handle: oncallHandle, + pse_on_call_id: psecOncallHandle, + }) + .then(response => { + toast.success(response?.data?.data); + dispatch(fetchTeamDetails(teamId?.toString() || '')); + }) + .catch(error => { + const toastMessage = `${ + error?.response?.data?.error?.message + ? `${error?.response?.data?.error?.message}` + : '' + }`; + toast.error(toastMessage); + }); + }; + + const DEFAULT_TEAM_ONCALL = teamDetails?.oncall?.name + ? `@${teamDetails?.oncall?.name}` + : '@oncall_handle'; + + const DEFAULT_PSEC_ONCALL = + teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' + ? '@' + teamDetails?.pse_oncall?.name + : '@psec_oncall_handle'; + + useEffect(() => { + if (teamDetails?.oncall?.name) { + setOncallHandle(teamDetails?.oncall?.name); + } + }, [teamDetails?.oncall?.name]); + useEffect(() => { + if (teamDetails?.pse_oncall?.name) { + setPsecOncallHandle(teamDetails?.pse_oncall?.name); + } + }, [teamDetails?.pse_oncall?.name]); return (
-
-
Slack Details
-
-
-
+
+ + {teamDetails?.name} + {' '} +
+
+ + SLACK DETAILS + {' '} +
+
+ + Team channel + {' '} + {returnSlackChannel()} + +
+
+ + Team on call + {' '} + + {DEFAULT_TEAM_ONCALL} + + + setPsecOncallHandle(e)} + className={styles['oncall-selector']} + allowClear + value={psecOncallHandle} + /> + +
+
+
+ +
+ + MEMBERS + {' '} +
-
- Member Details
{teamMembers?.map((item: any) => (
diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index b0f8247..656a231 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -24,7 +24,7 @@ } .select-picker-wrapper { min-width: 300px; - height: 414px !important; + min-height: 414px !important; border: none !important; box-shadow: none !important; } diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 002a0c4..1eec424 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -15,7 +15,7 @@ const CutomeTeamOptions = option => {
{option.label}
-
{10}
+
{option?.value}
@@ -27,12 +27,10 @@ const CutomeTeamOptions = option => { const TeamList = () => { const dispatch = useDispatch(); const teamList = useSelector((state: RootState) => state.team1.teams.data); - const defaultTeam = useSelector( (state: RootState) => state.team1.teams.data[0], ); - console.log('defaultTeam', defaultTeam); const [input, setInput] = useState(''); const [selectedTeam, setSelectedTeam] = useState(); @@ -67,7 +65,6 @@ const TeamList = () => { onChange={handleInputChange} containerClassName={styles['search-input']} value={selectedTeam?.label ?? defaultTeam?.name ?? ''} - // value={selectedTeam?.label ?? ''} />
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 6f4018f..97ce532 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -4,15 +4,11 @@ import Typography from '@navi/web-ui/lib/primitives/Typography'; import { AppDispatch, RootState } from '@src/store'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; import TeamDetails from './TeamDetails'; -import { - TeamState, - fetchDefaultTeamDetails, - fetchTeamDetails, - fetchTeams, -} from '@src/slices/team1Slice'; +import { fetchDefaultTeamDetails, fetchTeams } from '@src/slices/team1Slice'; import styles from './Team.module.scss'; import TeamList from './TeamList'; +import { fetchAllBots } from './partials/Bots'; const TeamRevamp: FC = () => { const dispatch = useDispatch(); const data = useSelector((state: RootState) => state.team1.teams.data[0]); @@ -20,10 +16,12 @@ const TeamRevamp: FC = () => { useEffect(() => { dispatch(fetchTeams()); }, []); - useEffect(() => { - dispatch(fetchTeamDetails(data?.id.toString())); - }, [[data?.id]]); + fetchAllBots(); + }, []); + useEffect(() => { + dispatch(fetchDefaultTeamDetails(data?.id.toString())); + }, []); return (
diff --git a/src/Pages/TeamRevamp/partials/Bots.tsx b/src/Pages/TeamRevamp/partials/Bots.tsx new file mode 100644 index 0000000..6a6b5c4 --- /dev/null +++ b/src/Pages/TeamRevamp/partials/Bots.tsx @@ -0,0 +1,23 @@ +import { ApiService } from '@src/services/api'; +import { FETCH_ALL_BOTS_DATA, PickerOptionProps } from './constants'; +import { toast } from '@navi/web-ui/lib/primitives/Toast'; + +let bots: Array = []; + +export const getBots = (): Array => bots; + +export const fetchAllBots = (): void => { + ApiService.get(FETCH_ALL_BOTS_DATA) + .then(response => { + bots = []; + response?.data?.data.map(data => { + bots.push({ + label: '@' + data.name, + value: data.id, + }); + }); + }) + .catch(error => { + toast.error(error?.message); + }); +}; diff --git a/src/Pages/TeamRevamp/partials/Hooks.tsx b/src/Pages/TeamRevamp/partials/Hooks.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/Pages/TeamRevamp/partials/constants.tsx b/src/Pages/TeamRevamp/partials/constants.tsx index f5cea4c..f6abb0c 100644 --- a/src/Pages/TeamRevamp/partials/constants.tsx +++ b/src/Pages/TeamRevamp/partials/constants.tsx @@ -1,5 +1,4 @@ import { createURL } from '@src/services/globalUtils'; - const URL_PREFIX = createURL('/houston'); export const FETCH_TEAM_DATA = `${URL_PREFIX}/teams`; @@ -77,9 +76,9 @@ export interface TeamsDetail { lastUpdatedAt?: string; managerId?: string; name?: string; - oncall?: {}; + oncall?: { id: string; name: string }; participants?: Array; - pse_oncall?: {}; + pse_oncall?: { id: string; name: string }; slackUserIds?: string[]; webhookSlackChannelId?: string; webhookSlackChannelName?: string; From 12bc3c0a58690092e6d609add7a2e51149374054 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Fri, 22 Dec 2023 15:10:58 +0530 Subject: [PATCH 06/39] TP-51166 | created custom components for slack details --- .../TeamDetails/TeamDetails.module.scss | 14 +- src/Pages/TeamRevamp/TeamDetails/index.tsx | 192 ++++++++---------- .../TeamRevamp/TeamList/TeamList.module.scss | 2 +- src/Pages/TeamRevamp/TeamList/index.tsx | 4 +- src/Pages/TeamRevamp/index.tsx | 16 +- .../partials/CreateTeam/CreateTeam.tsx | 9 +- src/assets/ManagerIconFill.tsx | 28 +++ src/assets/PersonIconFill.tsx | 29 +++ src/slices/team1Slice.tsx | 20 +- 9 files changed, 185 insertions(+), 129 deletions(-) create mode 100644 src/assets/ManagerIconFill.tsx create mode 100644 src/assets/PersonIconFill.tsx diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index ca8b964..806dfc7 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -25,14 +25,10 @@ display: inline-flex; gap: 24px; } -.oncall-selector { - width: 259px; - height: 36px; - align-items: center; +.select-picker-wrapper { + min-height: 80px !important; + position: absolute; } - -.oncall-selector { - width: 259px; - height: 36px; - align-items: center; +.on-call-handler { + position: relative; } diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index a38ad0f..deb94eb 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,10 +1,5 @@ import { useDispatch, useSelector } from 'react-redux'; -import { - fetchTeamDetails, - fetchTeams, - selectSearchTeamData, -} from '@src/slices/team1Slice'; - +import { fetchTeamDetails, selectSearchTeamData } from '@src/slices/team1Slice'; import PersonIcon from '@src/assets/PersonIcon'; import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; import DrawerStyles from '@src/Pages/Incidents/DrawerMode/DrawerMode.module.scss'; @@ -13,23 +8,26 @@ import { UPDATE_TEAM_DATA } from '../partials/constants'; import { ApiService } from '@src/services/api'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch } from '@src/store'; -import { ConfigProvider, Select, ThemeConfig } from 'antd'; import { getBots } from '../partials/Bots'; import styles from './TeamDetails.module.scss'; import SlackIcon from '@src/assets/SlackIcon'; +import ManagerIconFill from '@src/assets/ManagerIconFill'; +import { Filter, SelectPicker } from '@navi/web-ui/lib/components'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; const TeamDetails = () => { const { teamDetails } = useSelector(selectSearchTeamData); const teamId = teamDetails?.id; const dispatch = useDispatch(); const [emailIds, setEmailIds] = useState(''); - const [oncallHandle, setOncallHandle] = useState( - undefined, - ); + const [oncallHandle, setOncallHandle] = useState(''); const [slackChannelId, setSlackChannelId] = useState(''); - const [psecOncallHandle, setPsecOncallHandle] = useState( - undefined, - ); + const [psecOncallHandle, setPsecOncallHandle] = useState(''); + const [input, setInput] = useState(''); + const [psecOncall, setPsecOncall] = useState(''); + const [openOnCall, setOpenOnCall] = useState(false); + const [openPsecOnCall, setOpenPsecOnCall] = useState(false); + const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, email: item.email, @@ -52,14 +50,13 @@ const TeamDetails = () => { }) .then(response => { toast.success(response?.data?.data); - dispatch(fetchTeams()); dispatch(fetchTeamDetails(teamId?.toString() || '')); }) .catch(error => { const toastMessage = `${ error?.response?.data?.error?.message ? `${error?.response?.data?.error?.message}` - : '' + : 'Something went wrong,Pls try again later' }`; toast.error(toastMessage); }); @@ -74,46 +71,13 @@ const TeamDetails = () => { const handleslackChannelId = (e: any) => { setSlackChannelId(e.target.value); }; - - const filterOption = (input: string, option): boolean => - (option?.label ?? '').toLowerCase().includes(input.toLowerCase()); - - const dropdownTheme: ThemeConfig = { - token: { - colorTextPlaceholder: 'var(--navi-color-navigation-blue-c2)', - borderRadius: 8, - colorBorder: 'var(--navi-bordered-input-input-wrapper-border-default)', - fontSize: 14, - }, + const handleOncallChange = (event: SelectPickerOptionProps) => { + setOncallHandle(event.value.toString()); }; - const returnSlackChannel = () => { - return teamDetails?.webhookSlackChannelName != 'not found' && - teamDetails?.webhookSlackChannelId ? ( - - ) : ( - - - - - ); + const handlePsecOncall = (event: SelectPickerOptionProps) => { + setPsecOncallHandle(event.value.toString()); }; + const submitHandler = (): void => { const endPoint = UPDATE_TEAM_DATA(); ApiService.post(endPoint, { @@ -125,12 +89,14 @@ const TeamDetails = () => { .then(response => { toast.success(response?.data?.data); dispatch(fetchTeamDetails(teamId?.toString() || '')); + handleCloseOncallPicker(); + handleClosePsecOnCallPicker(); }) .catch(error => { const toastMessage = `${ error?.response?.data?.error?.message ? `${error?.response?.data?.error?.message}` - : '' + : 'Something went wrong,Pls try again later' }`; toast.error(toastMessage); }); @@ -145,16 +111,18 @@ const TeamDetails = () => { ? '@' + teamDetails?.pse_oncall?.name : '@psec_oncall_handle'; - useEffect(() => { - if (teamDetails?.oncall?.name) { - setOncallHandle(teamDetails?.oncall?.name); - } - }, [teamDetails?.oncall?.name]); - useEffect(() => { - if (teamDetails?.pse_oncall?.name) { - setPsecOncallHandle(teamDetails?.pse_oncall?.name); - } - }, [teamDetails?.pse_oncall?.name]); + const handleOpenOncallPicker = () => { + setOpenOnCall(true); + }; + const handleCloseOncallPicker = () => { + setOpenOnCall(false); + }; + const handleOpenPsecOnCallPicker = () => { + setOpenPsecOnCall(true); + }; + const handleClosePsecOnCallPicker = () => { + setOpenPsecOnCall(false); + }; return (
@@ -170,63 +138,65 @@ const TeamDetails = () => {
Team channel - {' '} - {returnSlackChannel()} +
Team on call {' '} - - {DEFAULT_TEAM_ONCALL} - - - setPsecOncall(e.target.value)} + containerClassName={styles['search-input']} + value={DEFAULT_PSEC_ONCALL} + > +
+ {openPsecOnCall && ( + setPsecOncallHandle(e)} - className={styles['oncall-selector']} - allowClear - value={psecOncallHandle} - /> - + onSelectionChange={handlePsecOncall as any} + multiSelect={false} + updateSearch={psecOncall || ''} + wrapperClasses={styles['select-picker-wrapper']} + > + )}
@@ -252,11 +222,21 @@ const TeamDetails = () => {
- + {manager?.name === item.name ? ( + + ) : ( + + )}
-
{item.name}
-
{item.email}
+
+ {' '} + {item.name}{' '} +
+
+ {' '} + {item.email}{' '} +
{manager?.name === item.name ? 'Manager' : 'Member'}
diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index 656a231..c629717 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -24,7 +24,7 @@ } .select-picker-wrapper { min-width: 300px; - min-height: 414px !important; + min-height: calc(100vh - 228px) !important; border: none !important; box-shadow: none !important; } diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 1eec424..f53819a 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { AppDispatch, RootState } from '@src/store'; import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; @@ -15,7 +15,7 @@ const CutomeTeamOptions = option => {
{option.label}
-
{option?.value}
+
{10}
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 97ce532..2007db8 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -4,14 +4,20 @@ import Typography from '@navi/web-ui/lib/primitives/Typography'; import { AppDispatch, RootState } from '@src/store'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; import TeamDetails from './TeamDetails'; -import { fetchDefaultTeamDetails, fetchTeams } from '@src/slices/team1Slice'; +import { + fetchDefaultTeamDetails, + fetchTeamDetails, + fetchTeams, +} from '@src/slices/team1Slice'; import styles from './Team.module.scss'; import TeamList from './TeamList'; import { fetchAllBots } from './partials/Bots'; const TeamRevamp: FC = () => { const dispatch = useDispatch(); - const data = useSelector((state: RootState) => state.team1.teams.data[0]); + const selectedTeam = useSelector( + (state: RootState) => state.team1.teams.selectedTeam, + ); useEffect(() => { dispatch(fetchTeams()); @@ -20,8 +26,10 @@ const TeamRevamp: FC = () => { fetchAllBots(); }, []); useEffect(() => { - dispatch(fetchDefaultTeamDetails(data?.id.toString())); - }, []); + if (selectedTeam) { + dispatch(fetchTeamDetails(selectedTeam?.id)); + } + }, [selectedTeam]); return (
diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index ed5bac2..309283f 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -12,6 +12,7 @@ import { fetchTeams, selectSearchTeamData, setModalOpen, + setSelectedTeam, } from '@src/slices/team1Slice'; import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; import { @@ -66,12 +67,12 @@ const CreateTeam: React.FC = () => { ApiService.post(CREATE_TEAM, { name: teamName, manager_email: email }) .then((response: any) => { const toastMessage = `${response?.data?.data?.message}`; + const teamId = response?.data?.data?.id.toString(); toast.success(toastMessage); - dispatch(setModalOpen(false)); - setTeamName(''); - setEmail(''); + clearErrors(); + dispatch(setSelectedTeam(teamId)); dispatch(fetchTeams()); - dispatch(fetchTeamDetails(response?.data?.data?.id.toString())); + dispatch(fetchTeamDetails(teamId)); }) .catch(error => { const toastMessage = `${ diff --git a/src/assets/ManagerIconFill.tsx b/src/assets/ManagerIconFill.tsx new file mode 100644 index 0000000..b594c29 --- /dev/null +++ b/src/assets/ManagerIconFill.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; +import { IconProps } from './types'; + +const ManagerIconFill: FC = ({ + width = '32', + height = '32', + ...restProps +}) => { + return ( + + + + + ); +}; + +export default ManagerIconFill; diff --git a/src/assets/PersonIconFill.tsx b/src/assets/PersonIconFill.tsx new file mode 100644 index 0000000..c8dec7e --- /dev/null +++ b/src/assets/PersonIconFill.tsx @@ -0,0 +1,29 @@ +import React, { FC, MouseEventHandler, useState } from 'react'; +import { IconProps } from './types'; + +const PersonIconFill: FC = ({ + width = '32', + height = '32', + onClick, + ...restProps +}) => { + return ( + + + + + ); +}; + +export default PersonIconFill; diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index ae665e7..ae085bc 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -8,12 +8,14 @@ import { } from '@src/Pages/TeamRevamp/partials/constants'; import { ApiService } from '@src/services/api'; import { RootState } from '@src/store'; +import { stat } from 'fs'; export interface TeamState { data: TeamsData; isPending: boolean; modalOpen: boolean; teamDetails: TeamsDetail; error: string; + selectedTeam: number | string | any; } type TeamInitialState = { @@ -27,6 +29,7 @@ const initialState: TeamInitialState = { isPending: false, modalOpen: false, teamDetails: {}, + selectedTeam: [], }, }; @@ -63,8 +66,9 @@ const team1Slice = createSlice({ setTeamDetails: (state, action) => { state.teams.teamDetails = action.payload; }, - setDefaultDetails: (state, action) => { - state.teams.teamDetails = action.payload; + + setSelectedTeam: (state, action) => { + state.teams.selectedTeam = action.payload; }, }, @@ -73,16 +77,25 @@ const team1Slice = createSlice({ state.teams.data = action.payload.data.data; state.teams.isPending = false; state.teams.error = ''; + // selectedTeam is set to the first team in the list only when the list teamDeatis is empty + // if (!state.teams.teamDetails) { + // state.teams.selectedTeam = action.payload.data.data[0]; + // } else { + // state.teams.selectedTeam = action.payload.data.data; + // } + state.teams.selectedTeam = action.payload.data.data[0]; }); builder.addCase(fetchTeams.rejected, (state, action) => { state.teams.error = action.error.message ?? 'Something went wrong'; state.teams.data = {}; state.teams.isPending = false; + state.teams.selectedTeam = []; }); builder.addCase(fetchTeams.pending, (state, action) => { state.teams.isPending = true; state.teams.data = {}; state.teams.error = ''; + state.teams.selectedTeam = []; }); builder.addCase(fetchTeamDetails.fulfilled, (state, action) => { state.teams.teamDetails = action.payload.data.data; @@ -109,5 +122,6 @@ const team1Slice = createSlice({ export const selectSearchTeamData = (state: RootState) => state.team1.teams; -export const { setTeamData, setModalOpen, setTeamDetails } = team1Slice.actions; +export const { setTeamData, setModalOpen, setTeamDetails, setSelectedTeam } = + team1Slice.actions; export default team1Slice.reducer; From 5c78dd54f5a3b8169e553709e17f6266de066c0e Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 26 Dec 2023 16:13:19 +0530 Subject: [PATCH 07/39] TP-51166 | tooltip for channel and channel details added --- .../TeamDetails/TeamDetails.module.scss | 40 ++++- src/Pages/TeamRevamp/TeamDetails/index.tsx | 148 ++++++++++++++---- .../TeamRevamp/TeamList/TeamList.module.scss | 5 + src/Pages/TeamRevamp/TeamList/index.tsx | 18 ++- 4 files changed, 172 insertions(+), 39 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 806dfc7..69546cf 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -6,8 +6,8 @@ margin: 55px; } .team-input-wrapper { - width: (259px); - height: (36px); + min-width: (320px); + height: (38px); } .add-member { margin-top: 55px; @@ -32,3 +32,39 @@ .on-call-handler { position: relative; } +.change-manger-dropdown { + display: flex; +} +.arrow-icon { + width: 7px; + height: 7px; + padding-left: 10px; + margin-top: 5px; +} +.manager-picker-wrapper { + min-width: 103px; + min-height: 88px; + padding: 8px, 0px, 8px, 0px; +} +.search-input { + min-width: 320px !important; +} +.info-icon { + height: 20px; + width: 20px; + margin-left: 4px; + margin-top: 5px; + display: flex; + align-items: center; + :hover { + cursor: pointer; + } +} + +.team-name-wrapper { + display: flex; + align-items: center; +} +.update-details { + margin-left: 900px; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index deb94eb..1a33bad 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,10 +1,18 @@ import { useDispatch, useSelector } from 'react-redux'; import { fetchTeamDetails, selectSearchTeamData } from '@src/slices/team1Slice'; import PersonIcon from '@src/assets/PersonIcon'; -import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; +import { + BorderedInput, + Button, + Tooltip, + Typography, +} from '@navi/web-ui/lib/primitives'; import DrawerStyles from '@src/Pages/Incidents/DrawerMode/DrawerMode.module.scss'; import { useEffect, useState } from 'react'; -import { UPDATE_TEAM_DATA } from '../partials/constants'; +import { + FETCH_SINGLE_TEAM_DATA, + UPDATE_TEAM_DATA, +} from '../partials/constants'; import { ApiService } from '@src/services/api'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch } from '@src/store'; @@ -14,17 +22,30 @@ import SlackIcon from '@src/assets/SlackIcon'; import ManagerIconFill from '@src/assets/ManagerIconFill'; import { Filter, SelectPicker } from '@navi/web-ui/lib/components'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; +import { + AlertOutlineIcon, + ArrowDownIcon, + ArrowDownSolidIcon, +} from '@navi/web-ui/lib/icons'; const TeamDetails = () => { const { teamDetails } = useSelector(selectSearchTeamData); const teamId = teamDetails?.id; const dispatch = useDispatch(); const [emailIds, setEmailIds] = useState(''); - const [oncallHandle, setOncallHandle] = useState(''); const [slackChannelId, setSlackChannelId] = useState(''); - const [psecOncallHandle, setPsecOncallHandle] = useState(''); + const [oncall, setOncall] = useState({ + label: '', + value: '', + }); + + const [psecOncall, setPsecOncallValue] = useState({ + label: '', + value: '', + }); const [input, setInput] = useState(''); - const [psecOncall, setPsecOncall] = useState(''); + const [psecInput, setPsecInput] = useState(''); + const [openOnCall, setOpenOnCall] = useState(false); const [openPsecOnCall, setOpenPsecOnCall] = useState(false); @@ -36,6 +57,11 @@ const TeamDetails = () => { const manager = teamDetails?.participants?.find( (item: any) => item.id === teamDetails?.managerId, ); + + useEffect(() => { + dispatch(fetchTeamDetails(teamId?.toString() || '')); + }, []); + const handleAddMember = (e: any) => { setEmailIds(e.target.value); }; @@ -72,10 +98,18 @@ const TeamDetails = () => { setSlackChannelId(e.target.value); }; const handleOncallChange = (event: SelectPickerOptionProps) => { - setOncallHandle(event.value.toString()); + setOncall({ + label: event.label, + value: event.value.toString(), + }); + setOpenOnCall(false); }; const handlePsecOncall = (event: SelectPickerOptionProps) => { - setPsecOncallHandle(event.value.toString()); + setPsecOncallValue({ + label: event.label, + value: event.value.toString(), + }); + setOpenPsecOnCall(false); }; const submitHandler = (): void => { @@ -83,14 +117,14 @@ const TeamDetails = () => { ApiService.post(endPoint, { id: teamId, webhook_slack_channel: slackChannelId, - on_call_handle: oncallHandle, - pse_on_call_id: psecOncallHandle, + on_call_handle: oncall.value, + pse_on_call_id: psecOncall.value, }) .then(response => { toast.success(response?.data?.data); dispatch(fetchTeamDetails(teamId?.toString() || '')); - handleCloseOncallPicker(); - handleClosePsecOnCallPicker(); + setOpenOnCall(false); + setOpenPsecOnCall(false); }) .catch(error => { const toastMessage = `${ @@ -111,17 +145,12 @@ const TeamDetails = () => { ? '@' + teamDetails?.pse_oncall?.name : '@psec_oncall_handle'; - const handleOpenOncallPicker = () => { - setOpenOnCall(true); - }; - const handleCloseOncallPicker = () => { - setOpenOnCall(false); + const handleOncallPicker = () => { + setOpenOnCall(!openOnCall); }; + const handleOpenPsecOnCallPicker = () => { - setOpenPsecOnCall(true); - }; - const handleClosePsecOnCallPicker = () => { - setOpenPsecOnCall(false); + setOpenPsecOnCall(!openPsecOnCall); }; return (
@@ -135,17 +164,36 @@ const TeamDetails = () => { SLACK DETAILS {' '}
-
- - Team channel - +
+
+ + Team channel + +
+ + + +
+
@@ -154,7 +202,7 @@ const TeamDetails = () => { {' '}
{ className={styles['on-call-handler']} > setPsecOncall(e.target.value)} + onChange={e => setPsecInput(e.target.value)} containerClassName={styles['search-input']} value={DEFAULT_PSEC_ONCALL} > @@ -193,16 +241,18 @@ const TeamDetails = () => { options={getBots()} onSelectionChange={handlePsecOncall as any} multiSelect={false} - updateSearch={psecOncall || ''} + updateSearch={psecInput || ''} wrapperClasses={styles['select-picker-wrapper']} > )}
- +
+ +
MEMBERS @@ -217,7 +267,7 @@ const TeamDetails = () => { containerClassName={styles['team-input-wrapper']} />
-
+ {/*
{teamMembers?.map((item: any) => (
@@ -236,13 +286,43 @@ const TeamDetails = () => {
{' '} {item.email}{' '} +
*/} + {/*
openState(item.name)} + > + + {manager?.name === item.name ? 'Manager' : 'Member'} + {' '} +
- {manager?.name === item.name ? 'Manager' : 'Member'} + {manager?.name !== item.name && ( +
+ {openMember === item.name && ( + + )} +
+ )}
))} -
+
*/}
); }; diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index c629717..4963c0c 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -43,3 +43,8 @@ .custom-options:hover { color: var(--navi-color-blue-base); } +.search-results { + margin-left: 27px; + margin-top: 12px; + color: var(--navi-color-gray-c3); +} diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index f53819a..01f3505 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -1,9 +1,9 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { AppDispatch, RootState } from '@src/store'; import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; -import { BorderedInput, Button } from '@navi/web-ui/lib/primitives'; +import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; import PersonIcon from '@src/assets/PersonIcon'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; @@ -17,7 +17,7 @@ const CutomeTeamOptions = option => {
{10}
- +
@@ -53,6 +53,7 @@ const TeamList = () => { const handleSelectionChange = (event: SelectPickerOptionProps) => { setSelectedTeam(event); fetchTeamData(event.value.toString()); + setInput(''); }; return ( @@ -76,6 +77,16 @@ const TeamList = () => { {''}
+ {input && !selectedTeam && ( +
+ {' '} + + {' '} + SEARCH RESULTS{' '} + +
+ )} + { customOptionTemplate={CutomeTeamOptions} selectedValue={selectedTeam?.value ?? defaultTeam?.id ?? ''} /> +
From 049b97ad63991cac16ed683d709ea8ddad73c934 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Wed, 27 Dec 2023 13:50:00 +0530 Subject: [PATCH 08/39] TP-45116 | bug fixed for slack details --- src/Pages/TeamRevamp/TeamDetails/index.tsx | 63 +++++++++++++++------- src/Pages/TeamRevamp/TeamList/index.tsx | 29 ++++++++-- src/slices/team1Slice.tsx | 2 +- 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 1a33bad..2457ac3 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -8,7 +8,7 @@ import { Typography, } from '@navi/web-ui/lib/primitives'; import DrawerStyles from '@src/Pages/Incidents/DrawerMode/DrawerMode.module.scss'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { FETCH_SINGLE_TEAM_DATA, UPDATE_TEAM_DATA, @@ -27,9 +27,11 @@ import { ArrowDownIcon, ArrowDownSolidIcon, } from '@navi/web-ui/lib/icons'; +import { TeamState } from '@src/slices/team1Slice'; const TeamDetails = () => { - const { teamDetails } = useSelector(selectSearchTeamData); + const teamData: TeamState = useSelector(selectSearchTeamData); + const { teamDetails } = teamData; const teamId = teamDetails?.id; const dispatch = useDispatch(); const [emailIds, setEmailIds] = useState(''); @@ -45,10 +47,14 @@ const TeamDetails = () => { }); const [input, setInput] = useState(''); const [psecInput, setPsecInput] = useState(''); - const [openOnCall, setOpenOnCall] = useState(false); const [openPsecOnCall, setOpenPsecOnCall] = useState(false); - + const [search, setSearch] = useState(''); + const searchRef = useRef(null); + const [openMember, setOpenMember] = useState(''); + const openState = (name: string) => { + setOpenMember(name); + }; const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, email: item.email, @@ -59,8 +65,9 @@ const TeamDetails = () => { ); useEffect(() => { - dispatch(fetchTeamDetails(teamId?.toString() || '')); - }, []); + setInput(''); + setPsecInput(''); + }, [teamData]); const handleAddMember = (e: any) => { setEmailIds(e.target.value); @@ -102,6 +109,8 @@ const TeamDetails = () => { label: event.label, value: event.value.toString(), }); + setInput(event?.label); + // setInput(''); setOpenOnCall(false); }; const handlePsecOncall = (event: SelectPickerOptionProps) => { @@ -109,6 +118,7 @@ const TeamDetails = () => { label: event.label, value: event.value.toString(), }); + setPsecInput(event?.label); setOpenPsecOnCall(false); }; @@ -138,7 +148,9 @@ const TeamDetails = () => { const DEFAULT_TEAM_ONCALL = teamDetails?.oncall?.name ? `@${teamDetails?.oncall?.name}` - : '@oncall_handle'; + : ''; + + const DEFAULT_TEAM_ONCALL_ID = teamDetails?.oncall?.id || ''; const DEFAULT_PSEC_ONCALL = teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' @@ -206,17 +218,27 @@ const TeamDetails = () => { className={styles['on-call-handler']} > setInput(e.target.value)} + onChange={e => { + // eslint-disable-next-line + searchRef.current = true; + setInput(e.target.value); + }} + placeholder="@oncall_handle" + onBlur={() => { + // eslint-disable-next-line + searchRef.current = false; + }} containerClassName={styles['search-input']} - value={DEFAULT_TEAM_ONCALL} + value={input || DEFAULT_TEAM_ONCALL} >
{openOnCall && ( )} @@ -233,7 +255,7 @@ const TeamDetails = () => { setPsecInput(e.target.value)} containerClassName={styles['search-input']} - value={DEFAULT_PSEC_ONCALL} + value={psecInput || DEFAULT_PSEC_ONCALL} >
{openPsecOnCall && ( @@ -267,7 +289,7 @@ const TeamDetails = () => { containerClassName={styles['team-input-wrapper']} />
- {/*
+
{teamMembers?.map((item: any) => (
@@ -286,8 +308,8 @@ const TeamDetails = () => {
{' '} {item.email}{' '} -
*/} - {/*
+
openState(item.name)} > @@ -310,10 +332,15 @@ const TeamDetails = () => { value: '2', }, ]} - onSelectionChange={handleOncallChange as any} - multiSelect={false} - updateSearch={input || ''} wrapperClasses={styles['manager-picker-wrapper']} + onSelectionChange={function ( + val: + | SelectPickerOptionProps + | SelectPickerOptionProps[], + ): void { + throw new Error('Function not implemented.'); + }} + multiSelect={false} > )}
@@ -322,7 +349,7 @@ const TeamDetails = () => {
))} -
*/} +
); }; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 01f3505..5d0f97a 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -10,9 +10,9 @@ import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; import styles from './TeamList.module.scss'; -const CutomeTeamOptions = option => { +const CutomeTeamOptions = (option: SelectPickerOptionProps) => { return ( -
+
{option.label}
{10}
@@ -33,6 +33,15 @@ const TeamList = () => { const [input, setInput] = useState(''); const [selectedTeam, setSelectedTeam] = useState(); + const scroll = useRef(false); + + const scrollintoView = (event: string): void => { + if (!event) return; + const element = document.getElementById(event); + if (element) { + element.scrollIntoView(true); + } + }; const options = Array.isArray(teamList) ? teamList.map(item => ({ @@ -41,18 +50,30 @@ const TeamList = () => { })) : []; + useEffect(() => { + if (!scroll.current && selectedTeam?.label) { + scroll.current = true; + scrollintoView(selectedTeam?.value?.toString() ?? ''); + return; + } + }, [selectedTeam?.label, options]); + + // const newOptions = [selectedTeam, ...options]; + const handleInputChange = (event: React.ChangeEvent) => { setInput(event.target.value); }; const createHandler = () => { dispatch(setModalOpen(true)); }; - const fetchTeamData = (val: string) => { + const fetchDetails = (val: string) => { dispatch(fetchTeamDetails(val)); }; + const handleSelectionChange = (event: SelectPickerOptionProps) => { setSelectedTeam(event); - fetchTeamData(event.value.toString()); + fetchDetails(event.value.toString()); + scroll.current = false; setInput(''); }; diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index ae085bc..2186b45 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -8,7 +8,7 @@ import { } from '@src/Pages/TeamRevamp/partials/constants'; import { ApiService } from '@src/services/api'; import { RootState } from '@src/store'; -import { stat } from 'fs'; + export interface TeamState { data: TeamsData; isPending: boolean; From 710770f8af74793f9e08da1fe242b1f234213169 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 2 Jan 2024 11:19:11 +0530 Subject: [PATCH 09/39] TP-51166 | Added scroll while searching , member details added --- src/Pages/Incidents/Incidents.module.scss | 3 + .../TeamDetails/TeamDetails.module.scss | 4 - src/Pages/TeamRevamp/TeamDetails/index.tsx | 136 +--------- src/Pages/TeamRevamp/TeamList/index.tsx | 52 ++-- src/Pages/TeamRevamp/index.tsx | 29 +- .../partials/CreateTeam/CreateTeam.tsx | 31 ++- src/Pages/TeamRevamp/partials/Hooks.tsx | 0 .../TeamMemberDetails.module.scss | 16 ++ .../partials/TeamMemberDetails/index.tsx | 253 ++++++++++++++++++ src/assets/DeleteIcon.tsx | 26 ++ src/assets/PersonIconFill.tsx | 4 + src/slices/team1Slice.tsx | 11 +- 12 files changed, 397 insertions(+), 168 deletions(-) delete mode 100644 src/Pages/TeamRevamp/partials/Hooks.tsx create mode 100644 src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss create mode 100644 src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx create mode 100644 src/assets/DeleteIcon.tsx diff --git a/src/Pages/Incidents/Incidents.module.scss b/src/Pages/Incidents/Incidents.module.scss index e55fe1a..5f7d0e6 100644 --- a/src/Pages/Incidents/Incidents.module.scss +++ b/src/Pages/Incidents/Incidents.module.scss @@ -183,3 +183,6 @@ .popup-style { display: inline; } +.list-wrapper { + display: flex; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 69546cf..1773529 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -1,7 +1,3 @@ -.member-details-wrapper { - display: inline-grid; - grid-template-columns: 0.5fr 0.5fr; -} .team-details-wrapper { margin: 55px; } diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 2457ac3..dab6a1e 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -28,13 +28,13 @@ import { ArrowDownSolidIcon, } from '@navi/web-ui/lib/icons'; import { TeamState } from '@src/slices/team1Slice'; +import TeamMemberDetails from '../partials/TeamMemberDetails'; const TeamDetails = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const { teamDetails } = teamData; const teamId = teamDetails?.id; const dispatch = useDispatch(); - const [emailIds, setEmailIds] = useState(''); const [slackChannelId, setSlackChannelId] = useState(''); const [oncall, setOncall] = useState({ label: '', @@ -49,58 +49,13 @@ const TeamDetails = () => { const [psecInput, setPsecInput] = useState(''); const [openOnCall, setOpenOnCall] = useState(false); const [openPsecOnCall, setOpenPsecOnCall] = useState(false); - const [search, setSearch] = useState(''); const searchRef = useRef(null); - const [openMember, setOpenMember] = useState(''); - const openState = (name: string) => { - setOpenMember(name); - }; - const teamMembers = teamDetails?.participants?.map((item: any) => ({ - name: item.name, - email: item.email, - })); - - const manager = teamDetails?.participants?.find( - (item: any) => item.id === teamDetails?.managerId, - ); useEffect(() => { setInput(''); setPsecInput(''); }, [teamData]); - const handleAddMember = (e: any) => { - setEmailIds(e.target.value); - }; - const addMemberHandler = (): void => { - const endPoint = UPDATE_TEAM_DATA(); - const finalSlackData = emailIds?.includes(',') - ? emailIds.split(',').map(item => item?.trim()) - : [emailIds]; - ApiService.post(endPoint, { - id: teamId, - workEmailIds: finalSlackData, - }) - .then(response => { - toast.success(response?.data?.data); - dispatch(fetchTeamDetails(teamId?.toString() || '')); - }) - .catch(error => { - const toastMessage = `${ - error?.response?.data?.error?.message - ? `${error?.response?.data?.error?.message}` - : 'Something went wrong,Pls try again later' - }`; - toast.error(toastMessage); - }); - }; - const onKeyPressClickHandler = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - addMemberHandler(); - } - setEmailIds(''); - }; - const handleslackChannelId = (e: any) => { setSlackChannelId(e.target.value); }; @@ -110,7 +65,6 @@ const TeamDetails = () => { value: event.value.toString(), }); setInput(event?.label); - // setInput(''); setOpenOnCall(false); }; const handlePsecOncall = (event: SelectPickerOptionProps) => { @@ -151,11 +105,12 @@ const TeamDetails = () => { : ''; const DEFAULT_TEAM_ONCALL_ID = teamDetails?.oncall?.id || ''; + const DEFAULT_TEAM_PSEC_ID = teamDetails?.pse_oncall?.id || ''; const DEFAULT_PSEC_ONCALL = teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' ? '@' + teamDetails?.pse_oncall?.name - : '@psec_oncall_handle'; + : ''; const handleOncallPicker = () => { setOpenOnCall(!openOnCall); @@ -219,15 +174,9 @@ const TeamDetails = () => { > { - // eslint-disable-next-line - searchRef.current = true; setInput(e.target.value); }} placeholder="@oncall_handle" - onBlur={() => { - // eslint-disable-next-line - searchRef.current = false; - }} containerClassName={styles['search-input']} value={input || DEFAULT_TEAM_ONCALL} > @@ -254,6 +203,7 @@ const TeamDetails = () => { > setPsecInput(e.target.value)} + placeholder="@psec_oncall_handle" containerClassName={styles['search-input']} value={psecInput || DEFAULT_PSEC_ONCALL} > @@ -264,6 +214,7 @@ const TeamDetails = () => { onSelectionChange={handlePsecOncall as any} multiSelect={false} updateSearch={psecInput || ''} + selectedValue={psecOncall.value || DEFAULT_TEAM_PSEC_ID} wrapperClasses={styles['select-picker-wrapper']} > )} @@ -275,81 +226,8 @@ const TeamDetails = () => { Update details
-
- - MEMBERS - {' '} - -
-
- {teamMembers?.map((item: any) => ( -
-
-
- {manager?.name === item.name ? ( - - ) : ( - - )} -
-
-
- {' '} - {item.name}{' '} -
-
- {' '} - {item.email}{' '} -
-
openState(item.name)} - > - - {manager?.name === item.name ? 'Manager' : 'Member'} - {' '} - -
- {manager?.name !== item.name && ( -
- {openMember === item.name && ( - - )} -
- )} -
-
-
- ))} -
+ +
); }; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 5d0f97a..10b5071 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -7,15 +7,21 @@ import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; import PersonIcon from '@src/assets/PersonIcon'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; -import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; +import { + fetchTeamDetails, + setModalOpen, + selectSearchTeamData, + fetchTeams, +} from '@src/slices/team1Slice'; +import { TeamState } from '@src/slices/team1Slice'; import styles from './TeamList.module.scss'; -const CutomeTeamOptions = (option: SelectPickerOptionProps) => { +const CutomeTeamOptions = option => { return (
{option.label}
-
{10}
+
{option.count}
@@ -30,35 +36,50 @@ const TeamList = () => { const defaultTeam = useSelector( (state: RootState) => state.team1.teams.data[0], ); + // const teamCreated = useSelector( + // (state: RootState) => state.team1.teams.selectedTeam, + // ); + + // const { selectedTeam } = teamData; const [input, setInput] = useState(''); - const [selectedTeam, setSelectedTeam] = useState(); + + const [team, setTeam] = useState(); const scroll = useRef(false); const scrollintoView = (event: string): void => { if (!event) return; const element = document.getElementById(event); if (element) { - element.scrollIntoView(true); + setTimeout(() => { + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + }, 0); } }; + // const scrolltoteam = (id: string): void => { + // const element = document.getElementById(id); + // if (element) { + // setTimeout(() => { + // element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + // }, 0); + // } + // }; const options = Array.isArray(teamList) ? teamList.map(item => ({ label: item.name, value: item.id, + count: item.slackUserIds?.length, })) : []; useEffect(() => { - if (!scroll.current && selectedTeam?.label) { + if (!scroll.current && team?.label) { scroll.current = true; - scrollintoView(selectedTeam?.value?.toString() ?? ''); + scrollintoView(team?.value?.toString() ?? ''); return; } - }, [selectedTeam?.label, options]); - - // const newOptions = [selectedTeam, ...options]; + }, [team?.label, options]); const handleInputChange = (event: React.ChangeEvent) => { setInput(event.target.value); @@ -71,7 +92,7 @@ const TeamList = () => { }; const handleSelectionChange = (event: SelectPickerOptionProps) => { - setSelectedTeam(event); + setTeam(event); fetchDetails(event.value.toString()); scroll.current = false; setInput(''); @@ -86,7 +107,7 @@ const TeamList = () => { LeftInputAdornment={} onChange={handleInputChange} containerClassName={styles['search-input']} - value={selectedTeam?.label ?? defaultTeam?.name ?? ''} + value={input || ''} />
- {input && !selectedTeam && ( + {input && (
{' '} @@ -115,11 +136,10 @@ const TeamList = () => { updateSearch={input || ''} wrapperClasses={styles['select-picker-wrapper']} customOptionTemplate={CutomeTeamOptions} - selectedValue={selectedTeam?.value ?? defaultTeam?.id ?? ''} + selectedValue={team?.value ?? defaultTeam?.id ?? ''} /> - -
+
); }; diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 2007db8..63eef7a 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -1,13 +1,15 @@ -import { FC, useEffect } from 'react'; +import { FC, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import Typography from '@navi/web-ui/lib/primitives/Typography'; import { AppDispatch, RootState } from '@src/store'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; import TeamDetails from './TeamDetails'; import { - fetchDefaultTeamDetails, + TeamState, fetchTeamDetails, fetchTeams, + selectSearchTeamData, + setSelectedTeam, } from '@src/slices/team1Slice'; import styles from './Team.module.scss'; import TeamList from './TeamList'; @@ -15,9 +17,13 @@ import TeamList from './TeamList'; import { fetchAllBots } from './partials/Bots'; const TeamRevamp: FC = () => { const dispatch = useDispatch(); - const selectedTeam = useSelector( - (state: RootState) => state.team1.teams.selectedTeam, - ); + const teamData: TeamState = useSelector(selectSearchTeamData); + const { data } = teamData; + + const team1 = useSelector((state: RootState) => state.team1); + // const selectedTeam = useSelector( + // (state: RootState) => state.team1.teams.selectedTeam, + // ); useEffect(() => { dispatch(fetchTeams()); @@ -25,17 +31,24 @@ const TeamRevamp: FC = () => { useEffect(() => { fetchAllBots(); }, []); + // useEffect(() => { + // if (selectedTeam) { + // dispatch(fetchTeamDetails(selectedTeam?.id)); + // } + // }, [selectedTeam]); useEffect(() => { - if (selectedTeam) { - dispatch(fetchTeamDetails(selectedTeam?.id)); + if (data) { + dispatch(setSelectedTeam(data[0])); + // dispatch(fetchTeamDetails(data[0]?.id.toString())); } - }, [selectedTeam]); + }, []); return (
Teams
+
diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index 309283f..1ec7061 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -4,9 +4,10 @@ import { Typography } from '@navi/web-ui/lib/primitives'; import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { BorderedInput, ModalDialog } from '@navi/web-ui/lib/primitives'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import { AppDispatch } from '@src/store'; +import { AppDispatch, RootState } from '@src/store'; import { ApiService } from '@src/services/api'; import { + TeamState, fetchDefaultTeamDetails, fetchTeamDetails, fetchTeams, @@ -14,15 +15,25 @@ import { setModalOpen, setSelectedTeam, } from '@src/slices/team1Slice'; + import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; import { regularExpression, emailRegularExpression, } from '@src/Pages/TeamRevamp/partials/constants'; import styles from './CreateTeam.module.scss'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; -const CreateTeam: React.FC = () => { +interface CreateTeamProps { + // setTeam: () => void; + setTeam: React.Dispatch< + React.SetStateAction + >; +} + +const CreateTeam: React.FC = ({ setTeam }) => { const dispatch = useDispatch(); + const teamData: TeamState = useSelector(selectSearchTeamData); const { modalOpen } = useSelector(selectSearchTeamData); const [teamName, setTeamName] = useState(''); const [teamNameError, setTeamNameError] = useState(''); @@ -51,6 +62,9 @@ const CreateTeam: React.FC = () => { } }; + const selectedTeam = useSelector( + (state: RootState) => state.team1.teams.selectedTeam, + ); const handleTeamNameChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; setTeamName(inputValue); @@ -68,11 +82,14 @@ const CreateTeam: React.FC = () => { .then((response: any) => { const toastMessage = `${response?.data?.data?.message}`; const teamId = response?.data?.data?.id.toString(); + const createdTeamId = response?.data?.data?.id; + const teamName = response?.data?.data?.name; toast.success(toastMessage); clearErrors(); - dispatch(setSelectedTeam(teamId)); - dispatch(fetchTeams()); dispatch(fetchTeamDetails(teamId)); + setTeam({ label: teamName, value: createdTeamId }); + dispatch(setSelectedTeam(response?.data?.data?.id)); + dispatch(fetchTeams()); }) .catch(error => { const toastMessage = `${ @@ -84,6 +101,12 @@ const CreateTeam: React.FC = () => { }); }; + useEffect(() => { + if (selectedTeam) { + dispatch(fetchTeamDetails(selectedTeam?.id)); + } + }, [selectedTeam]); + const clearErrors = () => { dispatch(setModalOpen(false)); setTeamNameError(''); diff --git a/src/Pages/TeamRevamp/partials/Hooks.tsx b/src/Pages/TeamRevamp/partials/Hooks.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss new file mode 100644 index 0000000..1c3887a --- /dev/null +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -0,0 +1,16 @@ +.member-details-wrapper { + display: inline-grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 50px; +} +.personIcon { + display: flex; + justify-content: space-between; +} +.delete-icon { + width: 24px; + height: 24px; + cursor: pointer; + margin-top: 5px; + margin-left: 10px; +} diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx new file mode 100644 index 0000000..9b97fa7 --- /dev/null +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -0,0 +1,253 @@ +import { useDispatch, useSelector } from 'react-redux'; +import { useEffect, useState, useRef } from 'react'; +import { + fetchTeamDetails, + fetchTeams, + selectSearchTeamData, +} from '@src/slices/team1Slice'; +import { + BorderedInput, + Button, + Tooltip, + Typography, + ModalDialog, + IconButton, +} from '@navi/web-ui/lib/primitives'; + +import { + FETCH_SINGLE_TEAM_DATA, + MAKE_MANAGER, + REMOVE_TEAM_MEMBER, + UPDATE_TEAM_DATA, +} from '@src/Pages/TeamRevamp/partials/constants'; +import { ApiService } from '@src/services/api'; +import { toast } from '@navi/web-ui/lib/primitives/Toast'; +import { AppDispatch } from '@src/store'; +import PersonIconFill from '@src/assets/PersonIconFill'; +import ManagerIconFill from '@src/assets/ManagerIconFill'; +import DeleteIcon from '@src/assets/DeleteIcon'; +import { TeamState } from '@src/slices/team1Slice'; +import styles from './TeamMemberDetails.module.scss'; +import FallbackComponent from '@src/components/Fallback'; + +const TeamMemberDetails = () => { + const teamData: TeamState = useSelector(selectSearchTeamData); + const dispatch = useDispatch(); + const { teamDetails } = teamData; + const participants = teamDetails?.participants; + const teamId = teamDetails?.id?.toString() || ''; + const id = teamDetails?.id; + const [openDialog, setOpenDialog] = useState(false); + const [openModal, setOpenModal] = useState(false); + const [emailIds, setEmailIds] = useState(''); + const initialDialogsState: boolean[] = participants + ? participants.map(() => false) + : []; + + const [openDialogs, setOpenDialogs] = useState(initialDialogsState); + + const teamMembers = teamDetails?.participants?.map((item: any) => ({ + name: item.name, + email: item.email, + })); + + const manager = teamDetails?.participants?.find( + (item: any) => item.id === teamDetails?.managerId, + ); + const handleAddMember = (e: any) => { + setEmailIds(e.target.value); + }; + + const onKeyPressClickHandler = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + addMemberHandler(); + } + setEmailIds(''); + }; + const addMemberHandler = (): void => { + const endPoint = UPDATE_TEAM_DATA(); + const finalSlackData = emailIds?.includes(',') + ? emailIds.split(',').map(item => item?.trim()) + : [emailIds]; + ApiService.post(endPoint, { + id: id, + workEmailIds: finalSlackData, + }) + .then(response => { + toast.success(response?.data?.data); + dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeams()); + }) + .catch(error => { + const toastMessage = `${ + error?.response?.data?.error?.message + ? `${error?.response?.data?.error?.message}` + : 'Something went wrong,Pls try again later' + }`; + toast.error(toastMessage); + }); + }; + + const handleMakeManager = (memberID: string) => { + const endpoint = MAKE_MANAGER(teamId, memberID); + ApiService.patch(endpoint, {}) + .then(response => { + if (response.status === 200) { + toast.success(response.data.data); + dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeams()); + } else { + toast.error(response.data.error.message); + } + }) + .catch(error => { + toast.error(`Error in making manager of team : ${error.message}`); + }); + }; + + const handleOpenModal = () => () => { + setOpenModal(true); + }; + const handleResetModal = () => { + setOpenModal(false); + }; + const handleOpenDialog = () => () => { + setOpenDialog(true); + }; + const handleResetDialog = () => { + setOpenDialog(false); + }; + const removeMemberHandler = (memberID: string): void => { + const endpoint = REMOVE_TEAM_MEMBER(teamId, memberID); + ApiService.delete(endpoint) + .then(response => { + if (response.status === 200) { + toast.success(response.data.data); + dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeams()); + } else { + toast.error(response.data.error.message); + } + }) + .catch(error => { + toast.error(`Error removing member from team : ${error.message}`); + }); + }; + return ( +
+
+ + MEMBERS + {' '} + +
+
+ {teamMembers?.map((item: any) => ( +
+
+
+ {manager?.name === item.name ? ( + + ) : ( + + )} +
+ {' '} + {item.name}{' '} + {item.email}{' '} +
+ + {manager?.name === item.name ? 'Manager' : 'Member'} + {' '} + {manager?.name !== item.name && ( + + )} +
+
+ + {manager?.name !== item.name && ( +
+ +
+ )} +
+
+
+ ))} + {participants?.map((item: any) => ( +
+ { + handleMakeManager(item?.id || ''); + handleResetModal(); + }, + }, + ]} + header="Update manager? " + onClose={handleResetModal} + > +
+ + Are you sure you want to make {`${item?.name}`} manager of + this team? + +
+
+
+ ))} + + {participants?.map((item: any) => ( +
+ { + removeMemberHandler(item?.id || ''); + handleResetDialog(); + }, + }, + ]} + header="Remove member? " + onClose={handleResetDialog} + > +
+ + Are you sure you want to remove {`${item?.name}`} from this + team? + +
+
+
+ ))} +
+
+ ); +}; +export default TeamMemberDetails; diff --git a/src/assets/DeleteIcon.tsx b/src/assets/DeleteIcon.tsx new file mode 100644 index 0000000..a205bb0 --- /dev/null +++ b/src/assets/DeleteIcon.tsx @@ -0,0 +1,26 @@ +import React, { FC, MouseEventHandler, useState } from 'react'; +import { IconProps } from './types'; + +const DeleteIcon: FC = ({ + width = '24', + height = '24', + onClick, + ...restProps +}) => { + return ( + + + + ); +}; + +export default DeleteIcon; diff --git a/src/assets/PersonIconFill.tsx b/src/assets/PersonIconFill.tsx index c8dec7e..1e22078 100644 --- a/src/assets/PersonIconFill.tsx +++ b/src/assets/PersonIconFill.tsx @@ -7,6 +7,7 @@ const PersonIconFill: FC = ({ onClick, ...restProps }) => { + const [isHovered, setIsHovered] = useState(false); return ( = ({ height="32" viewBox="0 0 32 32" fill="none" + onClick={onClick as MouseEventHandler} + onMouseEnter={() => setIsHovered(true)} // Set isHovered to true on hover + onMouseLeave={() => setIsHovered(false)} > { state.teams.error = action.error.message ?? 'Something went wrong'; From 684853882158f97dd82385ff497cb0d52c73641d Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 2 Jan 2024 15:28:05 +0530 Subject: [PATCH 10/39] TP-51166 | Added scrolling on team creation and fixed remove member bug --- src/Pages/TeamRevamp/TeamList/index.tsx | 32 +++- src/Pages/TeamRevamp/index.tsx | 9 +- .../partials/CreateTeam/CreateTeam.tsx | 25 ++- .../partials/TeamMemberDetails/index.tsx | 162 +++++++++++------- src/slices/team1Slice.tsx | 15 +- 5 files changed, 161 insertions(+), 82 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 10b5071..b01c385 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -36,6 +36,10 @@ const TeamList = () => { const defaultTeam = useSelector( (state: RootState) => state.team1.teams.data[0], ); + const newTeam = useSelector((state: RootState) => state.team1.teams.newTeam); + const isPending = useSelector( + (state: RootState) => state.team1.teams.isPending, + ); // const teamCreated = useSelector( // (state: RootState) => state.team1.teams.selectedTeam, // ); @@ -74,12 +78,32 @@ const TeamList = () => { : []; useEffect(() => { - if (!scroll.current && team?.label) { + if (!scroll.current && team?.value) { scroll.current = true; - scrollintoView(team?.value?.toString() ?? ''); + if (input.length) { + setInput(''); + scrollintoView(team?.value?.toString() ?? ''); + } return; } - }, [team?.label, options]); + }, [team?.value, options]); + + useEffect(() => { + scroll.current = false; + if (!scroll.current && newTeam?.toString()?.length) { + scroll.current = true; + if (Array.isArray(teamList) && teamList.length) { + teamList?.forEach(item => { + if (item.id === newTeam) { + setTimeout(() => { + scrollintoView(newTeam?.toString() ?? ''); + }, 100); + } + }); + } + return; + } + }, [teamList]); const handleInputChange = (event: React.ChangeEvent) => { setInput(event.target.value); @@ -95,7 +119,7 @@ const TeamList = () => { setTeam(event); fetchDetails(event.value.toString()); scroll.current = false; - setInput(''); + // setInput(''); }; return ( diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 63eef7a..eecdb34 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -20,10 +20,9 @@ const TeamRevamp: FC = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const { data } = teamData; - const team1 = useSelector((state: RootState) => state.team1); - // const selectedTeam = useSelector( - // (state: RootState) => state.team1.teams.selectedTeam, - // ); + const selectedTeam = useSelector( + (state: RootState) => state.team1.teams.selectedTeam, + ); useEffect(() => { dispatch(fetchTeams()); @@ -35,7 +34,7 @@ const TeamRevamp: FC = () => { // if (selectedTeam) { // dispatch(fetchTeamDetails(selectedTeam?.id)); // } - // }, [selectedTeam]); + // }, []); useEffect(() => { if (data) { dispatch(setSelectedTeam(data[0])); diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index 1ec7061..fa234de 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -13,7 +13,9 @@ import { fetchTeams, selectSearchTeamData, setModalOpen, + setNewTeam, setSelectedTeam, + setTeamData, } from '@src/slices/team1Slice'; import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; @@ -39,7 +41,7 @@ const CreateTeam: React.FC = ({ setTeam }) => { const [teamNameError, setTeamNameError] = useState(''); const [emailError, setEmailError] = useState(''); const [email, setEmail] = useState(''); - + const [createdTeam, setCreatedTeam] = useState(''); const validateTeamName = (value: string): void => { value = value.trim(); if (!regularExpression.test(value)) { @@ -81,14 +83,15 @@ const CreateTeam: React.FC = ({ setTeam }) => { ApiService.post(CREATE_TEAM, { name: teamName, manager_email: email }) .then((response: any) => { const toastMessage = `${response?.data?.data?.message}`; - const teamId = response?.data?.data?.id.toString(); const createdTeamId = response?.data?.data?.id; const teamName = response?.data?.data?.name; toast.success(toastMessage); clearErrors(); - dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeamDetails(createdTeamId.toString())); setTeam({ label: teamName, value: createdTeamId }); - dispatch(setSelectedTeam(response?.data?.data?.id)); + dispatch(setSelectedTeam(createdTeamId)); + setCreatedTeam(createdTeamId); + dispatch(setNewTeam(createdTeamId)); dispatch(fetchTeams()); }) .catch(error => { @@ -114,7 +117,19 @@ const CreateTeam: React.FC = ({ setTeam }) => { setTeamName(''); setEmail(''); }; - + // const scrolltoteam = (id: string): void => { + // const element = document.getElementById(id); + // if (element) { + // setTimeout(() => { + // element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + // }, 0); + // } + // }; + // useEffect(() => { + // if (createdTeam) { + // scrolltoteam(createdTeam); + // } + // }, [createdTeam]); return (
{modalOpen && ( diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 9b97fa7..6d09525 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -30,6 +30,11 @@ import { TeamState } from '@src/slices/team1Slice'; import styles from './TeamMemberDetails.module.scss'; import FallbackComponent from '@src/components/Fallback'; +type SelectedMember = { + id: number; + name: string; +}; + const TeamMemberDetails = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const dispatch = useDispatch(); @@ -40,11 +45,7 @@ const TeamMemberDetails = () => { const [openDialog, setOpenDialog] = useState(false); const [openModal, setOpenModal] = useState(false); const [emailIds, setEmailIds] = useState(''); - const initialDialogsState: boolean[] = participants - ? participants.map(() => false) - : []; - - const [openDialogs, setOpenDialogs] = useState(initialDialogsState); + const [selectedMember, setSelectedMember] = useState(); const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, @@ -105,13 +106,15 @@ const TeamMemberDetails = () => { }); }; - const handleOpenModal = () => () => { + const handleOpenModal = member => () => { + setSelectedMember(member); setOpenModal(true); }; const handleResetModal = () => { setOpenModal(false); }; - const handleOpenDialog = () => () => { + const handleOpenDialog = member => () => { + setSelectedMember(member); setOpenDialog(true); }; const handleResetDialog = () => { @@ -159,7 +162,7 @@ const TeamMemberDetails = () => { ) : ( )} -
+
{' '} {item.name}{' '} {item.email}{' '} @@ -168,7 +171,7 @@ const TeamMemberDetails = () => { {manager?.name === item.name ? 'Manager' : 'Member'} {' '} {manager?.name !== item.name && ( - )} @@ -177,7 +180,8 @@ const TeamMemberDetails = () => { {manager?.name !== item.name && (
@@ -187,65 +191,93 @@ const TeamMemberDetails = () => {
))} - {participants?.map((item: any) => ( -
- { - handleMakeManager(item?.id || ''); - handleResetModal(); + {/* {participants?.map((item: any) => ( +
+ {openModal ? ( + -
- - Are you sure you want to make {`${item?.name}`} manager of - this team? - -
-
+ { + label: 'Update', + onClick: () => { + handleMakeManager(item?.id || ''); + handleResetModal(); + }, + }, + ]} + header="Update manager? " + onClose={handleResetModal} + > +
+ + Are you sure you want to make {`${item?.name}`} manager of + this team? + +
+ + ) : null}
- ))} + ))} */} - {participants?.map((item: any) => ( -
- { - removeMemberHandler(item?.id || ''); - handleResetDialog(); - }, - }, - ]} - header="Remove member? " - onClose={handleResetDialog} - > -
- - Are you sure you want to remove {`${item?.name}`} from this - team? - -
-
+ { + handleMakeManager(selectedMember?.id?.toString() || ''); + handleResetModal(); + }, + }, + ]} + header="Update manager? " + onClose={handleResetModal} + > +
+ + Are you sure you want to make {`${selectedMember?.name}`} manager + of this team? +
- ))} +
+ { + removeMemberHandler(selectedMember?.id.toString() || ''); + handleResetDialog(); + }, + }, + ]} + header="Remove member? " + onClose={handleResetDialog} + > +
+ + Are you sure you want to remove {`${selectedMember?.name}`} from + this team? + +
+
+ {/* {participants?.map((item: any) => ( +
+ +
+ ))} */}
); diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index ad92c3e..a5fcb9c 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -16,6 +16,7 @@ export interface TeamState { teamDetails: TeamsDetail; error: string; selectedTeam: number | string | any; + newTeam: number | string; } type TeamInitialState = { @@ -30,6 +31,7 @@ const initialState: TeamInitialState = { modalOpen: false, teamDetails: {}, selectedTeam: [], + newTeam: '', }, }; @@ -66,7 +68,9 @@ const team1Slice = createSlice({ setTeamDetails: (state, action) => { state.teams.teamDetails = action.payload; }, - + setNewTeam: (state, action) => { + state.teams.newTeam = action.payload; + }, setSelectedTeam: (state, action) => { state.teams.selectedTeam = action.payload; }, @@ -119,6 +123,11 @@ const team1Slice = createSlice({ export const selectSearchTeamData = (state: RootState) => state.team1.teams; -export const { setTeamData, setModalOpen, setTeamDetails, setSelectedTeam } = - team1Slice.actions; +export const { + setTeamData, + setModalOpen, + setTeamDetails, + setSelectedTeam, + setNewTeam, +} = team1Slice.actions; export default team1Slice.reducer; From 44a6d51f66d4541e4930ba1324efb6c9a18f7ca0 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Wed, 3 Jan 2024 15:55:19 +0530 Subject: [PATCH 11/39] TP-51166 | Updated member details css, outside click handle on dropdown and loader --- src/Pages/TeamRevamp/TeamDetails/index.tsx | 40 +++--- .../TeamRevamp/TeamList/TeamList.module.scss | 3 + src/Pages/TeamRevamp/index.tsx | 11 +- .../TeamMemberDetails.module.scss | 37 ++++-- .../partials/TeamMemberDetails/index.tsx | 115 +++++++----------- src/slices/team1Slice.tsx | 25 ++-- 6 files changed, 115 insertions(+), 116 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index dab6a1e..561bb56 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -7,33 +7,28 @@ import { Tooltip, Typography, } from '@navi/web-ui/lib/primitives'; -import DrawerStyles from '@src/Pages/Incidents/DrawerMode/DrawerMode.module.scss'; -import { useEffect, useState, useRef } from 'react'; -import { - FETCH_SINGLE_TEAM_DATA, - UPDATE_TEAM_DATA, -} from '../partials/constants'; + +import { useEffect, useState, useRef, MutableRefObject } from 'react'; +import { UPDATE_TEAM_DATA } from '../partials/constants'; import { ApiService } from '@src/services/api'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch } from '@src/store'; import { getBots } from '../partials/Bots'; import styles from './TeamDetails.module.scss'; -import SlackIcon from '@src/assets/SlackIcon'; -import ManagerIconFill from '@src/assets/ManagerIconFill'; -import { Filter, SelectPicker } from '@navi/web-ui/lib/components'; + +import { SelectPicker } from '@navi/web-ui/lib/components'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; -import { - AlertOutlineIcon, - ArrowDownIcon, - ArrowDownSolidIcon, -} from '@navi/web-ui/lib/icons'; +import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { TeamState } from '@src/slices/team1Slice'; import TeamMemberDetails from '../partials/TeamMemberDetails'; +import FallbackComponent from '@src/components/Fallback'; +import useOutsideClick from '@src/services/hooks/useOustideClick'; const TeamDetails = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const { teamDetails } = teamData; const teamId = teamDetails?.id; + const dispatch = useDispatch(); const [slackChannelId, setSlackChannelId] = useState(''); const [oncall, setOncall] = useState({ @@ -75,6 +70,19 @@ const TeamDetails = () => { setPsecInput(event?.label); setOpenPsecOnCall(false); }; + const handleOncallOutsideClick = () => { + setOpenOnCall(false); + }; + const handlePsecOncallOutsideClick = () => { + setOpenPsecOnCall(false); + }; + const refOncall = useOutsideClick({ + callback: handleOncallOutsideClick, + }) as MutableRefObject; + + const refPsecOncall = useOutsideClick({ + callback: handlePsecOncallOutsideClick, + }) as MutableRefObject; const submitHandler = (): void => { const endPoint = UPDATE_TEAM_DATA(); @@ -167,7 +175,7 @@ const TeamDetails = () => { Team on call {' '} -
+
{ )}
-
+
PSEC on call {' '} diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index 4963c0c..460f4d3 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -28,6 +28,9 @@ border: none !important; box-shadow: none !important; } +.select-picker-wrapper::-webkit-scrollbar { + display: none; +} .icon-wrapper { display: flex; gap: 8px; diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index eecdb34..99e6526 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -20,25 +20,16 @@ const TeamRevamp: FC = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const { data } = teamData; - const selectedTeam = useSelector( - (state: RootState) => state.team1.teams.selectedTeam, - ); - useEffect(() => { dispatch(fetchTeams()); }, []); useEffect(() => { fetchAllBots(); }, []); - // useEffect(() => { - // if (selectedTeam) { - // dispatch(fetchTeamDetails(selectedTeam?.id)); - // } - // }, []); + useEffect(() => { if (data) { dispatch(setSelectedTeam(data[0])); - // dispatch(fetchTeamDetails(data[0]?.id.toString())); } }, []); return ( diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index 1c3887a..0a48f25 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -1,16 +1,37 @@ .member-details-wrapper { - display: inline-grid; + width: 100%; +} + +.add-member { + margin-bottom: 24px; +} + +.details-wrapper { + display: grid; grid-template-columns: 1fr 1fr 1fr; + justify-content: space-around; gap: 50px; } -.personIcon { +.icon-wrapper { display: flex; - justify-content: space-between; } -.delete-icon { - width: 24px; - height: 24px; + +.items-wrapper .make-manger-button, +.items-wrapper .delete-icon { + display: none; +} + +.items-wrapper:hover .make-manger-button, +.items-wrapper:hover .delete-icon { + display: block; + justify-content: flex-end; +} + +.items-wrapper:hover { + background-color: #cce4ff; cursor: pointer; - margin-top: 5px; - margin-left: 10px; +} + +.manager-wrapper { + background-color: antiquewhite; } diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 6d09525..0674fb8 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -15,7 +15,6 @@ import { } from '@navi/web-ui/lib/primitives'; import { - FETCH_SINGLE_TEAM_DATA, MAKE_MANAGER, REMOVE_TEAM_MEMBER, UPDATE_TEAM_DATA, @@ -30,35 +29,39 @@ import { TeamState } from '@src/slices/team1Slice'; import styles from './TeamMemberDetails.module.scss'; import FallbackComponent from '@src/components/Fallback'; -type SelectedMember = { +type MemberType = { id: number; name: string; + email: string; }; const TeamMemberDetails = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const dispatch = useDispatch(); const { teamDetails } = teamData; - const participants = teamDetails?.participants; + const { isPending } = teamData; const teamId = teamDetails?.id?.toString() || ''; const id = teamDetails?.id; const [openDialog, setOpenDialog] = useState(false); const [openModal, setOpenModal] = useState(false); const [emailIds, setEmailIds] = useState(''); - const [selectedMember, setSelectedMember] = useState(); + const [selectedMember, setSelectedMember] = useState(); const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, email: item.email, + id: item.id, })); const manager = teamDetails?.participants?.find( (item: any) => item.id === teamDetails?.managerId, ); - const handleAddMember = (e: any) => { + const handleAddMember = (e: React.ChangeEvent) => { setEmailIds(e.target.value); }; - + if (isPending) { + return ; + } const onKeyPressClickHandler = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { addMemberHandler(); @@ -77,7 +80,6 @@ const TeamMemberDetails = () => { .then(response => { toast.success(response?.data?.data); dispatch(fetchTeamDetails(teamId)); - dispatch(fetchTeams()); }) .catch(error => { const toastMessage = `${ @@ -96,7 +98,6 @@ const TeamMemberDetails = () => { if (response.status === 200) { toast.success(response.data.data); dispatch(fetchTeamDetails(teamId)); - dispatch(fetchTeams()); } else { toast.error(response.data.error.message); } @@ -109,6 +110,7 @@ const TeamMemberDetails = () => { const handleOpenModal = member => () => { setSelectedMember(member); setOpenModal(true); + console.log('clicked'); }; const handleResetModal = () => { setOpenModal(false); @@ -127,7 +129,6 @@ const TeamMemberDetails = () => { if (response.status === 200) { toast.success(response.data.data); dispatch(fetchTeamDetails(teamId)); - dispatch(fetchTeams()); } else { toast.error(response.data.error.message); } @@ -136,12 +137,15 @@ const TeamMemberDetails = () => { toast.error(`Error removing member from team : ${error.message}`); }); }; + return (
- - MEMBERS - {' '} +
+ {' '} + MEMBERS
+
+ { containerClassName={styles['team-input-wrapper']} />
-
- {teamMembers?.map((item: any) => ( -
-
-
- {manager?.name === item.name ? ( - - ) : ( - - )} -
+
+
+ {teamMembers?.map((item: any) => ( +
+
+
+ {' '} + {manager?.name === item.name ? ( + + ) : ( + + )} +
+ +
{' '} {item.name}{' '} {item.email}{' '} -
- - {manager?.name === item.name ? 'Manager' : 'Member'} - {' '} + + {manager?.name === item.name ? 'Manager' : ''} + {' '} +
{manager?.name !== item.name && ( )}
- {manager?.name !== item.name && (
{ )}
-
- ))} - {/* {participants?.map((item: any) => ( -
- {openModal ? ( - { - handleMakeManager(item?.id || ''); - handleResetModal(); - }, - }, - ]} - header="Update manager? " - onClose={handleResetModal} - > -
- - Are you sure you want to make {`${item?.name}`} manager of - this team? - -
-
- ) : null} -
- ))} */} - + ))} +
{ { label: 'Update', onClick: () => { - handleMakeManager(selectedMember?.id?.toString() || ''); + handleMakeManager(selectedMember?.id.toString() || ''); handleResetModal(); }, }, @@ -273,11 +255,6 @@ const TeamMemberDetails = () => {
- {/* {participants?.map((item: any) => ( -
- -
- ))} */}
); diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index a5fcb9c..3a8fdd1 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -47,13 +47,13 @@ export const fetchTeamDetails = createAsyncThunk( return await ApiService.get(endPoint); }, ); -export const fetchDefaultTeamDetails = createAsyncThunk( - 'team/fetchDefaultTeamDetails', - async (id: string) => { - const endPoint = FETCH_SINGLE_TEAM_DATA(id); - return await ApiService.get(endPoint); - }, -); +// export const fetchDefaultTeamDetails = createAsyncThunk( +// 'team/fetchDefaultTeamDetails', +// async (id: string) => { +// const endPoint = FETCH_SINGLE_TEAM_DATA(id); +// return await ApiService.get(endPoint); +// }, +// ); const team1Slice = createSlice({ name: 'team1', @@ -84,7 +84,6 @@ const team1Slice = createSlice({ if (!state.teams.selectedTeam) { state.teams.selectedTeam = action.payload.data.data[0]; } - // state.teams.selectedTeam = action.payload.data.data[0]; }); builder.addCase(fetchTeams.rejected, (state, action) => { state.teams.error = action.error.message ?? 'Something went wrong'; @@ -113,11 +112,11 @@ const team1Slice = createSlice({ state.teams.teamDetails = {}; state.teams.error = ''; }); - builder.addCase(fetchDefaultTeamDetails.fulfilled, (state, action) => { - state.teams.teamDetails = action.payload.data.data; - state.teams.isPending = false; - state.teams.error = ''; - }); + // builder.addCase(fetchDefaultTeamDetails.fulfilled, (state, action) => { + // state.teams.teamDetails = action.payload.data.data; + // state.teams.isPending = false; + // state.teams.error = ''; + // }); }, }); From 091ae1e6699b96ef57973fcd93c5334db7bb1d63 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Thu, 4 Jan 2024 14:16:27 +0530 Subject: [PATCH 12/39] TP-51166 | added design for hover state in team members detail --- .../TeamMemberDetails.module.scss | 44 +++++++++++++---- .../partials/TeamMemberDetails/index.tsx | 49 +++++++++++++++---- src/assets/MemberIcon.tsx | 28 +++++++++++ 3 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 src/assets/MemberIcon.tsx diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index 0a48f25..f9a6d95 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -1,7 +1,6 @@ .member-details-wrapper { width: 100%; } - .add-member { margin-bottom: 24px; } @@ -12,26 +11,53 @@ justify-content: space-around; gap: 50px; } -.icon-wrapper { - display: flex; +.item-details { + // background-color: aqua; + margin-top: 12px; + margin-left: 10px; + width: calc(100% - 48px); +} +.details-container { + display: flex; + // background-color: blueviolet; + width: 253px; +} +.icon-wrapper { + margin-top: 12px; + width: 32px; + margin-left: 6px; + // background-color: brown; +} +.items-wrapper { + width: 253px; + border-radius: 8px; + height: 90px; + // background-color: aqua; } - .items-wrapper .make-manger-button, .items-wrapper .delete-icon { display: none; } -.items-wrapper:hover .make-manger-button, +.items-wrapper:hover .make-manger-button { + display: block; + margin-top: 4px; +} .items-wrapper:hover .delete-icon { display: block; - justify-content: flex-end; + margin-top: 12px; + width: 16px; + margin-right: 6px; } - .items-wrapper:hover { - background-color: #cce4ff; + background-color: #b3c7df; cursor: pointer; } .manager-wrapper { - background-color: antiquewhite; + width: 253px; + border-radius: 8px; +} +.input-wrapper { + display: flex; } diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 0674fb8..82bd2b1 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -24,6 +24,7 @@ import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch } from '@src/store'; import PersonIconFill from '@src/assets/PersonIconFill'; import ManagerIconFill from '@src/assets/ManagerIconFill'; +import MemberIcon from '@src/assets/MemberIcon'; import DeleteIcon from '@src/assets/DeleteIcon'; import { TeamState } from '@src/slices/team1Slice'; import styles from './TeamMemberDetails.module.scss'; @@ -46,6 +47,10 @@ const TeamMemberDetails = () => { const [openModal, setOpenModal] = useState(false); const [emailIds, setEmailIds] = useState(''); const [selectedMember, setSelectedMember] = useState(); + const [hovered, setHovered] = useState({ + ishovered: false, + id: '', + }); const teamMembers = teamDetails?.participants?.map((item: any) => ({ name: item.name, @@ -137,7 +142,18 @@ const TeamMemberDetails = () => { toast.error(`Error removing member from team : ${error.message}`); }); }; - + const handleIconchange = item => { + setHovered({ + ishovered: false, + id: item.id, + }); + }; + const handleHoverIcon = item => { + setHovered({ + ishovered: true, + id: item.id, + }); + }; return (
@@ -166,14 +182,21 @@ const TeamMemberDetails = () => { : styles['items-wrapper'] } key={item.id} + onMouseEnter={() => handleHoverIcon(item)} + onMouseLeave={() => handleIconchange(item)} > -
-
- {' '} +
+
{manager?.name === item.name ? ( ) : ( - +
+ {hovered.ishovered && hovered.id === item.id ? ( + + ) : ( + + )} +
)}
@@ -224,10 +247,16 @@ const TeamMemberDetails = () => { onClose={handleResetModal} >
- - Are you sure you want to make {`${selectedMember?.name}`} manager - of this team? - +
+ + Are you sure you want to make{' '} + + {' '} + {`${selectedMember?.name}`}{' '} + + manager of this team? + {' '} +
{ onClose={handleResetDialog} >
- + Are you sure you want to remove {`${selectedMember?.name}`} from this team? diff --git a/src/assets/MemberIcon.tsx b/src/assets/MemberIcon.tsx new file mode 100644 index 0000000..5dbb4be --- /dev/null +++ b/src/assets/MemberIcon.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; +import { IconProps } from './types'; + +const MemberIcon: FC = ({ + width = '32', + height = '32', + ...restProps +}) => { + return ( + + + + + ); +}; + +export default MemberIcon; From 808964392cebef52d09ab061fdbe6b28c4f69e5d Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Thu, 4 Jan 2024 14:20:37 +0530 Subject: [PATCH 13/39] TP-51166 | fixed build fail --- .../partials/CreateTeam/CreateTeam.tsx | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index fa234de..610dcab 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -8,7 +8,6 @@ import { AppDispatch, RootState } from '@src/store'; import { ApiService } from '@src/services/api'; import { TeamState, - fetchDefaultTeamDetails, fetchTeamDetails, fetchTeams, selectSearchTeamData, @@ -27,7 +26,6 @@ import styles from './CreateTeam.module.scss'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; interface CreateTeamProps { - // setTeam: () => void; setTeam: React.Dispatch< React.SetStateAction >; @@ -117,19 +115,7 @@ const CreateTeam: React.FC = ({ setTeam }) => { setTeamName(''); setEmail(''); }; - // const scrolltoteam = (id: string): void => { - // const element = document.getElementById(id); - // if (element) { - // setTimeout(() => { - // element.scrollIntoView({ behavior: 'smooth', block: 'center' }); - // }, 0); - // } - // }; - // useEffect(() => { - // if (createdTeam) { - // scrolltoteam(createdTeam); - // } - // }, [createdTeam]); + return (
{modalOpen && ( From 10c19fc5ddc8cb132e071bdd8b16c8e03b08e009 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Thu, 4 Jan 2024 19:37:09 +0530 Subject: [PATCH 14/39] TP-51166 | RBAC in progress --- .../TeamDetails/TeamDetails.module.scss | 16 +++- src/Pages/TeamRevamp/TeamDetails/index.tsx | 86 ++++++++++++------- .../TeamRevamp/TeamList/TeamList.module.scss | 1 + src/Pages/TeamRevamp/TeamList/index.tsx | 52 ++++------- .../TeamMemberDetails.module.scss | 37 ++++++++ .../partials/TeamMemberDetails/index.tsx | 62 +++++++------ src/assets/AlertIcon.tsx | 38 ++++++++ src/services/hooks/useAuth.tsx | 12 +++ 8 files changed, 211 insertions(+), 93 deletions(-) create mode 100644 src/assets/AlertIcon.tsx create mode 100644 src/services/hooks/useAuth.tsx diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 1773529..34ca09c 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -1,12 +1,12 @@ .team-details-wrapper { - margin: 55px; + margin: 36px; } .team-input-wrapper { min-width: (320px); height: (38px); } .add-member { - margin-top: 55px; + margin-top: 72px; margin-bottom: 24px; } .member-div { @@ -64,3 +64,15 @@ .update-details { margin-left: 900px; } +.hint-text { + color: red; +} +.fallback-component { + height: 100px; + margin-left: 450px; +} +.update-details-disabled { + margin-left: 900px; + opacity: 0; + pointer-events: none; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 561bb56..ec0a187 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -21,15 +21,20 @@ import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicke import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { TeamState } from '@src/slices/team1Slice'; import TeamMemberDetails from '../partials/TeamMemberDetails'; -import FallbackComponent from '@src/components/Fallback'; import useOutsideClick from '@src/services/hooks/useOustideClick'; +import FallbackComponent from '@src/components/Fallback'; +import { useAuthData } from '@src/services/hooks/useAuth'; const TeamDetails = () => { + const dispatch = useDispatch(); const teamData: TeamState = useSelector(selectSearchTeamData); const { teamDetails } = teamData; const teamId = teamDetails?.id; - - const dispatch = useDispatch(); + const { isPending } = teamData; + // const Role = useAuthData(); + const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); + const userID = userData?.clientId || []; + const userEmail = userData?.emailId || ''; const [slackChannelId, setSlackChannelId] = useState(''); const [oncall, setOncall] = useState({ label: '', @@ -45,13 +50,22 @@ const TeamDetails = () => { const [openOnCall, setOpenOnCall] = useState(false); const [openPsecOnCall, setOpenPsecOnCall] = useState(false); const searchRef = useRef(null); + const DEFAULT_TEAM_ONCALL = teamDetails?.oncall?.name + ? `@${teamDetails?.oncall?.name}` + : ''; + const DEFAULT_TEAM_ONCALL_ID = teamDetails?.oncall?.id || ''; + const DEFAULT_TEAM_PSEC_ID = teamDetails?.pse_oncall?.id || ''; + const DEFAULT_PSEC_ONCALL = + teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' + ? '@' + teamDetails?.pse_oncall?.name + : ''; useEffect(() => { setInput(''); setPsecInput(''); }, [teamData]); - const handleslackChannelId = (e: any) => { + const handleslackChannelId = (e: React.ChangeEvent) => { setSlackChannelId(e.target.value); }; const handleOncallChange = (event: SelectPickerOptionProps) => { @@ -70,10 +84,10 @@ const TeamDetails = () => { setPsecInput(event?.label); setOpenPsecOnCall(false); }; - const handleOncallOutsideClick = () => { + const handleOncallOutsideClick = (): void => { setOpenOnCall(false); }; - const handlePsecOncallOutsideClick = () => { + const handlePsecOncallOutsideClick = (): void => { setOpenPsecOnCall(false); }; const refOncall = useOutsideClick({ @@ -84,6 +98,12 @@ const TeamDetails = () => { callback: handlePsecOncallOutsideClick, }) as MutableRefObject; + const handleOncallPicker = (): void => { + setOpenOnCall(!openOnCall); + }; + const handleOpenPsecOnCallPicker = (): void => { + setOpenPsecOnCall(!openPsecOnCall); + }; const submitHandler = (): void => { const endPoint = UPDATE_TEAM_DATA(); ApiService.post(endPoint, { @@ -107,25 +127,21 @@ const TeamDetails = () => { toast.error(toastMessage); }); }; - - const DEFAULT_TEAM_ONCALL = teamDetails?.oncall?.name - ? `@${teamDetails?.oncall?.name}` - : ''; - - const DEFAULT_TEAM_ONCALL_ID = teamDetails?.oncall?.id || ''; - const DEFAULT_TEAM_PSEC_ID = teamDetails?.pse_oncall?.id || ''; - - const DEFAULT_PSEC_ONCALL = - teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' - ? '@' + teamDetails?.pse_oncall?.name - : ''; - - const handleOncallPicker = () => { - setOpenOnCall(!openOnCall); - }; - - const handleOpenPsecOnCallPicker = () => { - setOpenPsecOnCall(!openPsecOnCall); + if (isPending) { + return ( +
+ +
+ ); + } + const managerEmail = teamDetails?.participants?.find( + participant => participant.id === teamDetails?.managerId, + )?.email; + const isUserParticipant = (teamDetails: any) => { + const participantEmails = teamDetails?.participants?.map( + (participant: any) => participant.email, + ); + return participantEmails?.includes(userEmail); }; return (
@@ -158,7 +174,7 @@ const TeamDetails = () => { { // value={'#' + teamDetails?.webhookSlackChannelName} hintMsg="Channel name populates after entering id" error={teamDetails?.webhookSlackChannelName === ''} + disabled={!isUserParticipant(teamDetails)} />
@@ -187,6 +204,7 @@ const TeamDetails = () => { placeholder="@oncall_handle" containerClassName={styles['search-input']} value={input || DEFAULT_TEAM_ONCALL} + disabled={!isUserParticipant(teamDetails)} >
{openOnCall && ( @@ -214,6 +232,7 @@ const TeamDetails = () => { placeholder="@psec_oncall_handle" containerClassName={styles['search-input']} value={psecInput || DEFAULT_PSEC_ONCALL} + disabled={!isUserParticipant(teamDetails)} >
{openPsecOnCall && ( @@ -229,12 +248,21 @@ const TeamDetails = () => {
-
-
-
); diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index 460f4d3..a0ad6e2 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -1,4 +1,5 @@ .team-div { + margin-top: 36px; padding-top: 8px; padding-bottom: 8px; border-radius: 8px; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index b01c385..a629da4 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -6,14 +6,9 @@ import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicke import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; import PersonIcon from '@src/assets/PersonIcon'; +import { useAuthData } from '@src/services/hooks/useAuth'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; -import { - fetchTeamDetails, - setModalOpen, - selectSearchTeamData, - fetchTeams, -} from '@src/slices/team1Slice'; -import { TeamState } from '@src/slices/team1Slice'; +import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; import styles from './TeamList.module.scss'; const CutomeTeamOptions = option => { @@ -37,17 +32,8 @@ const TeamList = () => { (state: RootState) => state.team1.teams.data[0], ); const newTeam = useSelector((state: RootState) => state.team1.teams.newTeam); - const isPending = useSelector( - (state: RootState) => state.team1.teams.isPending, - ); - // const teamCreated = useSelector( - // (state: RootState) => state.team1.teams.selectedTeam, - // ); - - // const { selectedTeam } = teamData; - + const Role = useAuthData(); const [input, setInput] = useState(''); - const [team, setTeam] = useState(); const scroll = useRef(false); @@ -60,15 +46,6 @@ const TeamList = () => { }, 0); } }; - // const scrolltoteam = (id: string): void => { - // const element = document.getElementById(id); - // if (element) { - // setTimeout(() => { - // element.scrollIntoView({ behavior: 'smooth', block: 'center' }); - // }, 0); - // } - // }; - const options = Array.isArray(teamList) ? teamList.map(item => ({ label: item.name, @@ -119,12 +96,10 @@ const TeamList = () => { setTeam(event); fetchDetails(event.value.toString()); scroll.current = false; - // setInput(''); }; return (
-

TeamList

{ containerClassName={styles['search-input']} value={input || ''} /> - + + {Role.includes('Admin') && ( + + )}
{input && (
diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index f9a6d95..c1fdbd1 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -59,5 +59,42 @@ border-radius: 8px; } .input-wrapper { + // display: flex; + //background-color: aqua; +} +.remove-button { + background-color: red; + height: 36px; + width: 77px; + color: white; +} +.cancel-button { + background: var(--navi-color-gray-bg-primary); + height: 36px; + width: 77px; + color: var(--navi-color-gray-c2); + margin-right: 12px; + border: 1px solid var(--navi-color-gray-border); + border-radius: 6px; + margin-right: 16px; +} +.team-input-wrapper { + width: 510px; + height: 36px; +} +.footer-wrapper { + display: flex; + justify-content: flex-end; + margin-top: 32px; +} +.abc { + //display: flex; +} +.xyz { + margin: 0px 0px 0px 5px; + //display: flex; +} + +.input-wrapper-child { display: flex; } diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 82bd2b1..5b8169b 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -26,6 +26,7 @@ import PersonIconFill from '@src/assets/PersonIconFill'; import ManagerIconFill from '@src/assets/ManagerIconFill'; import MemberIcon from '@src/assets/MemberIcon'; import DeleteIcon from '@src/assets/DeleteIcon'; +import AlertIcon from '@src/assets/AlertIcon'; import { TeamState } from '@src/slices/team1Slice'; import styles from './TeamMemberDetails.module.scss'; import FallbackComponent from '@src/components/Fallback'; @@ -85,6 +86,7 @@ const TeamMemberDetails = () => { .then(response => { toast.success(response?.data?.data); dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeams()); }) .catch(error => { const toastMessage = `${ @@ -115,7 +117,6 @@ const TeamMemberDetails = () => { const handleOpenModal = member => () => { setSelectedMember(member); setOpenModal(true); - console.log('clicked'); }; const handleResetModal = () => { setOpenModal(false); @@ -132,8 +133,11 @@ const TeamMemberDetails = () => { ApiService.delete(endpoint) .then(response => { if (response.status === 200) { - toast.success(response.data.data); + toast(response.data.data, { + icon: , + }); dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeams()); } else { toast.error(response.data.error.message); } @@ -163,8 +167,7 @@ const TeamMemberDetails = () => {
{ onClose={handleResetModal} >
-
- +
+ Are you sure you want to make{' '} - - {' '} - {`${selectedMember?.name}`}{' '} - - manager of this team? - {' '} + + + {' '} + {`${selectedMember?.name}`}{' '} +
+ + manager of this team? + {' '}
{ - removeMemberHandler(selectedMember?.id.toString() || ''); - handleResetDialog(); - }, - }, - ]} header="Remove member? " onClose={handleResetDialog} > @@ -283,6 +275,26 @@ const TeamMemberDetails = () => { this team?
+ +
+ + +
diff --git a/src/assets/AlertIcon.tsx b/src/assets/AlertIcon.tsx new file mode 100644 index 0000000..71e325c --- /dev/null +++ b/src/assets/AlertIcon.tsx @@ -0,0 +1,38 @@ +import React, { FC, useState } from 'react'; +import { IconProps } from './types'; + +const AlertIcon: FC = ({ + width = '24', + height = '24', + onClick, + ...restProps +}) => { + return ( + + + + + + + + + ); +}; + +export default AlertIcon; diff --git a/src/services/hooks/useAuth.tsx b/src/services/hooks/useAuth.tsx new file mode 100644 index 0000000..0ff1b17 --- /dev/null +++ b/src/services/hooks/useAuth.tsx @@ -0,0 +1,12 @@ +import { useState, useEffect } from 'react'; +const useAuthData = () => { + const [userRole, setUserRole] = useState(''); + + useEffect(() => { + const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); + setUserRole(userData?.roles || []); + }, []); + + return userRole; +}; +export { useAuthData }; From 8f4bddab12b8d118b6525b6567df65128ac5004b Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Fri, 5 Jan 2024 19:35:15 +0530 Subject: [PATCH 15/39] TP-51166 | RBAC implemented ( admin, manager ,TM , non-team member ) --- .../TeamDetails/TeamDetails.module.scss | 34 ++- src/Pages/TeamRevamp/TeamDetails/index.tsx | 203 ++++++++++-------- .../TeamRevamp/TeamList/TeamList.module.scss | 1 + .../partials/CreateTeam/CreateTeam.tsx | 8 +- .../TeamMemberDetails.module.scss | 36 ++-- .../partials/TeamMemberDetails/index.tsx | 64 +++--- 6 files changed, 184 insertions(+), 162 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 34ca09c..ebb5c4e 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -1,5 +1,6 @@ .team-details-wrapper { - margin: 36px; + margin-left: 79px; + margin-top: 20px; } .team-input-wrapper { min-width: (320px); @@ -11,11 +12,18 @@ } .member-div { margin: 24px 24px 24px 0px; - color: var(--Grayscale-Content-3, #969696); + color: var(--navi-color-gray-c3); +} +.on-call-wrapper { + display: flex; + margin-top: 10px; +} +.team-oncall { + margin-right: 24px; } .team-name { margin: 24px 24px 24px 0px; - color: var(--Grayscale-Content-1, #1c1c1c); + color: var(--navi-color-gray-c1); } .slack-details-wrapper { display: inline-flex; @@ -24,24 +32,11 @@ .select-picker-wrapper { min-height: 80px !important; position: absolute; + z-index: 1; } .on-call-handler { position: relative; } -.change-manger-dropdown { - display: flex; -} -.arrow-icon { - width: 7px; - height: 7px; - padding-left: 10px; - margin-top: 5px; -} -.manager-picker-wrapper { - min-width: 103px; - min-height: 88px; - padding: 8px, 0px, 8px, 0px; -} .search-input { min-width: 320px !important; } @@ -56,17 +51,14 @@ cursor: pointer; } } - .team-name-wrapper { display: flex; align-items: center; + margin-top: 5px; } .update-details { margin-left: 900px; } -.hint-text { - color: red; -} .fallback-component { height: 100px; margin-left: 450px; diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index ec0a187..a357c49 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -31,9 +31,8 @@ const TeamDetails = () => { const { teamDetails } = teamData; const teamId = teamDetails?.id; const { isPending } = teamData; - // const Role = useAuthData(); + const Role = useAuthData(); const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); - const userID = userData?.clientId || []; const userEmail = userData?.emailId || ''; const [slackChannelId, setSlackChannelId] = useState(''); const [oncall, setOncall] = useState({ @@ -99,9 +98,11 @@ const TeamDetails = () => { }) as MutableRefObject; const handleOncallPicker = (): void => { + if (!isUserParticipant(teamDetails) && !Role.includes('Admin')) return; setOpenOnCall(!openOnCall); }; const handleOpenPsecOnCallPicker = (): void => { + if (!isUserParticipant(teamDetails) && !Role.includes('Admin')) return; setOpenPsecOnCall(!openPsecOnCall); }; const submitHandler = (): void => { @@ -134,6 +135,7 @@ const TeamDetails = () => {
); } + // const Role = useAuthData(); const managerEmail = teamDetails?.participants?.find( participant => participant.id === teamDetails?.managerId, )?.email; @@ -143,6 +145,7 @@ const TeamDetails = () => { ); return participantEmails?.includes(userEmail); }; + return (
@@ -150,107 +153,119 @@ const TeamDetails = () => { {teamDetails?.name} {' '}
-
- - SLACK DETAILS - {' '} -
-
-
- - Team channel - -
- + SLACK DETAILS + {' '} +
+
+
+ Team channel +
+ - - + withPointer + position={'right'} + > + + +
-
- -
-
- - Team on call - {' '} -
-
- { - setInput(e.target.value); - }} - placeholder="@oncall_handle" - containerClassName={styles['search-input']} - value={input || DEFAULT_TEAM_ONCALL} - disabled={!isUserParticipant(teamDetails)} - > -
- {openOnCall && ( - - )} -
-
-
- - PSEC on call - {' '} -
setPsecInput(e.target.value)} - placeholder="@psec_oncall_handle" - containerClassName={styles['search-input']} - value={psecInput || DEFAULT_PSEC_ONCALL} - disabled={!isUserParticipant(teamDetails)} - > + inputLabel="" + inputSize="medium" + placeholder="Enter channel I.D" + onChange={handleslackChannelId} + containerClassName={styles['team-input-wrapper']} + value={ + teamDetails?.webhookSlackChannelName !== 'not found' + ? `@${teamDetails?.webhookSlackChannelName}` + : '' + } + // value={'#' + teamDetails?.webhookSlackChannelName} + hintMsg="Channel name populates after entering id" + error={teamDetails?.webhookSlackChannelName === ''} + disabled={ + !isUserParticipant(teamDetails) && !Role.includes('Admin') + } + /> +
+
+
+ Team on call{' '} +
+
+ { + setInput(e.target.value); + }} + placeholder="@oncall_handle" + containerClassName={styles['search-input']} + value={input || DEFAULT_TEAM_ONCALL} + disabled={ + !isUserParticipant(teamDetails) && + !Role.includes('Admin') + } + > +
+ {openOnCall && ( + + )} +
+
+
+
+ PSEC on call +
+ setPsecInput(e.target.value)} + placeholder="@psec_oncall_handle" + containerClassName={styles['search-input']} + value={psecInput || DEFAULT_PSEC_ONCALL} + disabled={ + !isUserParticipant(teamDetails) && + !Role.includes('Admin') + } + > +
+ {openPsecOnCall && ( + + )} +
+
- {openPsecOnCall && ( - - )}
= ({ setTeam }) => { const [teamNameError, setTeamNameError] = useState(''); const [emailError, setEmailError] = useState(''); const [email, setEmail] = useState(''); - const [createdTeam, setCreatedTeam] = useState(''); + // const [createdTeam, setCreatedTeam] = useState(''); const validateTeamName = (value: string): void => { value = value.trim(); if (!regularExpression.test(value)) { @@ -88,7 +88,7 @@ const CreateTeam: React.FC = ({ setTeam }) => { dispatch(fetchTeamDetails(createdTeamId.toString())); setTeam({ label: teamName, value: createdTeamId }); dispatch(setSelectedTeam(createdTeamId)); - setCreatedTeam(createdTeamId); + // setCreatedTeam(createdTeamId); dispatch(setNewTeam(createdTeamId)); dispatch(fetchTeams()); }) @@ -144,7 +144,7 @@ const CreateTeam: React.FC = ({ setTeam }) => { value={teamName} onChange={handleTeamNameChange} error={teamNameError} - Icon={} + Icon={} placeholder="E.g. NaviPay_Operations" /> @@ -160,7 +160,7 @@ const CreateTeam: React.FC = ({ setTeam }) => { onChange={handleEmailChange} placeholder="name@navi.com" error={emailError} - Icon={} + Icon={} />
diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index c1fdbd1..6bfa24f 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -9,10 +9,8 @@ display: grid; grid-template-columns: 1fr 1fr 1fr; justify-content: space-around; - gap: 50px; } .item-details { - // background-color: aqua; margin-top: 12px; margin-left: 10px; width: calc(100% - 48px); @@ -20,7 +18,7 @@ .details-container { display: flex; // background-color: blueviolet; - width: 253px; + width: 252px; } .icon-wrapper { margin-top: 12px; @@ -34,6 +32,11 @@ height: 90px; // background-color: aqua; } +.team-member-view .make-manger-button, +.delete-icon { + display: none; +} + .items-wrapper .make-manger-button, .items-wrapper .delete-icon { display: none; @@ -50,7 +53,8 @@ margin-right: 6px; } .items-wrapper:hover { - background-color: #b3c7df; + // background-color: #b3c7df; + background-color: var(--navi-color-blue-bg); cursor: pointer; } @@ -58,15 +62,11 @@ width: 253px; border-radius: 8px; } -.input-wrapper { - // display: flex; - //background-color: aqua; -} .remove-button { - background-color: red; + background-color: var(--navi-color-red-base); height: 36px; width: 77px; - color: white; + color: var(--navi-color-gray-bg-primary); } .cancel-button { background: var(--navi-color-gray-bg-primary); @@ -87,14 +87,16 @@ justify-content: flex-end; margin-top: 32px; } -.abc { - //display: flex; -} -.xyz { - margin: 0px 0px 0px 5px; - //display: flex; -} .input-wrapper-child { display: flex; } +.horizontal-line { + display: flex; +} +.hr { + width: calc(90vh - 10px); + border: 1px (--navi-color-gray-border); + margin-top: 12px; + margin-left: 5px; +} diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 5b8169b..4969c8c 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -30,6 +30,7 @@ import AlertIcon from '@src/assets/AlertIcon'; import { TeamState } from '@src/slices/team1Slice'; import styles from './TeamMemberDetails.module.scss'; import FallbackComponent from '@src/components/Fallback'; +import { useAuthData } from '@src/services/hooks/useAuth'; type MemberType = { id: number; @@ -44,6 +45,12 @@ const TeamMemberDetails = () => { const { isPending } = teamData; const teamId = teamDetails?.id?.toString() || ''; const id = teamDetails?.id; + const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); + const Role = useAuthData(); + const userEmail = userData?.emailId || ''; + const managerEmail = teamDetails?.participants?.find( + (item: any) => item.id === teamDetails?.managerId, + )?.email; const [openDialog, setOpenDialog] = useState(false); const [openModal, setOpenModal] = useState(false); const [emailIds, setEmailIds] = useState(''); @@ -62,6 +69,12 @@ const TeamMemberDetails = () => { const manager = teamDetails?.participants?.find( (item: any) => item.id === teamDetails?.managerId, ); + const isUserParticipant = (teamDetails: any) => { + const participantEmails = teamDetails?.participants?.map( + (participant: any) => participant.email, + ); + return participantEmails?.includes(userEmail); + }; const handleAddMember = (e: React.ChangeEvent) => { setEmailIds(e.target.value); }; @@ -146,24 +159,28 @@ const TeamMemberDetails = () => { toast.error(`Error removing member from team : ${error.message}`); }); }; - const handleIconchange = item => { - setHovered({ - ishovered: false, - id: item.id, - }); + const handleHoverchange = item => { + if (userEmail === managerEmail || Role.includes('Admin')) { + setHovered({ + ishovered: false, + id: item.id, + }); + } }; const handleHoverIcon = item => { - setHovered({ - ishovered: true, - id: item.id, - }); + if (userEmail === managerEmail || Role.includes('Admin')) { + setHovered({ + ishovered: true, + id: item.id, + }); + } }; return (
- {' '} - MEMBERS
+ MEMBERS +
{' '}
{ onKeyPress={onKeyPressClickHandler} value={emailIds} containerClassName={styles['team-input-wrapper']} + disabled={!isUserParticipant(teamDetails) && !Role.includes('Admin')} />
@@ -182,11 +200,13 @@ const TeamMemberDetails = () => { className={ manager?.name === item.name ? styles['manager-wrapper'] + : userEmail !== managerEmail && !Role.includes('Admin') + ? styles['team-member-view'] : styles['items-wrapper'] } key={item.id} onMouseEnter={() => handleHoverIcon(item)} - onMouseLeave={() => handleIconchange(item)} + onMouseLeave={() => handleHoverchange(item)} >
@@ -205,8 +225,8 @@ const TeamMemberDetails = () => {
{' '} - {item.name}{' '} - {item.email}{' '} + {item.name} + {item.email} {manager?.name === item.name ? 'Manager' : ''} {' '} @@ -250,18 +270,10 @@ const TeamMemberDetails = () => { onClose={handleResetModal} >
-
- - Are you sure you want to make{' '} - - - {' '} - {`${selectedMember?.name}`}{' '} - -
- - manager of this team? - {' '} + + Are you sure you want to make {`${selectedMember?.name}`} manager + of this team? +
Date: Mon, 8 Jan 2024 15:04:12 +0530 Subject: [PATCH 16/39] TP-51166 | fixing design --- .../TeamDetails/TeamDetails.module.scss | 7 +- src/Pages/TeamRevamp/TeamDetails/index.tsx | 222 +++++++++--------- src/Pages/TeamRevamp/TeamList/index.tsx | 34 ++- src/Pages/TeamRevamp/index.tsx | 10 +- .../partials/CreateTeam/CreateTeam.tsx | 66 ++---- .../partials/TeamMemberDetails/index.tsx | 19 +- src/services/hooks/isFirstRender.tsx | 10 + src/slices/team1Slice.tsx | 22 +- 8 files changed, 193 insertions(+), 197 deletions(-) create mode 100644 src/services/hooks/isFirstRender.tsx diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index ebb5c4e..714689f 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -1,14 +1,12 @@ .team-details-wrapper { - margin-left: 79px; - margin-top: 20px; + margin: 20px 0px 0px 79px; } .team-input-wrapper { min-width: (320px); height: (38px); } .add-member { - margin-top: 72px; - margin-bottom: 24px; + margin: 72px 0px 24px 0px; } .member-div { margin: 24px 24px 24px 0px; @@ -45,7 +43,6 @@ width: 20px; margin-left: 4px; margin-top: 5px; - display: flex; align-items: center; :hover { cursor: pointer; diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index a357c49..93771f0 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,6 +1,5 @@ import { useDispatch, useSelector } from 'react-redux'; -import { fetchTeamDetails, selectSearchTeamData } from '@src/slices/team1Slice'; -import PersonIcon from '@src/assets/PersonIcon'; +import { useEffect, useState, useRef, MutableRefObject } from 'react'; import { BorderedInput, Button, @@ -8,22 +7,21 @@ import { Typography, } from '@navi/web-ui/lib/primitives'; -import { useEffect, useState, useRef, MutableRefObject } from 'react'; +import { SelectPicker } from '@navi/web-ui/lib/components'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; +import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; +import { fetchTeamDetails, selectSearchTeamData } from '@src/slices/team1Slice'; import { UPDATE_TEAM_DATA } from '../partials/constants'; import { ApiService } from '@src/services/api'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch } from '@src/store'; import { getBots } from '../partials/Bots'; -import styles from './TeamDetails.module.scss'; - -import { SelectPicker } from '@navi/web-ui/lib/components'; -import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; -import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { TeamState } from '@src/slices/team1Slice'; import TeamMemberDetails from '../partials/TeamMemberDetails'; import useOutsideClick from '@src/services/hooks/useOustideClick'; import FallbackComponent from '@src/components/Fallback'; import { useAuthData } from '@src/services/hooks/useAuth'; +import styles from './TeamDetails.module.scss'; const TeamDetails = () => { const dispatch = useDispatch(); @@ -49,6 +47,11 @@ const TeamDetails = () => { const [openOnCall, setOpenOnCall] = useState(false); const [openPsecOnCall, setOpenPsecOnCall] = useState(false); const searchRef = useRef(null); + + useEffect(() => { + setInput(''); + setPsecInput(''); + }, [teamData]); const DEFAULT_TEAM_ONCALL = teamDetails?.oncall?.name ? `@${teamDetails?.oncall?.name}` : ''; @@ -58,12 +61,12 @@ const TeamDetails = () => { teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' ? '@' + teamDetails?.pse_oncall?.name : ''; - - useEffect(() => { - setInput(''); - setPsecInput(''); - }, [teamData]); - + const webChannelId = + teamDetails?.webhookSlackChannelName !== 'not found' + ? `@${teamDetails?.webhookSlackChannelName}` + : teamDetails?.webhookSlackChannelId + ? teamDetails?.webhookSlackChannelId + : ''; const handleslackChannelId = (e: React.ChangeEvent) => { setSlackChannelId(e.target.value); }; @@ -135,7 +138,6 @@ const TeamDetails = () => {
); } - // const Role = useAuthData(); const managerEmail = teamDetails?.participants?.find( participant => participant.id === teamDetails?.managerId, )?.email; @@ -148,114 +150,102 @@ const TeamDetails = () => { return (
-
- - {teamDetails?.name} + + {teamDetails?.name} + {' '} +
+ + SLACK DETAILS {' '} -
-
-
- - SLACK DETAILS - {' '} -
-
-
- Team channel -
- Team channel +
+ - - -
+ withPointer + position={'right'} + > + +
-
-
-
- Team on call{' '} -
-
- { - setInput(e.target.value); - }} - placeholder="@oncall_handle" - containerClassName={styles['search-input']} - value={input || DEFAULT_TEAM_ONCALL} - disabled={ - !isUserParticipant(teamDetails) && - !Role.includes('Admin') - } - > -
- {openOnCall && ( - - )} + +
+
+
+ Team on call{' '} +
+
+ { + setInput(e.target.value); + }} + placeholder="@oncall_handle" + containerClassName={styles['search-input']} + value={input || DEFAULT_TEAM_ONCALL} + disabled={ + !isUserParticipant(teamDetails) && !Role.includes('Admin') + } + >
+ {openOnCall && ( + + )}
-
-
- PSEC on call -
- setPsecInput(e.target.value)} - placeholder="@psec_oncall_handle" - containerClassName={styles['search-input']} - value={psecInput || DEFAULT_PSEC_ONCALL} - disabled={ - !isUserParticipant(teamDetails) && - !Role.includes('Admin') - } - > -
- {openPsecOnCall && ( - - )} +
+
+
+ PSEC on call +
+ setPsecInput(e.target.value)} + placeholder="@psec_oncall_handle" + containerClassName={styles['search-input']} + value={psecInput || DEFAULT_PSEC_ONCALL} + disabled={ + !isUserParticipant(teamDetails) && !Role.includes('Admin') + } + >
+ {openPsecOnCall && ( + + )}
diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index a629da4..8437033 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -10,6 +10,7 @@ import { useAuthData } from '@src/services/hooks/useAuth'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; import styles from './TeamList.module.scss'; +import useIsFirstRender from '@src/services/hooks/isFirstRender'; const CutomeTeamOptions = option => { return ( @@ -27,15 +28,22 @@ const CutomeTeamOptions = option => { const TeamList = () => { const dispatch = useDispatch(); + const Role = useAuthData(); const teamList = useSelector((state: RootState) => state.team1.teams.data); const defaultTeam = useSelector( (state: RootState) => state.team1.teams.data[0], ); const newTeam = useSelector((state: RootState) => state.team1.teams.newTeam); - const Role = useAuthData(); const [input, setInput] = useState(''); const [team, setTeam] = useState(); const scroll = useRef(false); + console.log('team', team); + // useEffect(() => { + // console.log('defaultTeam', defaultTeam); + // if () { + // setTeam({ label: defaultTeam?.name, value: defaultTeam?.id }); + // } + // }, [defaultTeam]); const scrollintoView = (event: string): void => { if (!event) return; @@ -82,7 +90,9 @@ const TeamList = () => { } }, [teamList]); - const handleInputChange = (event: React.ChangeEvent) => { + const handleInputChange = ( + event: React.ChangeEvent, + ): void => { setInput(event.target.value); }; const createHandler = () => { @@ -92,12 +102,22 @@ const TeamList = () => { dispatch(fetchTeamDetails(val)); }; - const handleSelectionChange = (event: SelectPickerOptionProps) => { + const handleSelectionChange = (event): void => { setTeam(event); fetchDetails(event.value.toString()); scroll.current = false; }; - + function getSelectedValue(team, defaultTeam) { + if (team && team.value) { + return team.value; + } else if (defaultTeam && defaultTeam.id) { + return defaultTeam.id; + } else { + return ''; + } + } + const selectedTeam = getSelectedValue(team, defaultTeam); + console.log('selectedTeam', selectedTeam); return (
@@ -133,12 +153,12 @@ const TeamList = () => {
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 99e6526..57e95f3 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -19,7 +19,10 @@ const TeamRevamp: FC = () => { const dispatch = useDispatch(); const teamData: TeamState = useSelector(selectSearchTeamData); const { data } = teamData; - + const { teamDetails } = teamData; + const teamId = teamDetails?.id || ''; + const { selectedTeam } = teamData; + const selectedTeamId = selectedTeam?.id || ''; useEffect(() => { dispatch(fetchTeams()); }, []); @@ -32,6 +35,11 @@ const TeamRevamp: FC = () => { dispatch(setSelectedTeam(data[0])); } }, []); + useEffect(() => { + if (selectedTeam) { + dispatch(fetchTeamDetails(selectedTeamId.toString())); + } + }, [selectedTeam]); return (
diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index 6772923..fccca93 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -7,14 +7,11 @@ import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch, RootState } from '@src/store'; import { ApiService } from '@src/services/api'; import { - TeamState, fetchTeamDetails, fetchTeams, selectSearchTeamData, setModalOpen, setNewTeam, - setSelectedTeam, - setTeamData, } from '@src/slices/team1Slice'; import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; @@ -33,45 +30,40 @@ interface CreateTeamProps { const CreateTeam: React.FC = ({ setTeam }) => { const dispatch = useDispatch(); - const teamData: TeamState = useSelector(selectSearchTeamData); const { modalOpen } = useSelector(selectSearchTeamData); - const [teamName, setTeamName] = useState(''); - const [teamNameError, setTeamNameError] = useState(''); - const [emailError, setEmailError] = useState(''); - const [email, setEmail] = useState(''); - // const [createdTeam, setCreatedTeam] = useState(''); + const [teamName, setTeamName] = useState(''); + const [teamNameError, setTeamNameError] = useState(''); + const [emailError, setEmailError] = useState(''); + const [email, setEmail] = useState(''); + const newTeam = useSelector((state: RootState) => state.team1.teams.newTeam); const validateTeamName = (value: string): void => { value = value.trim(); - if (!regularExpression.test(value)) { - setTeamNameError( - 'Min. 3 characters required. Use spaces, ‘_’, or ‘-’ only', - ); - } else { - setTeamNameError(''); - } + setTeamNameError( + !regularExpression.test(value) + ? 'Min. 3 characters required. Use spaces, ‘_’, or ‘-’ only' + : '', + ); }; const validateEmail = (value: string): void => { const validEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; - if (validEmail.test(value)) { - if (!emailRegularExpression.test(value)) { - setEmailError('Please enter a Navi email ID'); - } else { - setEmailError(''); - } - } + setEmailError( + validEmail.test(value) + ? !emailRegularExpression.test(value) + ? 'Please enter a Navi email ID' + : '' + : '', + ); }; - - const selectedTeam = useSelector( - (state: RootState) => state.team1.teams.selectedTeam, - ); - const handleTeamNameChange = (e: React.ChangeEvent) => { + const handleTeamNameChange = ( + e: React.ChangeEvent, + ): void => { const inputValue = e.target.value; setTeamName(inputValue); validateTeamName(inputValue); }; - const handleEmailChange = (e: React.ChangeEvent) => { + const handleEmailChange = (e: React.ChangeEvent): void => { const inputValue = e.target.value; setEmail(inputValue); validateEmail(inputValue); @@ -79,16 +71,14 @@ const CreateTeam: React.FC = ({ setTeam }) => { const addTeamHandler = (): void => { ApiService.post(CREATE_TEAM, { name: teamName, manager_email: email }) - .then((response: any) => { + .then(response => { const toastMessage = `${response?.data?.data?.message}`; const createdTeamId = response?.data?.data?.id; - const teamName = response?.data?.data?.name; + const createdTeamName = response?.data?.data?.name; toast.success(toastMessage); clearErrors(); dispatch(fetchTeamDetails(createdTeamId.toString())); - setTeam({ label: teamName, value: createdTeamId }); - dispatch(setSelectedTeam(createdTeamId)); - // setCreatedTeam(createdTeamId); + setTeam({ label: createdTeamName, value: createdTeamId }); dispatch(setNewTeam(createdTeamId)); dispatch(fetchTeams()); }) @@ -102,13 +92,7 @@ const CreateTeam: React.FC = ({ setTeam }) => { }); }; - useEffect(() => { - if (selectedTeam) { - dispatch(fetchTeamDetails(selectedTeam?.id)); - } - }, [selectedTeam]); - - const clearErrors = () => { + const clearErrors = (): void => { dispatch(setModalOpen(false)); setTeamNameError(''); setEmailError(''); diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 4969c8c..aeabd32 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -41,12 +41,11 @@ type MemberType = { const TeamMemberDetails = () => { const teamData: TeamState = useSelector(selectSearchTeamData); const dispatch = useDispatch(); - const { teamDetails } = teamData; - const { isPending } = teamData; - const teamId = teamDetails?.id?.toString() || ''; - const id = teamDetails?.id; const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); const Role = useAuthData(); + const { teamDetails } = teamData; + const { isPending } = teamData; + const teamId = teamDetails?.id; const userEmail = userData?.emailId || ''; const managerEmail = teamDetails?.participants?.find( (item: any) => item.id === teamDetails?.managerId, @@ -93,12 +92,12 @@ const TeamMemberDetails = () => { ? emailIds.split(',').map(item => item?.trim()) : [emailIds]; ApiService.post(endPoint, { - id: id, + id: teamId, workEmailIds: finalSlackData, }) .then(response => { toast.success(response?.data?.data); - dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeamDetails(teamId?.toString() || '')); dispatch(fetchTeams()); }) .catch(error => { @@ -112,12 +111,12 @@ const TeamMemberDetails = () => { }; const handleMakeManager = (memberID: string) => { - const endpoint = MAKE_MANAGER(teamId, memberID); + const endpoint = MAKE_MANAGER(teamId?.toString() || '', memberID); ApiService.patch(endpoint, {}) .then(response => { if (response.status === 200) { toast.success(response.data.data); - dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeamDetails(teamId?.toString() || '')); } else { toast.error(response.data.error.message); } @@ -142,14 +141,14 @@ const TeamMemberDetails = () => { setOpenDialog(false); }; const removeMemberHandler = (memberID: string): void => { - const endpoint = REMOVE_TEAM_MEMBER(teamId, memberID); + const endpoint = REMOVE_TEAM_MEMBER(teamId?.toString() || '', memberID); ApiService.delete(endpoint) .then(response => { if (response.status === 200) { toast(response.data.data, { icon: , }); - dispatch(fetchTeamDetails(teamId)); + dispatch(fetchTeamDetails(teamId?.toString() || '')); dispatch(fetchTeams()); } else { toast.error(response.data.error.message); diff --git a/src/services/hooks/isFirstRender.tsx b/src/services/hooks/isFirstRender.tsx new file mode 100644 index 0000000..2fdb81e --- /dev/null +++ b/src/services/hooks/isFirstRender.tsx @@ -0,0 +1,10 @@ +import { useRef } from 'react'; +const useIsFirstRender = (): boolean => { + const isFirst = useRef(true); + if (isFirst.current) { + isFirst.current = false; + return true; + } + return isFirst.current; +}; +export default useIsFirstRender; diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index 3a8fdd1..741ce3b 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import TeamDetails from '@src/Pages/TeamRevamp/TeamDetails'; + import { FETCH_SINGLE_TEAM_DATA, FETCH_TEAM_DATA, @@ -15,7 +15,7 @@ export interface TeamState { modalOpen: boolean; teamDetails: TeamsDetail; error: string; - selectedTeam: number | string | any; + selectedTeam: TeamsDetail; newTeam: number | string; } @@ -30,7 +30,7 @@ const initialState: TeamInitialState = { isPending: false, modalOpen: false, teamDetails: {}, - selectedTeam: [], + selectedTeam: {}, newTeam: '', }, }; @@ -47,13 +47,6 @@ export const fetchTeamDetails = createAsyncThunk( return await ApiService.get(endPoint); }, ); -// export const fetchDefaultTeamDetails = createAsyncThunk( -// 'team/fetchDefaultTeamDetails', -// async (id: string) => { -// const endPoint = FETCH_SINGLE_TEAM_DATA(id); -// return await ApiService.get(endPoint); -// }, -// ); const team1Slice = createSlice({ name: 'team1', @@ -89,13 +82,13 @@ const team1Slice = createSlice({ state.teams.error = action.error.message ?? 'Something went wrong'; state.teams.data = {}; state.teams.isPending = false; - state.teams.selectedTeam = []; + state.teams.selectedTeam = {}; }); builder.addCase(fetchTeams.pending, (state, action) => { state.teams.isPending = true; state.teams.data = {}; state.teams.error = ''; - state.teams.selectedTeam = []; + state.teams.selectedTeam = {}; }); builder.addCase(fetchTeamDetails.fulfilled, (state, action) => { state.teams.teamDetails = action.payload.data.data; @@ -112,11 +105,6 @@ const team1Slice = createSlice({ state.teams.teamDetails = {}; state.teams.error = ''; }); - // builder.addCase(fetchDefaultTeamDetails.fulfilled, (state, action) => { - // state.teams.teamDetails = action.payload.data.data; - // state.teams.isPending = false; - // state.teams.error = ''; - // }); }, }); From f51ad86e45de6e3915ed7b022fee4c75456e73ff Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 9 Jan 2024 12:54:44 +0530 Subject: [PATCH 17/39] TP-51166 | fixed select picker bug --- package.json | 3 +-- src/Pages/TeamRevamp/TeamList/index.tsx | 25 ++++++++++++------------- src/Pages/TeamRevamp/index.tsx | 4 ++-- src/slices/team1Slice.tsx | 7 +------ 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index f528df1..d3db39b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "build": "yarn run clear-build && tsc && vite build && yarn run copy-config", "lint": "eslint --fix \"**/?*.{ts,tsx,js,jsx}\"", "eslint-check": "eslint \"**/?*.{ts,tsx,js,jsx}\"", - "prepare": "husky install", "pretty": "prettier --write \"./**/*.{js,jsx,ts,tsx,css,scss,md}\"", "prettier-check": "prettier --check \"./**/*.{js,jsx,ts,tsx,css,scss,md}\"", "eslint-check-quiet": "yarn eslint-check --quiet" @@ -33,7 +32,7 @@ }, "dependencies": { "@navi/dark-knight": "^1.0.13", - "@navi/web-ui": "^1.58.13", + "@navi/web-ui": "^1.59.4", "@reduxjs/toolkit": "^1.9.7", "@stoddabr/react-tableau-embed-live": "^0.3.26", "antd": "^5.9.4", diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 8437033..2e55e98 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -107,17 +107,16 @@ const TeamList = () => { fetchDetails(event.value.toString()); scroll.current = false; }; - function getSelectedValue(team, defaultTeam) { - if (team && team.value) { - return team.value; - } else if (defaultTeam && defaultTeam.id) { - return defaultTeam.id; - } else { - return ''; - } - } - const selectedTeam = getSelectedValue(team, defaultTeam); - console.log('selectedTeam', selectedTeam); + // function getSelectedValue(team, defaultTeam) { + // if (team && team.value) { + // return team.value; + // } else if (defaultTeam && defaultTeam.id) { + // return defaultTeam.id; + // } else { + // return ''; + // } + // } + // const selectedTeam = getSelectedValue(team, defaultTeam); return (
@@ -154,11 +153,11 @@ const TeamList = () => {
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 57e95f3..1ceeae4 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -19,10 +19,9 @@ const TeamRevamp: FC = () => { const dispatch = useDispatch(); const teamData: TeamState = useSelector(selectSearchTeamData); const { data } = teamData; - const { teamDetails } = teamData; - const teamId = teamDetails?.id || ''; const { selectedTeam } = teamData; const selectedTeamId = selectedTeam?.id || ''; + useEffect(() => { dispatch(fetchTeams()); }, []); @@ -40,6 +39,7 @@ const TeamRevamp: FC = () => { dispatch(fetchTeamDetails(selectedTeamId.toString())); } }, [selectedTeam]); + return (
diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index 741ce3b..a3febd9 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -30,7 +30,7 @@ const initialState: TeamInitialState = { isPending: false, modalOpen: false, teamDetails: {}, - selectedTeam: {}, + selectedTeam: { id: 1, name: '' }, newTeam: '', }, }; @@ -74,21 +74,16 @@ const team1Slice = createSlice({ state.teams.data = action.payload.data.data; state.teams.isPending = false; state.teams.error = ''; - if (!state.teams.selectedTeam) { - state.teams.selectedTeam = action.payload.data.data[0]; - } }); builder.addCase(fetchTeams.rejected, (state, action) => { state.teams.error = action.error.message ?? 'Something went wrong'; state.teams.data = {}; state.teams.isPending = false; - state.teams.selectedTeam = {}; }); builder.addCase(fetchTeams.pending, (state, action) => { state.teams.isPending = true; state.teams.data = {}; state.teams.error = ''; - state.teams.selectedTeam = {}; }); builder.addCase(fetchTeamDetails.fulfilled, (state, action) => { state.teams.teamDetails = action.payload.data.data; From 4fa914c754ee30cbc3a3eb74776246bddd95af4a Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 9 Jan 2024 18:57:50 +0530 Subject: [PATCH 18/39] TP-51166 | optimization and design enhancement --- .../TeamDetails/TeamDetails.module.scss | 27 +-- src/Pages/TeamRevamp/TeamDetails/index.tsx | 172 +++++++++--------- src/Pages/TeamRevamp/TeamList/index.tsx | 17 -- src/Pages/TeamRevamp/index.tsx | 4 +- .../partials/CreateTeam/CreateTeam.tsx | 8 +- .../TeamMemberDetails.module.scss | 5 +- .../partials/TeamMemberDetails/index.tsx | 153 +++++++++------- src/Pages/TeamRevamp/partials/constants.tsx | 52 +++++- 8 files changed, 244 insertions(+), 194 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 714689f..f38bc1c 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -9,7 +9,7 @@ margin: 72px 0px 24px 0px; } .member-div { - margin: 24px 24px 24px 0px; + margin: 32px 0px 0px 0px; color: var(--navi-color-gray-c3); } .on-call-wrapper { @@ -25,7 +25,6 @@ } .slack-details-wrapper { display: inline-flex; - gap: 24px; } .select-picker-wrapper { min-height: 80px !important; @@ -38,23 +37,21 @@ .search-input { min-width: 320px !important; } -.info-icon { - height: 20px; - width: 20px; - margin-left: 4px; - margin-top: 5px; - align-items: center; - :hover { - cursor: pointer; - } -} -.team-name-wrapper { + +.info-icon-wrapper { display: flex; align-items: center; margin-top: 5px; + gap: 2px; +} +.info-icon { + width: 16px; + height: 16px; + margin-right: 4px; } .update-details { margin-left: 900px; + margin-bottom: 31px; } .fallback-component { height: 100px; @@ -65,3 +62,7 @@ opacity: 0; pointer-events: none; } +.team-channel-wrapper { + margin-right: 24px; + margin-top: 5px; +} diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 93771f0..d2444c7 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -153,100 +153,96 @@ const TeamDetails = () => { {teamDetails?.name} {' '} -
- - SLACK DETAILS - {' '} -
-
-
- Team channel -
- + SLACK DETAILS + {' '} +
+
+
+ Team channel +
+ - - -
+ withPointer + position={'right'} + > + +
-
-
-
- Team on call{' '} -
-
- { - setInput(e.target.value); - }} - placeholder="@oncall_handle" - containerClassName={styles['search-input']} - value={input || DEFAULT_TEAM_ONCALL} - disabled={ - !isUserParticipant(teamDetails) && !Role.includes('Admin') - } - > -
- {openOnCall && ( - - )} + +
+
+
+ Team on call{' '} +
+
+ { + setInput(e.target.value); + }} + placeholder="@oncall_handle" + containerClassName={styles['search-input']} + value={input || DEFAULT_TEAM_ONCALL} + disabled={ + !isUserParticipant(teamDetails) && !Role.includes('Admin') + } + >
+ {openOnCall && ( + + )}
-
-
- PSEC on call -
- setPsecInput(e.target.value)} - placeholder="@psec_oncall_handle" - containerClassName={styles['search-input']} - value={psecInput || DEFAULT_PSEC_ONCALL} - disabled={ - !isUserParticipant(teamDetails) && !Role.includes('Admin') - } - > -
- {openPsecOnCall && ( - - )} +
+
+
+ PSEC on call +
+ setPsecInput(e.target.value)} + placeholder="@psec_oncall_handle" + containerClassName={styles['search-input']} + value={psecInput || DEFAULT_PSEC_ONCALL} + disabled={ + !isUserParticipant(teamDetails) && !Role.includes('Admin') + } + >
+ {openPsecOnCall && ( + + )}
diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 2e55e98..d419b7c 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -37,13 +37,6 @@ const TeamList = () => { const [input, setInput] = useState(''); const [team, setTeam] = useState(); const scroll = useRef(false); - console.log('team', team); - // useEffect(() => { - // console.log('defaultTeam', defaultTeam); - // if () { - // setTeam({ label: defaultTeam?.name, value: defaultTeam?.id }); - // } - // }, [defaultTeam]); const scrollintoView = (event: string): void => { if (!event) return; @@ -107,16 +100,6 @@ const TeamList = () => { fetchDetails(event.value.toString()); scroll.current = false; }; - // function getSelectedValue(team, defaultTeam) { - // if (team && team.value) { - // return team.value; - // } else if (defaultTeam && defaultTeam.id) { - // return defaultTeam.id; - // } else { - // return ''; - // } - // } - // const selectedTeam = getSelectedValue(team, defaultTeam); return (
diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 1ceeae4..acf656b 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -1,7 +1,7 @@ import { FC, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import Typography from '@navi/web-ui/lib/primitives/Typography'; -import { AppDispatch, RootState } from '@src/store'; +import { AppDispatch } from '@src/store'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; import TeamDetails from './TeamDetails'; import { @@ -11,8 +11,8 @@ import { selectSearchTeamData, setSelectedTeam, } from '@src/slices/team1Slice'; -import styles from './Team.module.scss'; import TeamList from './TeamList'; +import styles from './Team.module.scss'; import { fetchAllBots } from './partials/Bots'; const TeamRevamp: FC = () => { diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index fccca93..9e749b5 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -1,10 +1,11 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Typography } from '@navi/web-ui/lib/primitives'; import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { BorderedInput, ModalDialog } from '@navi/web-ui/lib/primitives'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import { AppDispatch, RootState } from '@src/store'; +import { AppDispatch } from '@src/store'; import { ApiService } from '@src/services/api'; import { fetchTeamDetails, @@ -13,14 +14,12 @@ import { setModalOpen, setNewTeam, } from '@src/slices/team1Slice'; - import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; import { regularExpression, emailRegularExpression, } from '@src/Pages/TeamRevamp/partials/constants'; import styles from './CreateTeam.module.scss'; -import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; interface CreateTeamProps { setTeam: React.Dispatch< @@ -35,7 +34,6 @@ const CreateTeam: React.FC = ({ setTeam }) => { const [teamNameError, setTeamNameError] = useState(''); const [emailError, setEmailError] = useState(''); const [email, setEmail] = useState(''); - const newTeam = useSelector((state: RootState) => state.team1.teams.newTeam); const validateTeamName = (value: string): void => { value = value.trim(); setTeamNameError( diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index 6bfa24f..e0122bc 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -17,20 +17,17 @@ } .details-container { display: flex; - // background-color: blueviolet; width: 252px; } .icon-wrapper { margin-top: 12px; width: 32px; margin-left: 6px; - // background-color: brown; } .items-wrapper { width: 253px; border-radius: 8px; height: 90px; - // background-color: aqua; } .team-member-view .make-manger-button, .delete-icon { @@ -53,7 +50,6 @@ margin-right: 6px; } .items-wrapper:hover { - // background-color: #b3c7df; background-color: var(--navi-color-blue-bg); cursor: pointer; } @@ -81,6 +77,7 @@ .team-input-wrapper { width: 510px; height: 36px; + margin-top: 12px; } .footer-wrapper { display: flex; diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index aeabd32..d08612d 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -1,5 +1,5 @@ import { useDispatch, useSelector } from 'react-redux'; -import { useEffect, useState, useRef } from 'react'; +import { useState, useReducer } from 'react'; import { fetchTeamDetails, fetchTeams, @@ -8,37 +8,39 @@ import { import { BorderedInput, Button, - Tooltip, Typography, ModalDialog, - IconButton, } from '@navi/web-ui/lib/primitives'; - -import { - MAKE_MANAGER, - REMOVE_TEAM_MEMBER, - UPDATE_TEAM_DATA, -} from '@src/Pages/TeamRevamp/partials/constants'; -import { ApiService } from '@src/services/api'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import { AppDispatch } from '@src/store'; import PersonIconFill from '@src/assets/PersonIconFill'; import ManagerIconFill from '@src/assets/ManagerIconFill'; import MemberIcon from '@src/assets/MemberIcon'; import DeleteIcon from '@src/assets/DeleteIcon'; import AlertIcon from '@src/assets/AlertIcon'; -import { TeamState } from '@src/slices/team1Slice'; -import styles from './TeamMemberDetails.module.scss'; +import { ApiService } from '@src/services/api'; +import { AppDispatch } from '@src/store'; import FallbackComponent from '@src/components/Fallback'; import { useAuthData } from '@src/services/hooks/useAuth'; +import { TeamState } from '@src/slices/team1Slice'; +import { + MAKE_MANAGER, + REMOVE_TEAM_MEMBER, + TeamsDetail, + UPDATE_TEAM_DATA, + actionTypes, + initialState, + reducer, +} from '@src/Pages/TeamRevamp/partials/constants'; +import styles from './TeamMemberDetails.module.scss'; type MemberType = { - id: number; + id: string; name: string; email: string; }; const TeamMemberDetails = () => { + const [state, dispatchData] = useReducer(reducer, initialState); const teamData: TeamState = useSelector(selectSearchTeamData); const dispatch = useDispatch(); const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); @@ -48,49 +50,47 @@ const TeamMemberDetails = () => { const teamId = teamDetails?.id; const userEmail = userData?.emailId || ''; const managerEmail = teamDetails?.participants?.find( - (item: any) => item.id === teamDetails?.managerId, + item => item.id === teamDetails?.managerId, )?.email; - const [openDialog, setOpenDialog] = useState(false); - const [openModal, setOpenModal] = useState(false); - const [emailIds, setEmailIds] = useState(''); - const [selectedMember, setSelectedMember] = useState(); - const [hovered, setHovered] = useState({ - ishovered: false, - id: '', - }); - - const teamMembers = teamDetails?.participants?.map((item: any) => ({ + const manager = teamDetails?.participants?.find( + item => item.id === teamDetails?.managerId, + ); + const teamMembers = teamDetails?.participants?.map(item => ({ name: item.name, email: item.email, id: item.id, })); - const manager = teamDetails?.participants?.find( - (item: any) => item.id === teamDetails?.managerId, - ); - const isUserParticipant = (teamDetails: any) => { + const isUserParticipant = (teamDetails: TeamsDetail) => { const participantEmails = teamDetails?.participants?.map( (participant: any) => participant.email, ); return participantEmails?.includes(userEmail); }; - const handleAddMember = (e: React.ChangeEvent) => { - setEmailIds(e.target.value); + const handleAddMember = (e: React.ChangeEvent): void => { + dispatchData({ + type: actionTypes.SET_EMAIL_IDS, + payload: e.target.value, + }); }; if (isPending) { return ; } - const onKeyPressClickHandler = (event: React.KeyboardEvent) => { + const onKeyPressClickHandler = (event: React.KeyboardEvent): void => { if (event.key === 'Enter') { addMemberHandler(); } - setEmailIds(''); + + dispatchData({ + type: actionTypes.SET_EMAIL_IDS, + payload: '', + }); }; const addMemberHandler = (): void => { const endPoint = UPDATE_TEAM_DATA(); - const finalSlackData = emailIds?.includes(',') - ? emailIds.split(',').map(item => item?.trim()) - : [emailIds]; + const finalSlackData = state.emailIds?.includes(',') + ? state.emailIds.split(',').map(item => item?.trim()) + : [state.emailIds]; ApiService.post(endPoint, { id: teamId, workEmailIds: finalSlackData, @@ -126,19 +126,37 @@ const TeamMemberDetails = () => { }); }; - const handleOpenModal = member => () => { - setSelectedMember(member); - setOpenModal(true); + const handleOpenModal = (member: MemberType) => () => { + dispatchData({ + type: actionTypes.SET_SELECTED_MEMBER, + payload: member, + }); + dispatchData({ + type: actionTypes.SET_OPEN_MODAL, + payload: true, + }); }; - const handleResetModal = () => { - setOpenModal(false); + const handleResetModal = (): void => { + dispatchData({ + type: actionTypes.SET_OPEN_MODAL, + payload: false, + }); }; - const handleOpenDialog = member => () => { - setSelectedMember(member); - setOpenDialog(true); + const handleOpenDialog = (member: MemberType) => () => { + dispatchData({ + type: actionTypes.SET_SELECTED_MEMBER, + payload: member, + }); + dispatchData({ + type: actionTypes.SET_OPEN_DIALOG, + payload: true, + }); }; const handleResetDialog = () => { - setOpenDialog(false); + dispatchData({ + type: actionTypes.SET_OPEN_DIALOG, + payload: false, + }); }; const removeMemberHandler = (memberID: string): void => { const endpoint = REMOVE_TEAM_MEMBER(teamId?.toString() || '', memberID); @@ -158,19 +176,25 @@ const TeamMemberDetails = () => { toast.error(`Error removing member from team : ${error.message}`); }); }; - const handleHoverchange = item => { + const handleHoverchange = (item: MemberType) => { if (userEmail === managerEmail || Role.includes('Admin')) { - setHovered({ - ishovered: false, - id: item.id, + dispatchData({ + type: actionTypes.SET_HOVERED, + payload: { + ishovered: false, + id: item.id, + }, }); } }; - const handleHoverIcon = item => { + const handleHoverIcon = (item: MemberType) => { if (userEmail === managerEmail || Role.includes('Admin')) { - setHovered({ - ishovered: true, - id: item.id, + dispatchData({ + type: actionTypes.SET_HOVERED, + payload: { + ishovered: true, + id: item.id, + }, }); } }; @@ -187,14 +211,14 @@ const TeamMemberDetails = () => { onChange={handleAddMember} hintMsg="Press enter to add e-mails " onKeyPress={onKeyPressClickHandler} - value={emailIds} + value={state.emailIds} containerClassName={styles['team-input-wrapper']} disabled={!isUserParticipant(teamDetails) && !Role.includes('Admin')} />
- {teamMembers?.map((item: any) => ( + {teamMembers?.map(item => (
{ ) : (
- {hovered.ishovered && hovered.id === item.id ? ( + {state.hovered.ishovered && + state.hovered.id === item.id ? ( ) : ( @@ -251,7 +276,7 @@ const TeamMemberDetails = () => { ))}
{ { label: 'Update', onClick: () => { - handleMakeManager(selectedMember?.id.toString() || ''); + handleMakeManager(state.selectedMember?.id.toString() || ''); handleResetModal(); }, }, @@ -270,20 +295,20 @@ const TeamMemberDetails = () => { >
- Are you sure you want to make {`${selectedMember?.name}`} manager - of this team? + Are you sure you want to make {`${state.selectedMember?.name}`}{' '} + manager of this team?
- Are you sure you want to remove {`${selectedMember?.name}`} from - this team? + Are you sure you want to remove {`${state.selectedMember?.name}`}{' '} + from this team?
@@ -298,7 +323,7 @@ const TeamMemberDetails = () => {
-
+
Team on call{' '} -
-
- { - setInput(e.target.value); - }} - placeholder="@oncall_handle" - containerClassName={styles['search-input']} - value={input || DEFAULT_TEAM_ONCALL} - disabled={ - !isUserParticipant(teamDetails) && !Role.includes('Admin') - } - > -
- {openOnCall && ( - - )} +
+
+ {state.openOnCall && ( + + )}
-
-
- PSEC on call -
+ PSEC on call +
+ +
+ {state.openPsecOnCall && ( + + )} +
+
- {openPsecOnCall && ( - - )} + Update details +
-
- -
); diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index a72a52c..71070bc 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -11,7 +11,12 @@ import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; import styles from './TeamList.module.scss'; -const CutomeTeamOptions = option => { +type Options = { + label: string; + value: number; + count: number; +}; +const CustomTeamOptions = (option: Options): JSX.Element => { return (
{option.label}
@@ -138,7 +143,7 @@ const TeamList = () => { multiSelect={false} updateSearch={input || ''} wrapperClasses={styles['select-picker-wrapper']} - customOptionTemplate={CutomeTeamOptions} + customOptionTemplate={CustomTeamOptions as any} selectedValue={team?.value ?? defaultTeam?.id ?? ''} />
diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index 708955e..3b42b3a 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -91,13 +91,12 @@ .horizontal-line { display: flex; } -.hr { - width: calc(90vh - 10px); +.hr-tag { + width: 100%; border: 1px (--navi-color-gray-border); margin-top: 12px; margin-left: 5px; } .fallback-component { - height: 100px; - margin-left: calc(100vh - 24px); + height: 200px; } diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index fe89731..8e82029 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -47,7 +47,7 @@ const TeamMemberDetails = () => { const Role = useAuthData(); const { teamDetails } = teamData; const { isPending } = teamData; - + const [loading, setLoading] = useState(false); const teamId = teamDetails?.id; const userEmail = userData?.emailId || ''; const managerEmail = teamDetails?.participants?.find( @@ -74,7 +74,13 @@ const TeamMemberDetails = () => { payload: e.target.value, }); }; - + if (loading) { + return ( +
+ +
+ ); + } if (isPending) { return (
@@ -92,6 +98,7 @@ const TeamMemberDetails = () => { }); }; const addMemberHandler = (): void => { + setLoading(true); const endPoint = UPDATE_TEAM_DATA(); const finalSlackData = state.emailIds?.includes(',') ? state.emailIds.split(',').map(item => item?.trim()) @@ -102,6 +109,7 @@ const TeamMemberDetails = () => { }) .then(response => { toast.success(response?.data?.data); + setLoading(false); dispatch(fetchTeamDetails(teamId?.toString() || '')); dispatch(fetchTeams()); }) @@ -114,12 +122,33 @@ const TeamMemberDetails = () => { toast.error(toastMessage); }); }; - - const handleMakeManager = (memberID: string) => { + const removeMemberHandler = (memberID: string): void => { + setLoading(true); + const endpoint = REMOVE_TEAM_MEMBER(teamId?.toString() || '', memberID); + ApiService.delete(endpoint) + .then(response => { + setLoading(false); + if (response.status === 200) { + toast(response.data.data, { + icon: , + }); + dispatch(fetchTeamDetails(teamId?.toString() || '')); + dispatch(fetchTeams()); + } else { + toast.error(response.data.error.message); + } + }) + .catch(error => { + toast.error(`Error removing member from team : ${error.message}`); + }); + }; + const handleMakeManager = (memberID: string): void => { + setLoading(true); const endpoint = MAKE_MANAGER(teamId?.toString() || '', memberID); ApiService.patch(endpoint, {}) .then(response => { if (response.status === 200) { + setLoading(false); toast.success(response.data.data); dispatch(fetchTeamDetails(teamId?.toString() || '')); } else { @@ -163,24 +192,7 @@ const TeamMemberDetails = () => { payload: false, }); }; - const removeMemberHandler = (memberID: string): void => { - const endpoint = REMOVE_TEAM_MEMBER(teamId?.toString() || '', memberID); - ApiService.delete(endpoint) - .then(response => { - if (response.status === 200) { - toast(response.data.data, { - icon: , - }); - dispatch(fetchTeamDetails(teamId?.toString() || '')); - dispatch(fetchTeams()); - } else { - toast.error(response.data.error.message); - } - }) - .catch(error => { - toast.error(`Error removing member from team : ${error.message}`); - }); - }; + const handleHoverchange = (item: MemberType) => { if (userEmail === managerEmail || Role.includes('Admin')) { dispatchData({ @@ -208,7 +220,7 @@ const TeamMemberDetails = () => {
MEMBERS -
{' '} +
{' '}
{ +interface ActionType { + type: string; + payload: any; +} +export const reducer = (state: AppState, action: ActionType): AppState => { switch (action.type) { case actionTypes.SET_OPEN_DIALOG: return { @@ -126,8 +173,45 @@ export const reducer = (state, action) => { case actionTypes.SET_HOVERED: return { ...state, - hovered: action.payload, - id: action.payload.id, + hovered: { + ...state.hovered, + ...action.payload, + }, + }; + case actionTypes.SET_SLACK_CHANNEL_ID: + return { + ...state, + slackChannelId: action.payload, + }; + case actionTypes.SET_ONCALL: + return { + ...state, + oncall: action.payload, + }; + case actionTypes.SET_PSEC_ONCALL: + return { + ...state, + psecOncall: action.payload, + }; + case actionTypes.SET_INPUT: + return { + ...state, + input: action.payload, + }; + case actionTypes.SET_PSEC_INPUT: + return { + ...state, + psecInput: action.payload, + }; + case actionTypes.SET_OPEN_ONCALL: + return { + ...state, + openOnCall: action.payload, + }; + case actionTypes.SET_PSEC_OPEN_ONCALL: + return { + ...state, + openPsecOnCall: action.payload, }; default: return state; diff --git a/src/components/LeftNav/index.tsx b/src/components/LeftNav/index.tsx index de2b214..cece882 100644 --- a/src/components/LeftNav/index.tsx +++ b/src/components/LeftNav/index.tsx @@ -55,13 +55,6 @@ const LeftNav: React.FC = ({ children }) => { Icon: AlertOutlineIcon, handleNavigation: () => navigate('/severity'), }, - { - itemType: 'simpleNavItem', - label: 'Team', - route: '/team', - Icon: LeadIcon, - handleNavigation: () => navigate('/team'), - }, { itemType: 'simpleNavItem', label: 'Houston metrics', @@ -71,10 +64,10 @@ const LeftNav: React.FC = ({ children }) => { }, { itemType: 'simpleNavItem', - label: 'TEAM1', - route: '/Team-Revamp', + label: 'Team', + route: '/Teams', Icon: TeamIcon, - handleNavigation: () => navigate('/Team-Revamp'), + handleNavigation: () => navigate('/Teams'), }, { itemType: 'simpleNavItem', diff --git a/src/router.tsx b/src/router.tsx index ca69da8..a64d89a 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -20,11 +20,6 @@ const routes: CustomRouteObject[] = [ path: '/incident/:incidentId', element: , }, - { - id: 'TEAM', - path: '/team', - element: , - }, { id: 'SEVERITY', path: '/severity', @@ -37,7 +32,7 @@ const routes: CustomRouteObject[] = [ }, { id: 'TEAM', - path: '/Team-Revamp', + path: '/Teams', element: , }, { From 00a2867d390d007c361e59579894129cf04e7312 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 15 Jan 2024 22:41:55 +0530 Subject: [PATCH 23/39] TP-51166 | Code optimization and pr review comments resolve --- .../TeamDetails/TeamDetails.module.scss | 60 ---- src/Pages/TeamRevamp/TeamDetails/index.tsx | 317 +----------------- src/Pages/TeamRevamp/TeamList/SearchInput.tsx | 21 ++ src/Pages/TeamRevamp/TeamList/index.tsx | 23 +- .../TeamRevamp/{partials => }/constants.tsx | 15 +- src/Pages/TeamRevamp/index.tsx | 7 +- src/Pages/TeamRevamp/partials/Bots.tsx | 2 +- .../partials/CreateTeam/CreateTeam.tsx | 14 +- .../partials/SlackDetails/Slack.module.scss | 62 ++++ .../partials/SlackDetails/index.tsx | 311 +++++++++++++++++ .../TeamMemberDetails.module.scss | 1 + .../partials/TeamMemberDetails/index.tsx | 143 ++------ src/Pages/TeamRevamp/useTeamApis.tsx | 85 +++++ src/Pages/TeamRevamp/util.ts | 55 +++ src/slices/team1Slice.tsx | 2 +- 15 files changed, 592 insertions(+), 526 deletions(-) create mode 100644 src/Pages/TeamRevamp/TeamList/SearchInput.tsx rename src/Pages/TeamRevamp/{partials => }/constants.tsx (91%) create mode 100644 src/Pages/TeamRevamp/partials/SlackDetails/Slack.module.scss create mode 100644 src/Pages/TeamRevamp/partials/SlackDetails/index.tsx create mode 100644 src/Pages/TeamRevamp/useTeamApis.tsx create mode 100644 src/Pages/TeamRevamp/util.ts diff --git a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss index 89dbfda..5f8e897 100644 --- a/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss +++ b/src/Pages/TeamRevamp/TeamDetails/TeamDetails.module.scss @@ -1,67 +1,7 @@ .team-details-wrapper { margin: 20px 0px 0px 79px; } -.team-input-wrapper { - min-width: (320px); - height: (38px); -} -.add-member { - margin: 72px 0px 24px 0px; -} -.member-div { - margin: 32px 0px 0px 0px; - color: var(--navi-color-gray-c3); -} -.on-call-wrapper { - display: flex; - margin-top: 10px; -} -.team-name { - margin: 24px 24px 24px 0px; - color: var(--navi-color-gray-c1); -} -.slack-details-wrapper { - display: inline-flex; -} -.select-picker-wrapper { - min-height: 80px !important; - position: absolute; - z-index: 1; -} -.on-call-handler { - position: relative; - margin-right: 24px; -} -.search-input { - min-width: 320px !important; -} - -.info-icon-wrapper { - display: flex; - align-items: center; - margin-top: 5px; - gap: 2px; -} -.info-icon { - width: 16px; - height: 16px; - margin-right: 4px; -} -.update-details { - margin-left: calc(100% - 120px); - margin-bottom: 31px; - margin-top: 12px; -} .fallback-component { height: 100px; margin-left: 450px; } -.update-details-disabled { - margin-left: calc(100% - 120px); - opacity: 0; - pointer-events: none; -} -.team-channel-wrapper { - margin-right: 24px; - margin-top: 5px; -} diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 8bff2aa..5a24ae0 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -1,195 +1,13 @@ -import { useDispatch, useSelector } from 'react-redux'; -import { - useEffect, - useState, - useRef, - MutableRefObject, - useReducer, -} from 'react'; -import { - BorderedInput, - Button, - Tooltip, - Typography, -} from '@navi/web-ui/lib/primitives'; -import { SelectPicker } from '@navi/web-ui/lib/components'; -import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; -import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; -import { fetchTeamDetails, selectSearchTeamData } from '@src/slices/team1Slice'; -import { AppDispatch } from '@src/store'; -import useOutsideClick from '@src/services/hooks/useOustideClick'; +import { useSelector } from 'react-redux'; +import { selectSearchTeamData } from '@src/slices/team1Slice'; import FallbackComponent from '@src/components/Fallback'; -import { useAuthData } from '@src/services/hooks/useAuth'; -import { ApiService } from '@src/services/api'; -import { TeamState } from '@src/slices/team1Slice'; -import { - UPDATE_TEAM_DATA, - actionTypes, - initialState, - reducer, -} from '../partials/constants'; -import { getBots } from '../partials/Bots'; import TeamMemberDetails from '../partials/TeamMemberDetails'; import styles from './TeamDetails.module.scss'; +import SlackDetails from '../partials/SlackDetails'; -const TeamDetails = () => { - const dispatch = useDispatch(); - const [state, dispatchData] = useReducer(reducer, initialState); - const teamData: TeamState = useSelector(selectSearchTeamData); - const { teamDetails } = teamData; - const teamId = teamDetails?.id; - const { isPending } = teamData; - const Role = useAuthData(); - const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); - const userEmail = userData?.emailId || ''; - const searchRef = useRef(null); +const TeamDetails: React.FC = () => { + const { isPending } = useSelector(selectSearchTeamData); - useEffect(() => { - dispatchData({ - type: actionTypes.SET_INPUT, - payload: '', - }); - dispatchData({ - type: actionTypes.SET_PSEC_INPUT, - payload: '', - }); - }, [teamData]); - - const DEFAULT_TEAM_ONCALL = teamDetails?.oncall?.name - ? `@${teamDetails?.oncall?.name}` - : ''; - const DEFAULT_TEAM_ONCALL_ID = teamDetails?.oncall?.id || ''; - const DEFAULT_TEAM_PSEC_ID = teamDetails?.pse_oncall?.id || ''; - const DEFAULT_PSEC_ONCALL = - teamDetails?.pse_oncall && teamDetails?.pse_oncall?.name != '' - ? '@' + teamDetails?.pse_oncall?.name - : ''; - const webChannelId = - teamDetails?.webhookSlackChannelName !== 'not found' - ? `@${teamDetails?.webhookSlackChannelName}` - : teamDetails?.webhookSlackChannelId - ? teamDetails?.webhookSlackChannelId - : ''; - const handleslackChannelId = ( - e: React.ChangeEvent, - ): void => { - dispatchData({ - type: actionTypes.SET_SLACK_CHANNEL_ID, - payload: e.target.value, - }); - }; - const handleOncallChange = (event): void => { - dispatchData({ - type: actionTypes.SET_ONCALL, - payload: { - label: event.label, - value: event.value.toString(), - }, - }); - dispatchData({ - type: actionTypes.SET_INPUT, - payload: event.label, - }); - dispatchData({ - type: actionTypes.SET_OPEN_ONCALL, - payload: false, - }); - }; - const handlePsecOncall = (event): void => { - dispatchData({ - type: actionTypes.SET_PSEC_ONCALL, - payload: { - label: event.label, - value: event.value.toString(), - }, - }); - dispatchData({ - type: actionTypes.SET_PSEC_INPUT, - payload: event.label, - }); - dispatchData({ - type: actionTypes.SET_PSEC_OPEN_ONCALL, - payload: false, - }); - }; - const handleOncallOutsideClick = (): void => { - dispatchData({ - type: actionTypes.SET_OPEN_ONCALL, - payload: false, - }); - }; - const handlePsecOncallOutsideClick = (): void => { - dispatchData({ - type: actionTypes.SET_PSEC_OPEN_ONCALL, - payload: false, - }); - }; - const refOncall = useOutsideClick({ - callback: handleOncallOutsideClick, - }) as MutableRefObject; - - const refPsecOncall = useOutsideClick({ - callback: handlePsecOncallOutsideClick, - }) as MutableRefObject; - - const handleOncallPicker = (): void => { - if (!isUserParticipant(teamDetails) && !Role.includes('Admin')) return; - dispatchData({ - type: actionTypes.SET_OPEN_ONCALL, - payload: !state.openOnCall, - }); - }; - const handleOpenPsecOnCallPicker = (): void => { - if (!isUserParticipant(teamDetails) && !Role.includes('Admin')) return; - dispatchData({ - type: actionTypes.SET_PSEC_OPEN_ONCALL, - payload: !state.openPsecOnCall, - }); - }; - const handleInputChange = (e: React.ChangeEvent): void => { - dispatchData({ - type: actionTypes.SET_INPUT, - payload: e.target.value, - }); - }; - const handlePsecInputChange = ( - e: React.ChangeEvent, - ): void => { - dispatchData({ - type: actionTypes.SET_PSEC_INPUT, - payload: e.target.value, - }); - }; - const submitHandler = (): void => { - const endPoint = UPDATE_TEAM_DATA(); - ApiService.post(endPoint, { - id: teamId, - webhook_slack_channel: state.slackChannelId, - on_call_handle: state.oncall.value, - pse_on_call_id: state.psecOncall.value, - }) - .then(response => { - toast.success(response?.data?.data); - dispatch(fetchTeamDetails(teamId?.toString() || '')); - dispatchData({ - type: actionTypes.SET_OPEN_ONCALL, - payload: false, - }); - dispatchData({ - type: actionTypes.SET_PSEC_OPEN_ONCALL, - payload: false, - }); - }) - .catch(error => { - const toastMessage = `${ - error?.response?.data?.error?.message - ? `${error?.response?.data?.error?.message}` - : 'Something went wrong,Pls try again later' - }`; - toast.error(toastMessage); - }); - }; if (isPending) { return (
@@ -197,132 +15,9 @@ const TeamDetails = () => {
); } - const managerEmail = teamDetails?.participants?.find( - participant => participant.id === teamDetails?.managerId, - )?.email; - const isUserParticipant = (teamDetails: any) => { - const participantEmails = teamDetails?.participants?.map( - (participant: any) => participant.email, - ); - return participantEmails?.includes(userEmail); - }; - return (
- - {teamDetails?.name} - {' '} - - SLACK DETAILS - {' '} -
-
-
- Team channel -
- - - -
-
- -
-
-
- Team on call{' '} -
- -
- {state.openOnCall && ( - - )} -
-
- PSEC on call -
- -
- {state.openPsecOnCall && ( - - )} -
- -
-
-
-
+
); diff --git a/src/Pages/TeamRevamp/TeamList/SearchInput.tsx b/src/Pages/TeamRevamp/TeamList/SearchInput.tsx new file mode 100644 index 0000000..2e93b06 --- /dev/null +++ b/src/Pages/TeamRevamp/TeamList/SearchInput.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { BorderedInput } from '@navi/web-ui/lib/primitives'; +import { SearchIcon } from '@navi/web-ui/lib/icons'; +import { SearchInputComponentProps } from '../constants'; +import styles from './TeamList.module.scss'; + +const SearchInput: React.FC = ({ + value, + onChange, +}) => { + return ( + } + onChange={onChange} + containerClassName={styles['search-input']} + value={value} + /> + ); +}; + +export default SearchInput; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 71070bc..87649fd 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -2,13 +2,15 @@ import { useEffect, useState, useRef } from 'react'; import { AppDispatch, RootState } from '@src/store'; import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; -import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; -import { BorderedInput, Button, Typography } from '@navi/web-ui/lib/primitives'; -import { AddIcon, SearchIcon } from '@navi/web-ui/lib/icons'; -import PersonIcon from '@src/assets/PersonIcon'; import { useAuthData } from '@src/services/hooks/useAuth'; -import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; +import { Button, Typography } from '@navi/web-ui/lib/primitives'; +import { AddIcon } from '@navi/web-ui/lib/icons'; +import PersonIcon from '@src/assets/PersonIcon'; import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; + +import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; +import SearchInput from './SearchInput'; import styles from './TeamList.module.scss'; type Options = { @@ -30,7 +32,7 @@ const CustomTeamOptions = (option: Options): JSX.Element => { ); }; -const TeamList = () => { +const TeamList: React.FC = () => { const dispatch = useDispatch(); const Role = useAuthData(); const teamList = useSelector((state: RootState) => state.team1.teams.data); @@ -104,17 +106,12 @@ const TeamList = () => { fetchDetails(event.value.toString()); scroll.current = false; }; + return (
- } - onChange={handleInputChange} - containerClassName={styles['search-input']} - value={input || ''} - /> - + {Role.includes('Admin') && (
diff --git a/src/Pages/TeamRevamp/partials/Bots.tsx b/src/Pages/TeamRevamp/partials/Bots.tsx index 6a6b5c4..f921b7f 100644 --- a/src/Pages/TeamRevamp/partials/Bots.tsx +++ b/src/Pages/TeamRevamp/partials/Bots.tsx @@ -1,5 +1,5 @@ import { ApiService } from '@src/services/api'; -import { FETCH_ALL_BOTS_DATA, PickerOptionProps } from './constants'; +import { FETCH_ALL_BOTS_DATA, PickerOptionProps } from '../constants'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; let bots: Array = []; diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index 9e749b5..035f395 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -3,7 +3,6 @@ import { useDispatch, useSelector } from 'react-redux'; import { Typography } from '@navi/web-ui/lib/primitives'; import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import { BorderedInput, ModalDialog } from '@navi/web-ui/lib/primitives'; -import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { AppDispatch } from '@src/store'; import { ApiService } from '@src/services/api'; @@ -14,19 +13,15 @@ import { setModalOpen, setNewTeam, } from '@src/slices/team1Slice'; -import { CREATE_TEAM } from '@src/Pages/TeamRevamp/partials/constants'; +import { CREATE_TEAM } from '@src/Pages/TeamRevamp/constants'; import { regularExpression, emailRegularExpression, -} from '@src/Pages/TeamRevamp/partials/constants'; + validEmail, + CreateTeamProps, +} from '@src/Pages/TeamRevamp/constants'; import styles from './CreateTeam.module.scss'; -interface CreateTeamProps { - setTeam: React.Dispatch< - React.SetStateAction - >; -} - const CreateTeam: React.FC = ({ setTeam }) => { const dispatch = useDispatch(); const { modalOpen } = useSelector(selectSearchTeamData); @@ -44,7 +39,6 @@ const CreateTeam: React.FC = ({ setTeam }) => { }; const validateEmail = (value: string): void => { - const validEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; setEmailError( validEmail.test(value) ? !emailRegularExpression.test(value) diff --git a/src/Pages/TeamRevamp/partials/SlackDetails/Slack.module.scss b/src/Pages/TeamRevamp/partials/SlackDetails/Slack.module.scss new file mode 100644 index 0000000..6641e10 --- /dev/null +++ b/src/Pages/TeamRevamp/partials/SlackDetails/Slack.module.scss @@ -0,0 +1,62 @@ +.team-input-wrapper { + min-width: (320px); + height: (38px); +} +.add-member { + margin: 72px 0px 24px 0px; +} +.member-div { + margin: 32px 0px 0px 0px; + color: var(--navi-color-gray-c3); +} +.on-call-wrapper { + display: flex; + margin-top: 10px; +} +.team-name { + margin: 24px 24px 24px 0px; + color: var(--navi-color-gray-c1); +} +.slack-details-wrapper { + display: inline-flex; +} +.select-picker-wrapper { + min-height: 80px !important; + position: absolute; + z-index: 1; +} +.on-call-handler { + position: relative; + margin-right: 24px; +} +.search-input { + min-width: 320px !important; +} + +.info-icon-wrapper { + display: flex; + align-items: center; + margin-top: 5px; + gap: 2px; +} +.info-icon { + width: 16px; + height: 16px; + margin-right: 4px; +} +.update-details { + margin-left: calc(100% - 120px); + margin-bottom: 31px; + margin-top: 12px; +} +.update-details-disabled { + margin-left: calc(100% - 120px); + opacity: 0; + pointer-events: none; + margin-bottom: 31px; + margin-top: 12px; +} +.team-channel-wrapper { + margin-right: 24px; + margin-top: 5px; +} diff --git a/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx b/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx new file mode 100644 index 0000000..a466847 --- /dev/null +++ b/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx @@ -0,0 +1,311 @@ +import { useDispatch, useSelector } from 'react-redux'; +import { + useEffect, + useState, + useRef, + MutableRefObject, + useReducer, +} from 'react'; +import { + BorderedInput, + Button, + Tooltip, + Typography, +} from '@navi/web-ui/lib/primitives'; +import { SelectPicker } from '@navi/web-ui/lib/components'; +import { toast } from '@navi/web-ui/lib/primitives/Toast'; +import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; +import { fetchTeamDetails } from '@src/slices/team1Slice'; +import { AppDispatch } from '@src/store'; +import useOutsideClick from '@src/services/hooks/useOustideClick'; +import { useAuthData } from '@src/services/hooks/useAuth'; +import { ApiService } from '@src/services/api'; + +import { + TeamsDetail, + UPDATE_TEAM_DATA, + actionTypes, + initialState, + reducer, +} from '../../constants'; +import { getBots } from '../Bots'; +import useGetTeamDetailsConstants from '../../util'; +import styles from './Slack.module.scss'; + +const TeamDetails: React.FC = () => { + const dispatch = useDispatch(); + const [state, dispatchData] = useReducer(reducer, initialState); + const Role = useAuthData(); + const { + teamId, + userEmail, + managerEmail, + teamDetails, + teamData, + DEFAULT_TEAM_ONCALL, + DEFAULT_TEAM_ONCALL_ID, + DEFAULT_TEAM_PSEC_ID, + DEFAULT_PSEC_ONCALL, + webChannelId, + } = useGetTeamDetailsConstants(); + const searchRef = useRef(null); + + useEffect(() => { + dispatchData({ + type: actionTypes.SET_INPUT, + payload: '', + }); + dispatchData({ + type: actionTypes.SET_PSEC_INPUT, + payload: '', + }); + }, [teamData]); + + const handleslackChannelId = ( + e: React.ChangeEvent, + ): void => { + dispatchData({ + type: actionTypes.SET_SLACK_CHANNEL_ID, + payload: e.target.value, + }); + }; + const handleOncallChange = (event): void => { + dispatchData({ + type: actionTypes.SET_ONCALL, + payload: { + label: event.label, + value: event.value.toString(), + }, + }); + dispatchData({ + type: actionTypes.SET_INPUT, + payload: event.label, + }); + dispatchData({ + type: actionTypes.SET_OPEN_ONCALL, + payload: false, + }); + }; + const handlePsecOncall = (event): void => { + dispatchData({ + type: actionTypes.SET_PSEC_ONCALL, + payload: { + label: event.label, + value: event.value.toString(), + }, + }); + dispatchData({ + type: actionTypes.SET_PSEC_INPUT, + payload: event.label, + }); + dispatchData({ + type: actionTypes.SET_PSEC_OPEN_ONCALL, + payload: false, + }); + }; + const handleOncallOutsideClick = (): void => { + dispatchData({ + type: actionTypes.SET_OPEN_ONCALL, + payload: false, + }); + }; + const handlePsecOncallOutsideClick = (): void => { + dispatchData({ + type: actionTypes.SET_PSEC_OPEN_ONCALL, + payload: false, + }); + }; + const refOncall = useOutsideClick({ + callback: handleOncallOutsideClick, + }) as MutableRefObject; + + const refPsecOncall = useOutsideClick({ + callback: handlePsecOncallOutsideClick, + }) as MutableRefObject; + + const handleOncallPicker = (): void => { + if (!isUserParticipant(teamDetails) && !Role.includes('Admin')) return; + dispatchData({ + type: actionTypes.SET_OPEN_ONCALL, + payload: !state.openOnCall, + }); + }; + const handleOpenPsecOnCallPicker = (): void => { + if (!isUserParticipant(teamDetails) && !Role.includes('Admin')) return; + dispatchData({ + type: actionTypes.SET_PSEC_OPEN_ONCALL, + payload: !state.openPsecOnCall, + }); + }; + const handleInputChange = (e: React.ChangeEvent): void => { + dispatchData({ + type: actionTypes.SET_INPUT, + payload: e.target.value, + }); + }; + const handlePsecInputChange = ( + e: React.ChangeEvent, + ): void => { + dispatchData({ + type: actionTypes.SET_PSEC_INPUT, + payload: e.target.value, + }); + }; + const updateDetails = (): void => { + const endPoint = UPDATE_TEAM_DATA(); + ApiService.post(endPoint, { + id: teamId, + webhook_slack_channel: state.slackChannelId, + on_call_handle: state.oncall.value, + pse_on_call_id: state.psecOncall.value, + }) + .then(response => { + toast.success(response?.data?.data); + dispatch(fetchTeamDetails(teamId?.toString() || '')); + dispatchData({ + type: actionTypes.SET_OPEN_ONCALL, + payload: false, + }); + dispatchData({ + type: actionTypes.SET_PSEC_OPEN_ONCALL, + payload: false, + }); + }) + .catch(error => { + const toastMessage = `${ + error?.response?.data?.error?.message + ? `${error?.response?.data?.error?.message}` + : 'Something went wrong,Pls try again later' + }`; + toast.error(toastMessage); + }); + }; + + const isUserParticipant = (teamDetails: TeamsDetail): boolean | undefined => { + const participantEmails = teamDetails?.participants?.map( + (participant: any) => participant.email, + ); + return participantEmails?.includes(userEmail); + }; + + return ( +
+ + {teamDetails?.name} + {' '} + + SLACK DETAILS + {' '} +
+
+
+ Team channel +
+ + + +
+
+ +
+
+
+ Team on call{' '} +
+ +
+ {state.openOnCall && ( + + )} +
+
+ PSEC on call +
+ +
+ {state.openPsecOnCall && ( + + )} +
+ +
+
+
+
+
+ ); +}; + +export default TeamDetails; diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index 3b42b3a..991e190 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -9,6 +9,7 @@ display: grid; grid-template-columns: 1fr 1fr 1fr; justify-content: space-around; + gap: 12px; } .item-details { margin-top: 12px; diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 8e82029..7cee0c6 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -1,36 +1,23 @@ -import { useDispatch, useSelector } from 'react-redux'; -import { useState, useReducer } from 'react'; -import { - fetchTeamDetails, - fetchTeams, - selectSearchTeamData, -} from '@src/slices/team1Slice'; +import { useReducer } from 'react'; import { BorderedInput, Button, Typography, ModalDialog, } from '@navi/web-ui/lib/primitives'; -import { toast } from '@navi/web-ui/lib/primitives/Toast'; import PersonIconFill from '@src/assets/PersonIconFill'; import ManagerIconFill from '@src/assets/ManagerIconFill'; import MemberIcon from '@src/assets/MemberIcon'; import DeleteIcon from '@src/assets/DeleteIcon'; -import AlertIcon from '@src/assets/AlertIcon'; -import { ApiService } from '@src/services/api'; -import { AppDispatch } from '@src/store'; -import FallbackComponent from '@src/components/Fallback'; import { useAuthData } from '@src/services/hooks/useAuth'; -import { TeamState } from '@src/slices/team1Slice'; +import { useGetTeamDetailsConstants } from '@src/Pages/TeamRevamp/util'; +import useTeamApis from '../../useTeamApis'; import { - MAKE_MANAGER, - REMOVE_TEAM_MEMBER, TeamsDetail, - UPDATE_TEAM_DATA, actionTypes, initialState, reducer, -} from '@src/Pages/TeamRevamp/partials/constants'; +} from '@src/Pages/TeamRevamp/constants'; import styles from './TeamMemberDetails.module.scss'; type MemberType = { @@ -38,31 +25,13 @@ type MemberType = { name: string; email: string; }; - -const TeamMemberDetails = () => { +const TeamMemberDetails: React.FC = () => { const [state, dispatchData] = useReducer(reducer, initialState); - const teamData: TeamState = useSelector(selectSearchTeamData); - const dispatch = useDispatch(); - const userData = JSON.parse(localStorage.getItem('user-data') || '{}'); const Role = useAuthData(); - const { teamDetails } = teamData; - const { isPending } = teamData; - const [loading, setLoading] = useState(false); - const teamId = teamDetails?.id; - const userEmail = userData?.emailId || ''; - const managerEmail = teamDetails?.participants?.find( - item => item.id === teamDetails?.managerId, - )?.email; - const manager = teamDetails?.participants?.find( - item => item.id === teamDetails?.managerId, - ); - const teamMembers = teamDetails?.participants?.map(item => ({ - name: item.name, - email: item.email, - id: item.id, - })); - - const isUserParticipant = (teamDetails: TeamsDetail) => { + const { teamId, userEmail, managerEmail, manager, teamMembers, teamDetails } = + useGetTeamDetailsConstants(); + const { addMemberHandler, removeTeamMember, makeManager } = useTeamApis(); + const isUserParticipant = (teamDetails: TeamsDetail): boolean | undefined => { const participantEmails = teamDetails?.participants?.map( (participant: any) => participant.email, ); @@ -74,91 +43,19 @@ const TeamMemberDetails = () => { payload: e.target.value, }); }; - if (loading) { - return ( -
- -
- ); - } - if (isPending) { - return ( -
- -
- ); - } - const onKeyPressClickHandler = (event: React.KeyboardEvent): void => { + const onKeyPressClickHandler = event => { if (event.key === 'Enter') { - addMemberHandler(); + addMemberHandler(state.emailIds); + dispatchData({ + type: actionTypes.SET_EMAIL_IDS, + payload: '', + }); } dispatchData({ type: actionTypes.SET_EMAIL_IDS, payload: '', }); }; - const addMemberHandler = (): void => { - setLoading(true); - const endPoint = UPDATE_TEAM_DATA(); - const finalSlackData = state.emailIds?.includes(',') - ? state.emailIds.split(',').map(item => item?.trim()) - : [state.emailIds]; - ApiService.post(endPoint, { - id: teamId, - workEmailIds: finalSlackData, - }) - .then(response => { - toast.success(response?.data?.data); - setLoading(false); - dispatch(fetchTeamDetails(teamId?.toString() || '')); - dispatch(fetchTeams()); - }) - .catch(error => { - const toastMessage = `${ - error?.response?.data?.error?.message - ? `${error?.response?.data?.error?.message}` - : 'Something went wrong,Pls try again later' - }`; - toast.error(toastMessage); - }); - }; - const removeMemberHandler = (memberID: string): void => { - setLoading(true); - const endpoint = REMOVE_TEAM_MEMBER(teamId?.toString() || '', memberID); - ApiService.delete(endpoint) - .then(response => { - setLoading(false); - if (response.status === 200) { - toast(response.data.data, { - icon: , - }); - dispatch(fetchTeamDetails(teamId?.toString() || '')); - dispatch(fetchTeams()); - } else { - toast.error(response.data.error.message); - } - }) - .catch(error => { - toast.error(`Error removing member from team : ${error.message}`); - }); - }; - const handleMakeManager = (memberID: string): void => { - setLoading(true); - const endpoint = MAKE_MANAGER(teamId?.toString() || '', memberID); - ApiService.patch(endpoint, {}) - .then(response => { - if (response.status === 200) { - setLoading(false); - toast.success(response.data.data); - dispatch(fetchTeamDetails(teamId?.toString() || '')); - } else { - toast.error(response.data.error.message); - } - }) - .catch(error => { - toast.error(`Error in making manager of team : ${error.message}`); - }); - }; const handleOpenModal = (member: MemberType) => () => { dispatchData({ @@ -186,14 +83,14 @@ const TeamMemberDetails = () => { payload: true, }); }; - const handleResetDialog = () => { + const handleResetDialog = (): void => { dispatchData({ type: actionTypes.SET_OPEN_DIALOG, payload: false, }); }; - const handleHoverchange = (item: MemberType) => { + const handleHoverchange = (item: MemberType): void => { if (userEmail === managerEmail || Role.includes('Admin')) { dispatchData({ type: actionTypes.SET_HOVERED, @@ -204,7 +101,7 @@ const TeamMemberDetails = () => { }); } }; - const handleHoverIcon = (item: MemberType) => { + const handleHoverIcon = (item: MemberType): void => { if (userEmail === managerEmail || Role.includes('Admin')) { dispatchData({ type: actionTypes.SET_HOVERED, @@ -302,7 +199,7 @@ const TeamMemberDetails = () => { { label: 'Update', onClick: () => { - handleMakeManager(state.selectedMember?.id.toString() || ''); + makeManager(state.selectedMember?.id.toString() || ''); handleResetModal(); }, }, @@ -340,7 +237,7 @@ const TeamMemberDetails = () => { )} @@ -179,7 +184,9 @@ const TeamMemberDetails: React.FC = () => { {manager?.name !== item.name && (
{ + handleOpenDialog(item); + }} className={styles['delete-icon']} > From 97809724fde978756134b73f74544221e24308cb Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 16 Jan 2024 16:28:44 +0530 Subject: [PATCH 27/39] TP-51166 | design fix --- .../partials/TeamMemberDetails/TeamMemberDetails.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index 1d89563..c83bdf8 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -19,6 +19,7 @@ .details-container { display: flex; width: 252px; + height: 68px; } .icon-wrapper { margin-top: 12px; From a14d974f3bce72911d9664636b94e9d8d0a8451f Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Tue, 16 Jan 2024 17:31:25 +0530 Subject: [PATCH 28/39] TP-51166 | imports ordering --- src/Pages/TeamRevamp/TeamDetails/index.tsx | 2 +- src/Pages/TeamRevamp/TeamList/index.tsx | 5 ++--- src/Pages/TeamRevamp/index.tsx | 6 +++--- src/Pages/TeamRevamp/partials/SlackDetails/index.tsx | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamDetails/index.tsx b/src/Pages/TeamRevamp/TeamDetails/index.tsx index 5a24ae0..50ed116 100644 --- a/src/Pages/TeamRevamp/TeamDetails/index.tsx +++ b/src/Pages/TeamRevamp/TeamDetails/index.tsx @@ -2,8 +2,8 @@ import { useSelector } from 'react-redux'; import { selectSearchTeamData } from '@src/slices/team1Slice'; import FallbackComponent from '@src/components/Fallback'; import TeamMemberDetails from '../partials/TeamMemberDetails'; -import styles from './TeamDetails.module.scss'; import SlackDetails from '../partials/SlackDetails'; +import styles from './TeamDetails.module.scss'; const TeamDetails: React.FC = () => { const { isPending } = useSelector(selectSearchTeamData); diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 87649fd..91ff5a6 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -1,14 +1,13 @@ import { useEffect, useState, useRef } from 'react'; -import { AppDispatch, RootState } from '@src/store'; import { useDispatch, useSelector } from 'react-redux'; import { SelectPicker } from '@navi/web-ui/lib/components'; -import { useAuthData } from '@src/services/hooks/useAuth'; import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; import { Button, Typography } from '@navi/web-ui/lib/primitives'; import { AddIcon } from '@navi/web-ui/lib/icons'; import PersonIcon from '@src/assets/PersonIcon'; import { fetchTeamDetails, setModalOpen } from '@src/slices/team1Slice'; - +import { AppDispatch, RootState } from '@src/store'; +import { useAuthData } from '@src/services/hooks/useAuth'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; import SearchInput from './SearchInput'; import styles from './TeamList.module.scss'; diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index 4c9678a..fd3d159 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -1,10 +1,8 @@ -import { FC, useEffect, useState } from 'react'; +import { FC, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import Typography from '@navi/web-ui/lib/primitives/Typography'; import { AppDispatch } from '@src/store'; import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary'; -import TeamDetails from './TeamDetails'; -import { fetchAllBots } from './partials/Bots'; import { TeamState, fetchTeamDetails, @@ -12,6 +10,8 @@ import { selectSearchTeamData, setSelectedTeam, } from '@src/slices/team1Slice'; +import TeamDetails from './TeamDetails'; +import { fetchAllBots } from './partials/Bots'; import TeamList from './TeamList'; import styles from './Team.module.scss'; diff --git a/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx b/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx index 36c85ed..bd05f27 100644 --- a/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx @@ -10,7 +10,6 @@ import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import useOutsideClick from '@src/services/hooks/useOustideClick'; import { useAuthData } from '@src/services/hooks/useAuth'; import useTeamApis from '../../useTeamApis'; - import { TeamsDetail, actionTypes, From 5cfa45ca1c73863419c0459006428532544ab424 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Wed, 17 Jan 2024 12:47:26 +0530 Subject: [PATCH 29/39] TP-51166 | design callouts fix --- src/Pages/TeamRevamp/TeamList/TeamList.module.scss | 4 ++-- .../partials/TeamMemberDetails/TeamMemberDetails.module.scss | 5 ++++- src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index 57c0bf9..ad75fc7 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -41,9 +41,9 @@ display: flex; justify-content: space-between; font-size: small; - margin-bottom: 10px; - padding: 4px; color: var(--navi-color-gray-c2); + height: 29px; + padding-top: 15px; } .custom-options:hover { color: var(--navi-color-blue-base); diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss index c83bdf8..1e8db70 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/TeamMemberDetails.module.scss @@ -49,7 +49,7 @@ display: block; margin-top: 12px; width: 16px; - margin-right: 6px; + margin-right: 12px; } .items-wrapper:hover { background-color: var(--navi-color-blue-bg); @@ -99,3 +99,6 @@ margin-top: 12px; margin-left: 5px; } +.members-tag { + color: var(--navi-color-gray-c3); +} diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 51000a9..d9f5d4e 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -116,7 +116,9 @@ const TeamMemberDetails: React.FC = () => {
- MEMBERS + + MEMBERS +
{' '}
From 998cd7498aea308ae60e15c0536ae765df979db6 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Wed, 17 Jan 2024 13:25:24 +0530 Subject: [PATCH 30/39] TP-51166 | design-2 --- src/Pages/TeamRevamp/TeamList/TeamList.module.scss | 7 ++++--- src/Pages/TeamRevamp/TeamList/index.tsx | 2 +- src/assets/PersonIcon.tsx | 6 +----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index ad75fc7..3c1d66a 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -29,7 +29,11 @@ min-height: calc(100vh - 228px) !important; border: none !important; box-shadow: none !important; + :hover { + background-color: var(--navi-select-picker-bg-item-selected); + } } + .select-picker-wrapper::-webkit-scrollbar { display: none; } @@ -45,9 +49,6 @@ height: 29px; padding-top: 15px; } -.custom-options:hover { - color: var(--navi-color-blue-base); -} .search-results { margin-left: 27px; margin-top: 12px; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 91ff5a6..3309df4 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -17,7 +17,7 @@ type Options = { value: number; count: number; }; -const CustomTeamOptions = (option: Options): JSX.Element => { +const CustomTeamOptions = (option: Options, selected: boolean): JSX.Element => { return (
{option.label}
diff --git a/src/assets/PersonIcon.tsx b/src/assets/PersonIcon.tsx index 352a268..343ca4e 100644 --- a/src/assets/PersonIcon.tsx +++ b/src/assets/PersonIcon.tsx @@ -7,8 +7,6 @@ const PersonIcon: FC = ({ onClick, ...restProps }) => { - const [isHovered, setIsHovered] = useState(false); - return ( = ({ fill="none" xmlns="http://www.w3.org/2000/svg" onClick={onClick as MouseEventHandler} - onMouseEnter={() => setIsHovered(true)} // Set isHovered to true on hover - onMouseLeave={() => setIsHovered(false)} style={{ cursor: 'pointer' }} > ); From 616c1ea06eb057f262f4918d6ceed8c8d7fc3e62 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Wed, 17 Jan 2024 17:04:21 +0530 Subject: [PATCH 31/39] TP-51166 | design-3 --- .../TeamRevamp/TeamList/TeamList.module.scss | 3 ++ src/Pages/TeamRevamp/TeamList/index.tsx | 39 +++++++++++++------ .../partials/SlackDetails/Slack.module.scss | 4 +- src/assets/SelectedPersonIcon.tsx | 28 +++++++++++++ 4 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 src/assets/SelectedPersonIcon.tsx diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index 3c1d66a..be31dea 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -54,3 +54,6 @@ margin-top: 12px; color: var(--navi-color-gray-c3); } +.add-icon { + margin-left: 3px; +} diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index 3309df4..f0e38e2 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -11,20 +11,29 @@ import { useAuthData } from '@src/services/hooks/useAuth'; import CreateTeam from '@src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam'; import SearchInput from './SearchInput'; import styles from './TeamList.module.scss'; +import SelectedPersonIcon from '@src/assets/SelectedPersonIcon'; type Options = { label: string; value: number; - count: number; + additionalData: { + count: number; + isSelected: boolean; + }; }; -const CustomTeamOptions = (option: Options, selected: boolean): JSX.Element => { + +const CustomTeamOptions = (option: Options): JSX.Element => { return (
{option.label}
-
{option.count}
+
{option.additionalData.count}
- + {option.additionalData.isSelected ? ( + + ) : ( + + )}
@@ -38,9 +47,9 @@ const TeamList: React.FC = () => { const defaultTeam = useSelector( (state: RootState) => state.team1.teams.data[0], ); + const [team, setTeam] = useState(); const newTeam = useSelector((state: RootState) => state.team1.teams.newTeam); const [input, setInput] = useState(''); - const [team, setTeam] = useState(); const scroll = useRef(false); const scrollintoView = (event: string): void => { @@ -52,11 +61,15 @@ const TeamList: React.FC = () => { }, 0); } }; + const options = Array.isArray(teamList) ? teamList.map(item => ({ label: item.name, value: item.id, - count: item.slackUserIds?.length, + additionalData: { + count: item.slackUserIds?.length, + isSelected: item.id === team?.value, + }, })) : []; @@ -96,13 +109,10 @@ const TeamList: React.FC = () => { const createHandler = (): void => { dispatch(setModalOpen(true)); }; - const fetchDetails = (val: string): void => { - dispatch(fetchTeamDetails(val)); - }; const handleSelectionChange = (event): void => { setTeam(event); - fetchDetails(event.value.toString()); + dispatch(fetchTeamDetails(event.value.toString())); scroll.current = false; }; @@ -113,7 +123,13 @@ const TeamList: React.FC = () => { {Role.includes('Admin') && ( )}
From 47ff43036c64216a4fac9fd828bbed3f8b1c42f0 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Fri, 19 Jan 2024 14:26:33 +0530 Subject: [PATCH 35/39] TP-51166 | added Clickstream events --- src/Pages/TeamRevamp/TeamList/SearchInput.tsx | 2 +- .../{constants.tsx => constants.ts} | 126 +----------------- src/Pages/TeamRevamp/index.tsx | 10 +- src/Pages/TeamRevamp/partials/Bots.tsx | 3 +- .../partials/CreateTeam/CreateTeam.tsx | 10 +- .../partials/SlackDetails/index.tsx | 8 +- .../partials/TeamMemberDetails/index.tsx | 2 +- src/Pages/TeamRevamp/types.ts | 123 +++++++++++++++++ src/Pages/TeamRevamp/useTeamApis.tsx | 26 +++- src/Pages/TeamRevamp/util.ts | 2 +- src/components/LeftNav/index.tsx | 14 +- src/router.tsx | 2 +- src/slices/team1Slice.tsx | 3 +- 13 files changed, 183 insertions(+), 148 deletions(-) rename src/Pages/TeamRevamp/{constants.tsx => constants.ts} (61%) create mode 100644 src/Pages/TeamRevamp/types.ts diff --git a/src/Pages/TeamRevamp/TeamList/SearchInput.tsx b/src/Pages/TeamRevamp/TeamList/SearchInput.tsx index 2e93b06..69b5bb4 100644 --- a/src/Pages/TeamRevamp/TeamList/SearchInput.tsx +++ b/src/Pages/TeamRevamp/TeamList/SearchInput.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { BorderedInput } from '@navi/web-ui/lib/primitives'; import { SearchIcon } from '@navi/web-ui/lib/icons'; -import { SearchInputComponentProps } from '../constants'; +import { SearchInputComponentProps } from '../types'; import styles from './TeamList.module.scss'; const SearchInput: React.FC = ({ diff --git a/src/Pages/TeamRevamp/constants.tsx b/src/Pages/TeamRevamp/constants.ts similarity index 61% rename from src/Pages/TeamRevamp/constants.tsx rename to src/Pages/TeamRevamp/constants.ts index e2939a0..0f24bab 100644 --- a/src/Pages/TeamRevamp/constants.tsx +++ b/src/Pages/TeamRevamp/constants.ts @@ -1,7 +1,5 @@ -import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; import { createURL } from '@src/services/globalUtils'; -import { TeamState } from '@src/slices/team1Slice'; -import { ChangeEvent } from 'react'; +import { ActionType, AppState, Participant } from './types'; const URL_PREFIX = createURL('/houston'); @@ -29,95 +27,6 @@ export const regularExpression = /^[a-zA-Z][a-zA-Z0-9_ -]{1,48}[a-zA-Z0-9]$/; export const validEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; export const emailRegularExpression = /^[a-zA-Z]+\.[a-zA-Z]+@navi\.com$/; -export interface SearchInputComponentProps { - value: string; - onChange: (event: ChangeEvent) => void; -} -export interface useTeamApiProps { - addMemberHandler: (emailIds: string) => void; - removeTeamMember: (memberId: string) => void; - makeManager: (memberId: string) => void; - updateDetails: ( - slackChannelId: string, - oncall: string, - psecOncall: string, - ) => void; -} -export interface TeamsData { - id?: string; - name?: string; - slackUserIds?: Array; - lastUpdatedAt?: string; - expand?: boolean; -} -export interface CreateTeamProps { - setTeam: React.Dispatch< - React.SetStateAction - >; -} -export interface BotsData { - id: string; - name: string; -} - -export interface TeamFormProps { - teamId: string; - isExpanded: boolean; - setLastUpdatedAt: (value: string) => void; -} - -export const slackUserOptions = [ - { - label: 'Email-id', - value: 'WorkEmailIds', - }, - { - label: 'Slack user-id', - value: 'SlackUserIds', - }, -]; - -export const userInputPlaceholders = { - 'Email-id': 'email ids', - 'Slack user-id': 'slack user ids', -}; - -export interface PickerOptionProps { - label: string; - value: string; -} -export interface Details { - teamId: number | undefined; - userEmail: string | undefined; - managerEmail: string | undefined; - manager: Participant | undefined; - teamMembers: any; - teamDetails: TeamsDetail; - teamData: TeamState; - DEFAULT_TEAM_ONCALL: string; - DEFAULT_TEAM_ONCALL_ID: string; - DEFAULT_TEAM_PSEC_ID: string; - DEFAULT_PSEC_ONCALL: string; - webChannelId: string; -} -export interface Participant { - name: string; - email: string; - id: string; -} -export interface TeamsDetail { - id?: number; - lastUpdatedAt?: string; - managerId?: string; - name?: string; - oncall?: { id: string; name: string }; - participants?: Array; - pse_oncall?: { id: string; name: string }; - slackUserIds?: string[]; - webhookSlackChannelId?: string; - webhookSlackChannelName?: string; -} - export const actionTypes = { SET_OPEN_DIALOG: 'SET_OPEN_DIALOG', SET_OPEN_MODAL: 'SET_OPEN_MODAL', @@ -137,34 +46,6 @@ export const actionTypes = { SET_EMAIL: 'SET_EMAIL', SET_CLEAR_MODAL: 'SET_CLEAR_MODAL', }; -interface AppState { - openDialog: boolean; - openModal: boolean; - emailIds: string; - selectedMember?: Participant; - hovered: { - ishovered: boolean; - id: string; - }; - slackChannelId: string; - oncall: { - label: string; - value: string; - }; - psecOncall: { - label: string; - value: string; - }; - input: string; - psecInput: string; - openOnCall: boolean; - openPsecOnCall: boolean; - teamName: string; - teamNameError: string; - emailError: string; - email: string; - clearModal: boolean; -} export const initialState: AppState = { openDialog: false, @@ -194,10 +75,7 @@ export const initialState: AppState = { email: '', clearModal: false, }; -interface ActionType { - type: string; - payload: any; -} + export const reducer = (state: AppState, action: ActionType): AppState => { switch (action.type) { case actionTypes.SET_OPEN_DIALOG: diff --git a/src/Pages/TeamRevamp/index.tsx b/src/Pages/TeamRevamp/index.tsx index fd3d159..6941f14 100644 --- a/src/Pages/TeamRevamp/index.tsx +++ b/src/Pages/TeamRevamp/index.tsx @@ -14,12 +14,16 @@ import TeamDetails from './TeamDetails'; import { fetchAllBots } from './partials/Bots'; import TeamList from './TeamList'; import styles from './Team.module.scss'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; const TeamRevamp: FC = () => { const dispatch = useDispatch(); const teamData: TeamState = useSelector(selectSearchTeamData); const { data, selectedTeam } = teamData; const selectedTeamId = selectedTeam?.id || ''; + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; useEffect(() => { dispatch(fetchTeams()); @@ -37,7 +41,11 @@ const TeamRevamp: FC = () => { dispatch(fetchTeamDetails(selectedTeamId.toString())); } }, [selectedTeam]); - + useEffect(() => { + fireEvent(EVENT_NAME.Houston_Team_Land, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); + }, []); return (
diff --git a/src/Pages/TeamRevamp/partials/Bots.tsx b/src/Pages/TeamRevamp/partials/Bots.tsx index f921b7f..4f39381 100644 --- a/src/Pages/TeamRevamp/partials/Bots.tsx +++ b/src/Pages/TeamRevamp/partials/Bots.tsx @@ -1,5 +1,6 @@ import { ApiService } from '@src/services/api'; -import { FETCH_ALL_BOTS_DATA, PickerOptionProps } from '../constants'; +import { FETCH_ALL_BOTS_DATA } from '../constants'; +import { PickerOptionProps } from '../types'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; let bots: Array = []; diff --git a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx index c3b2891..ae1fcf0 100644 --- a/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx +++ b/src/Pages/TeamRevamp/partials/CreateTeam/CreateTeam.tsx @@ -23,14 +23,17 @@ import { regularExpression, emailRegularExpression, validEmail, - CreateTeamProps, } from '@src/Pages/TeamRevamp/constants'; +import { CreateTeamProps } from '@src/Pages/TeamRevamp/types'; import styles from './CreateTeam.module.scss'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; const CreateTeam: React.FC = ({ setTeam }) => { const dispatch = useDispatch(); const [state, dispatchData] = useReducer(reducer, initialState); - + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; const { modalOpen } = useSelector(selectSearchTeamData); const validateTeamName = (value: string): void => { value = value.trim(); @@ -73,6 +76,9 @@ const CreateTeam: React.FC = ({ setTeam }) => { }; const addTeamHandler = (): void => { + fireEvent(EVENT_NAME.Houston_Create_Team, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); ApiService.post(CREATE_TEAM, { name: state.teamName, manager_email: state.email, diff --git a/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx b/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx index 6d1aa6f..ab1c02f 100644 --- a/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/SlackDetails/index.tsx @@ -10,12 +10,8 @@ import { AlertOutlineIcon } from '@navi/web-ui/lib/icons'; import useOutsideClick from '@src/services/hooks/useOustideClick'; import { useAuthData } from '@src/services/hooks/useAuth'; import useTeamApis from '../../useTeamApis'; -import { - TeamsDetail, - actionTypes, - initialState, - reducer, -} from '../../constants'; +import { actionTypes, initialState, reducer } from '../../constants'; +import { TeamsDetail } from '../../types'; import { getBots } from '../Bots'; import useGetTeamDetailsConstants from '../../util'; import styles from './Slack.module.scss'; diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index ddd1276..736f8a9 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -13,11 +13,11 @@ import { useAuthData } from '@src/services/hooks/useAuth'; import { useGetTeamDetailsConstants } from '@src/Pages/TeamRevamp/util'; import useTeamApis from '../../useTeamApis'; import { - TeamsDetail, actionTypes, initialState, reducer, } from '@src/Pages/TeamRevamp/constants'; +import { TeamsDetail } from '@src/Pages/TeamRevamp/types'; import styles from './TeamMemberDetails.module.scss'; type MemberType = { diff --git a/src/Pages/TeamRevamp/types.ts b/src/Pages/TeamRevamp/types.ts new file mode 100644 index 0000000..b61b654 --- /dev/null +++ b/src/Pages/TeamRevamp/types.ts @@ -0,0 +1,123 @@ +import { ChangeEvent } from 'react'; +import { TeamState } from '@src/slices/team1Slice'; +import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types'; +export interface SearchInputComponentProps { + value: string; + onChange: (event: ChangeEvent) => void; +} +export interface useTeamApiProps { + addMemberHandler: (emailIds: string) => void; + removeTeamMember: (memberId: string) => void; + makeManager: (memberId: string) => void; + updateDetails: ( + slackChannelId: string, + oncall: string, + psecOncall: string, + ) => void; +} +export interface TeamsData { + id?: string; + name?: string; + slackUserIds?: Array; + lastUpdatedAt?: string; + expand?: boolean; +} +export interface CreateTeamProps { + setTeam: React.Dispatch< + React.SetStateAction + >; +} +export interface BotsData { + id: string; + name: string; +} + +export interface TeamFormProps { + teamId: string; + isExpanded: boolean; + setLastUpdatedAt: (value: string) => void; +} + +export const slackUserOptions = [ + { + label: 'Email-id', + value: 'WorkEmailIds', + }, + { + label: 'Slack user-id', + value: 'SlackUserIds', + }, +]; + +export const userInputPlaceholders = { + 'Email-id': 'email ids', + 'Slack user-id': 'slack user ids', +}; + +export interface PickerOptionProps { + label: string; + value: string; +} +export interface Details { + teamId: number | undefined; + userEmail: string | undefined; + managerEmail: string | undefined; + manager: Participant | undefined; + teamMembers: any; + teamDetails: TeamsDetail; + teamData: TeamState; + DEFAULT_TEAM_ONCALL: string; + DEFAULT_TEAM_ONCALL_ID: string; + DEFAULT_TEAM_PSEC_ID: string; + DEFAULT_PSEC_ONCALL: string; + webChannelId: string; +} +export interface Participant { + name: string; + email: string; + id: string; +} +export interface TeamsDetail { + id?: number; + lastUpdatedAt?: string; + managerId?: string; + name?: string; + oncall?: { id: string; name: string }; + participants?: Array; + pse_oncall?: { id: string; name: string }; + slackUserIds?: string[]; + webhookSlackChannelId?: string; + webhookSlackChannelName?: string; +} +export interface AppState { + openDialog: boolean; + openModal: boolean; + emailIds: string; + selectedMember?: Participant; + hovered: { + ishovered: boolean; + id: string; + }; + slackChannelId: string; + oncall: { + label: string; + value: string; + }; + psecOncall: { + label: string; + value: string; + }; + input: string; + psecInput: string; + openOnCall: boolean; + openPsecOnCall: boolean; + teamName: string; + teamNameError: string; + emailError: string; + email: string; + clearModal: boolean; +} +export interface ActionType { + type: string; + payload: any; +} diff --git a/src/Pages/TeamRevamp/useTeamApis.tsx b/src/Pages/TeamRevamp/useTeamApis.tsx index 7b21b76..91e1b76 100644 --- a/src/Pages/TeamRevamp/useTeamApis.tsx +++ b/src/Pages/TeamRevamp/useTeamApis.tsx @@ -14,14 +14,21 @@ import { reducer, } from './constants'; import { useGetTeamDetailsConstants } from './util'; -import { useTeamApiProps } from './constants'; +import { useTeamApiProps } from './types'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; const useTeamApis = (): useTeamApiProps => { const dispatch = useDispatch(); const { teamId } = useGetTeamDetailsConstants(); const [state, dispatchData] = useReducer(reducer, initialState); + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; const addMemberHandler = (emailIds: string): void => { + fireEvent(EVENT_NAME.Houston_Add_Member, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); const endPoint = UPDATE_TEAM_DATA(); const finalSlackData = emailIds.includes(',') ? emailIds.split(',').map(item => item?.trim()) @@ -47,6 +54,9 @@ const useTeamApis = (): useTeamApiProps => { }); }; const removeTeamMember = (memberId: string): void => { + fireEvent(EVENT_NAME.Houston_Remove_Member, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); const endpoint = REMOVE_TEAM_MEMBER(teamId?.toString() || '', memberId); ApiService.delete(endpoint) .then(response => { @@ -66,6 +76,9 @@ const useTeamApis = (): useTeamApiProps => { }; const makeManager = (memberId: string): void => { + fireEvent(EVENT_NAME.Houston_Make_Manager, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); const endpoint = MAKE_MANAGER(teamId?.toString() || '', memberId); ApiService.patch(endpoint, {}) @@ -86,6 +99,17 @@ const useTeamApis = (): useTeamApiProps => { oncall: string, psecOncall: string, ): void => { + if (oncall) { + fireEvent(EVENT_NAME.Houston_Add_Oncall, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); + } + + if (psecOncall) { + fireEvent(EVENT_NAME.Houston_Add_Pseconcall, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); + } const endPoint = UPDATE_TEAM_DATA(); ApiService.post(endPoint, { id: teamId, diff --git a/src/Pages/TeamRevamp/util.ts b/src/Pages/TeamRevamp/util.ts index 41ae771..e248c44 100644 --- a/src/Pages/TeamRevamp/util.ts +++ b/src/Pages/TeamRevamp/util.ts @@ -1,6 +1,6 @@ import { useSelector } from 'react-redux'; import { TeamState, selectSearchTeamData } from '@src/slices/team1Slice'; -import { Details } from './constants'; +import { Details } from './types'; export const useTeamDetails = (): TeamState => { return useSelector(selectSearchTeamData); }; diff --git a/src/components/LeftNav/index.tsx b/src/components/LeftNav/index.tsx index cece882..840e723 100644 --- a/src/components/LeftNav/index.tsx +++ b/src/components/LeftNav/index.tsx @@ -48,6 +48,13 @@ const LeftNav: React.FC = ({ children }) => { Icon: DashboardIcon, handleNavigation: () => navigate('/'), }, + { + itemType: 'simpleNavItem', + label: 'Team', + route: '/team', + Icon: TeamIcon, + handleNavigation: () => navigate('/team'), + }, { itemType: 'simpleNavItem', label: 'Severity', @@ -62,13 +69,6 @@ const LeftNav: React.FC = ({ children }) => { Icon: GroupIcon, handleNavigation: () => navigate('/metrics'), }, - { - itemType: 'simpleNavItem', - label: 'Team', - route: '/Teams', - Icon: TeamIcon, - handleNavigation: () => navigate('/Teams'), - }, { itemType: 'simpleNavItem', label: 'Jira dashboard', diff --git a/src/router.tsx b/src/router.tsx index a64d89a..dd6c79e 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -32,7 +32,7 @@ const routes: CustomRouteObject[] = [ }, { id: 'TEAM', - path: '/Teams', + path: '/team', element: , }, { diff --git a/src/slices/team1Slice.tsx b/src/slices/team1Slice.tsx index 3de0f05..a28dc06 100644 --- a/src/slices/team1Slice.tsx +++ b/src/slices/team1Slice.tsx @@ -3,9 +3,8 @@ import { PayloadAction, createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { FETCH_SINGLE_TEAM_DATA, FETCH_TEAM_DATA, - TeamsData, - TeamsDetail, } from '@src/Pages/TeamRevamp/constants'; +import { TeamsData, TeamsDetail } from '@src/Pages/TeamRevamp/types'; import { ApiService } from '@src/services/api'; import { RootState } from '@src/store'; From db55813b89a7ffa77839860c71297fba9d765712 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 22 Jan 2024 12:18:23 +0530 Subject: [PATCH 36/39] TP-51166 | UAT callouts fixed --- src/Pages/TeamRevamp/TeamList/SearchInput.tsx | 7 ++++- .../TeamRevamp/TeamList/TeamList.module.scss | 11 +++++-- src/Pages/TeamRevamp/TeamList/index.tsx | 23 ++++++++++++-- .../partials/SlackDetails/Slack.module.scss | 6 ++++ .../partials/SlackDetails/index.tsx | 8 +++++ .../partials/TeamMemberDetails/index.tsx | 30 +++++++++++-------- src/Pages/TeamRevamp/types.ts | 1 + 7 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/Pages/TeamRevamp/TeamList/SearchInput.tsx b/src/Pages/TeamRevamp/TeamList/SearchInput.tsx index 69b5bb4..1bf991e 100644 --- a/src/Pages/TeamRevamp/TeamList/SearchInput.tsx +++ b/src/Pages/TeamRevamp/TeamList/SearchInput.tsx @@ -7,13 +7,18 @@ import styles from './TeamList.module.scss'; const SearchInput: React.FC = ({ value, onChange, + isAdmin, }) => { + const containerClass = isAdmin + ? styles['search-bar'] + : styles['search-input']; return ( } onChange={onChange} - containerClassName={styles['search-input']} + containerClassName={containerClass} value={value} + placeholder="Search by team " /> ); }; diff --git a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss index 0f83ec9..d81b64f 100644 --- a/src/Pages/TeamRevamp/TeamList/TeamList.module.scss +++ b/src/Pages/TeamRevamp/TeamList/TeamList.module.scss @@ -19,11 +19,18 @@ margin-top: 8px; margin-left: 8px; } -.search-input { - width: 234px; +@mixin search { height: 36px; margin-left: 12px; } +.search-bar { + @include search; + width: 234px; +} +.search-input { + @include search; + width: 272px; +} .select-picker-wrapper { min-width: 300px; min-height: calc(100vh - 228px) !important; diff --git a/src/Pages/TeamRevamp/TeamList/index.tsx b/src/Pages/TeamRevamp/TeamList/index.tsx index c3deab9..07d2a1d 100644 --- a/src/Pages/TeamRevamp/TeamList/index.tsx +++ b/src/Pages/TeamRevamp/TeamList/index.tsx @@ -122,11 +122,22 @@ const TeamList: React.FC = () => { } }; + const returnIsFilteredResultEmpty = (): boolean => { + const filteredResult = options.filter(item => + item.label.toLowerCase().includes(input.toLowerCase()), + ); + return !filteredResult.length; + }; + return (
- + {Role.includes('Admin') && (
)} - + {returnIsFilteredResultEmpty() ? ( +
+ {' '} + + {' '} + No results found + +
+ ) : null} { ); return participantEmails?.includes(userEmail); }; + const isHintEnabled = + isUserParticipant(teamDetails) || + Role.includes('Admin') || + userEmail === managerEmail; + const hintClassName = isHintEnabled + ? styles['hint-text'] + : styles['hint-text-disabled']; return (
@@ -174,6 +181,7 @@ const TeamDetails: React.FC = () => { onChange={handleslackChannelId} containerClassName={styles['team-input-wrapper']} value={webChannelId} + hintTextClasses={hintClassName} hintMsg="Channel name populates after entering id" disabled={ !isUserParticipant(teamDetails) && !Role.includes('Admin') diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index 736f8a9..e173f8a 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -37,10 +37,12 @@ const TeamMemberDetails: React.FC = () => { ); return participantEmails?.includes(userEmail); }; + const handleAddMember = (e: React.ChangeEvent): void => { + const inputValue = e.target.value; dispatchData({ type: actionTypes.SET_EMAIL_IDS, - payload: e.target.value, + payload: inputValue, }); }; const onKeyPressClickHandler = (event: React.KeyboardEvent): void => { @@ -121,7 +123,6 @@ const TeamMemberDetails: React.FC = () => { styles['items-wrapper'] ); }; - return (
@@ -131,16 +132,21 @@ const TeamMemberDetails: React.FC = () => {
{' '}
- - + {(isUserParticipant(teamDetails) || + Role.includes('Admin') || + userEmail === managerEmail) && ( + + )}
diff --git a/src/Pages/TeamRevamp/types.ts b/src/Pages/TeamRevamp/types.ts index b61b654..881691c 100644 --- a/src/Pages/TeamRevamp/types.ts +++ b/src/Pages/TeamRevamp/types.ts @@ -4,6 +4,7 @@ import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicke export interface SearchInputComponentProps { value: string; onChange: (event: ChangeEvent) => void; + isAdmin: boolean; } export interface useTeamApiProps { addMemberHandler: (emailIds: string) => void; From 218e9e103b92c4001415471103e629025ee6782d Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 22 Jan 2024 12:40:50 +0530 Subject: [PATCH 37/39] TP-51166 | validate email's error for add member --- src/Pages/TeamRevamp/constants.ts | 7 ++++++ .../partials/TeamMemberDetails/index.tsx | 25 ++++++++++++++++++- src/Pages/TeamRevamp/types.ts | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Pages/TeamRevamp/constants.ts b/src/Pages/TeamRevamp/constants.ts index 0f24bab..2e7f89f 100644 --- a/src/Pages/TeamRevamp/constants.ts +++ b/src/Pages/TeamRevamp/constants.ts @@ -45,6 +45,7 @@ export const actionTypes = { SET_EMAIL_ERROR: 'SET_EMAIL_ERROR', SET_EMAIL: 'SET_EMAIL', SET_CLEAR_MODAL: 'SET_CLEAR_MODAL', + SET_ADD_MEMBER_ERROR: 'SET_ADD_MEMBER_ERROR', }; export const initialState: AppState = { @@ -74,6 +75,7 @@ export const initialState: AppState = { emailError: '', email: '', clearModal: false, + addMemberError: '', }; export const reducer = (state: AppState, action: ActionType): AppState => { @@ -169,6 +171,11 @@ export const reducer = (state: AppState, action: ActionType): AppState => { teamNameError: '', emailError: '', }; + case actionTypes.SET_ADD_MEMBER_ERROR: + return { + ...state, + addMemberError: action.payload, + }; default: return state; } diff --git a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx index e173f8a..edc2e0e 100644 --- a/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx +++ b/src/Pages/TeamRevamp/partials/TeamMemberDetails/index.tsx @@ -14,8 +14,10 @@ import { useGetTeamDetailsConstants } from '@src/Pages/TeamRevamp/util'; import useTeamApis from '../../useTeamApis'; import { actionTypes, + emailRegularExpression, initialState, reducer, + validEmail, } from '@src/Pages/TeamRevamp/constants'; import { TeamsDetail } from '@src/Pages/TeamRevamp/types'; import styles from './TeamMemberDetails.module.scss'; @@ -38,14 +40,34 @@ const TeamMemberDetails: React.FC = () => { return participantEmails?.includes(userEmail); }; + const validateEmail = (value: string): void => { + const emailAddresses = value.split(',').map(email => email.trim()); + const errorMessage = emailAddresses + .map(email => + validEmail.test(email) + ? emailRegularExpression.test(email) + ? '' + : 'Please enter a Navi email ID' + : '', + ) + .find(errorMessage => errorMessage !== ''); + dispatchData({ + type: actionTypes.SET_ADD_MEMBER_ERROR, + payload: errorMessage || '', + }); + }; const handleAddMember = (e: React.ChangeEvent): void => { const inputValue = e.target.value; dispatchData({ type: actionTypes.SET_EMAIL_IDS, payload: inputValue, }); + validateEmail(inputValue); }; const onKeyPressClickHandler = (event: React.KeyboardEvent): void => { + if (state.addMemberError) { + return; + } if (event.key === 'Enter') { addMemberHandler(state.emailIds); dispatchData({ @@ -138,13 +160,14 @@ const TeamMemberDetails: React.FC = () => { )}
diff --git a/src/Pages/TeamRevamp/types.ts b/src/Pages/TeamRevamp/types.ts index 881691c..47cebde 100644 --- a/src/Pages/TeamRevamp/types.ts +++ b/src/Pages/TeamRevamp/types.ts @@ -117,6 +117,7 @@ export interface AppState { emailError: string; email: string; clearModal: boolean; + addMemberError: string; } export interface ActionType { type: string; From 62d5e81c3b544bfea7953a12b69dafba30bc02a2 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 22 Jan 2024 12:52:15 +0530 Subject: [PATCH 38/39] TP-51166 | toast success for remove member --- src/Pages/TeamRevamp/useTeamApis.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Pages/TeamRevamp/useTeamApis.tsx b/src/Pages/TeamRevamp/useTeamApis.tsx index 91e1b76..90e56bf 100644 --- a/src/Pages/TeamRevamp/useTeamApis.tsx +++ b/src/Pages/TeamRevamp/useTeamApis.tsx @@ -1,7 +1,6 @@ import { useReducer } from 'react'; import { useDispatch } from 'react-redux'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; -import AlertIcon from '@src/assets/AlertIcon'; import { AppDispatch } from '@src/store'; import { ApiService } from '@src/services/api'; import { fetchTeamDetails, fetchTeams } from '@src/slices/team1Slice'; @@ -61,9 +60,7 @@ const useTeamApis = (): useTeamApiProps => { ApiService.delete(endpoint) .then(response => { if (response.status === 200) { - toast(response.data.data, { - icon: , - }); + toast.success(response.data.data); dispatch(fetchTeamDetails(teamId?.toString() || '')); dispatch(fetchTeams()); } else { From 2ffca1132f8bd1a78e7d23650dc42b9a146618b5 Mon Sep 17 00:00:00 2001 From: pooja-jaiswal_navi Date: Mon, 22 Jan 2024 17:37:46 +0530 Subject: [PATCH 39/39] TP-51166 | left nav position for team page --- src/components/LeftNav/index.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/LeftNav/index.tsx b/src/components/LeftNav/index.tsx index 840e723..0ee533d 100644 --- a/src/components/LeftNav/index.tsx +++ b/src/components/LeftNav/index.tsx @@ -48,13 +48,6 @@ const LeftNav: React.FC = ({ children }) => { Icon: DashboardIcon, handleNavigation: () => navigate('/'), }, - { - itemType: 'simpleNavItem', - label: 'Team', - route: '/team', - Icon: TeamIcon, - handleNavigation: () => navigate('/team'), - }, { itemType: 'simpleNavItem', label: 'Severity', @@ -62,6 +55,13 @@ const LeftNav: React.FC = ({ children }) => { Icon: AlertOutlineIcon, handleNavigation: () => navigate('/severity'), }, + { + itemType: 'simpleNavItem', + label: 'Team', + route: '/team', + Icon: TeamIcon, + handleNavigation: () => navigate('/team'), + }, { itemType: 'simpleNavItem', label: 'Houston metrics',