TP-51166 | Code optimization and pr review comments resolve
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<AppDispatch>();
|
||||
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<HTMLInputElement>(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<HTMLInputElement>,
|
||||
): 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<HTMLDivElement>;
|
||||
|
||||
const refPsecOncall = useOutsideClick({
|
||||
callback: handlePsecOncallOutsideClick,
|
||||
}) as MutableRefObject<HTMLDivElement>;
|
||||
|
||||
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<HTMLInputElement>): void => {
|
||||
dispatchData({
|
||||
type: actionTypes.SET_INPUT,
|
||||
payload: e.target.value,
|
||||
});
|
||||
};
|
||||
const handlePsecInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
): 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 (
|
||||
<div className={styles['fallback-component']}>
|
||||
@@ -197,132 +15,9 @@ const TeamDetails = () => {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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 (
|
||||
<div className={styles['team-details-wrapper']}>
|
||||
<Typography variant="h1" className={styles['team-name']}>
|
||||
{teamDetails?.name}
|
||||
</Typography>{' '}
|
||||
<Typography variant="h5" className={styles['member-div']}>
|
||||
SLACK DETAILS
|
||||
</Typography>{' '}
|
||||
<div className={styles['slack-details-wrapper']}>
|
||||
<div className={styles['team-channel-wrapper']}>
|
||||
<div className={styles['info-icon-wrapper']}>
|
||||
<Typography variant="h6">Team channel</Typography>
|
||||
<div className={styles['info-icon']}>
|
||||
<Tooltip
|
||||
text="Slack channel I.D. can be found at the bottom of
|
||||
the channel details window"
|
||||
withPointer
|
||||
position={'right'}
|
||||
>
|
||||
<AlertOutlineIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<BorderedInput
|
||||
inputLabel=""
|
||||
inputSize="medium"
|
||||
placeholder="Enter channel I.D"
|
||||
onChange={handleslackChannelId}
|
||||
containerClassName={styles['team-input-wrapper']}
|
||||
value={webChannelId}
|
||||
hintMsg="Channel name populates after entering id"
|
||||
disabled={
|
||||
!isUserParticipant(teamDetails) && !Role.includes('Admin')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles['on-call-wrapper']}>
|
||||
<div ref={refOncall}>
|
||||
<Typography variant="h6">Team on call</Typography>{' '}
|
||||
<div
|
||||
onClick={handleOncallPicker}
|
||||
className={styles['on-call-handler']}
|
||||
>
|
||||
<BorderedInput
|
||||
onChange={handleInputChange}
|
||||
placeholder="@oncall_handle"
|
||||
containerClassName={styles['search-input']}
|
||||
value={state.input || DEFAULT_TEAM_ONCALL}
|
||||
disabled={
|
||||
!isUserParticipant(teamDetails) && !Role.includes('Admin')
|
||||
}
|
||||
></BorderedInput>
|
||||
</div>
|
||||
{state.openOnCall && (
|
||||
<SelectPicker
|
||||
options={getBots()}
|
||||
onSelectionChange={handleOncallChange}
|
||||
selectedValue={state.oncall.value || DEFAULT_TEAM_ONCALL_ID}
|
||||
multiSelect={false}
|
||||
updateSearch={
|
||||
state.input || searchRef.current ? state.input : ''
|
||||
}
|
||||
wrapperClasses={styles['select-picker-wrapper']}
|
||||
></SelectPicker>
|
||||
)}
|
||||
</div>
|
||||
<div ref={refPsecOncall}>
|
||||
<Typography variant="h6">PSEC on call</Typography>
|
||||
<div
|
||||
onClick={handleOpenPsecOnCallPicker}
|
||||
className={styles['on-call-handler']}
|
||||
>
|
||||
<BorderedInput
|
||||
onChange={handlePsecInputChange}
|
||||
placeholder="@psec_oncall_handle"
|
||||
containerClassName={styles['search-input']}
|
||||
value={state.psecInput || DEFAULT_PSEC_ONCALL}
|
||||
disabled={
|
||||
!isUserParticipant(teamDetails) && !Role.includes('Admin')
|
||||
}
|
||||
></BorderedInput>
|
||||
</div>
|
||||
{state.openPsecOnCall && (
|
||||
<SelectPicker
|
||||
options={getBots()}
|
||||
onSelectionChange={handlePsecOncall}
|
||||
multiSelect={false}
|
||||
selectedValue={state.psecOncall.value || DEFAULT_TEAM_PSEC_ID}
|
||||
updateSearch={
|
||||
state.psecInput || searchRef.current ? state.psecInput : ''
|
||||
}
|
||||
wrapperClasses={styles['select-picker-wrapper']}
|
||||
></SelectPicker>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
managerEmail === userEmail ||
|
||||
isUserParticipant(teamDetails) ||
|
||||
Role.includes('Admin')
|
||||
? styles['update-details']
|
||||
: styles['update-details-disabled']
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={submitHandler}
|
||||
disabled={
|
||||
!state.slackChannelId && !state.psecInput && !state.input
|
||||
}
|
||||
>
|
||||
Update details
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SlackDetails />
|
||||
<TeamMemberDetails />
|
||||
</div>
|
||||
);
|
||||
|
||||
21
src/Pages/TeamRevamp/TeamList/SearchInput.tsx
Normal file
21
src/Pages/TeamRevamp/TeamList/SearchInput.tsx
Normal file
@@ -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<SearchInputComponentProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<BorderedInput
|
||||
LeftInputAdornment={<SearchIcon />}
|
||||
onChange={onChange}
|
||||
containerClassName={styles['search-input']}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchInput;
|
||||
@@ -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<AppDispatch>();
|
||||
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 (
|
||||
<div>
|
||||
<div className={styles['team-div']}>
|
||||
<div className={styles['search-wrapper']}>
|
||||
<BorderedInput
|
||||
LeftInputAdornment={<SearchIcon />}
|
||||
onChange={handleInputChange}
|
||||
containerClassName={styles['search-input']}
|
||||
value={input || ''}
|
||||
/>
|
||||
|
||||
<SearchInput value={input} onChange={handleInputChange} />
|
||||
{Role.includes('Admin') && (
|
||||
<Button
|
||||
startAdornment={<AddIcon color="white" size="lg" />}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types';
|
||||
import { createURL } from '@src/services/globalUtils';
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
const URL_PREFIX = createURL('/houston');
|
||||
|
||||
export const FETCH_TEAM_DATA = `${URL_PREFIX}/teams`;
|
||||
@@ -22,9 +25,13 @@ export const MAKE_MANAGER = (teamId: string, userId: string): string => {
|
||||
};
|
||||
|
||||
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<HTMLInputElement>) => void;
|
||||
}
|
||||
export interface TeamsData {
|
||||
id?: string;
|
||||
name?: string;
|
||||
@@ -32,7 +39,11 @@ export interface TeamsData {
|
||||
lastUpdatedAt?: string;
|
||||
expand?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateTeamProps {
|
||||
setTeam: React.Dispatch<
|
||||
React.SetStateAction<SelectPickerOptionProps | undefined>
|
||||
>;
|
||||
}
|
||||
export interface BotsData {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -18,14 +18,11 @@ import styles from './Team.module.scss';
|
||||
const TeamRevamp: FC = () => {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const teamData: TeamState = useSelector(selectSearchTeamData);
|
||||
const { data } = teamData;
|
||||
const { selectedTeam } = teamData;
|
||||
const { data, selectedTeam } = teamData;
|
||||
const selectedTeamId = selectedTeam?.id || '';
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchTeams());
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
fetchAllBots();
|
||||
}, []);
|
||||
|
||||
@@ -34,6 +31,7 @@ const TeamRevamp: FC = () => {
|
||||
dispatch(setSelectedTeam(data[0]));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTeam) {
|
||||
dispatch(fetchTeamDetails(selectedTeamId.toString()));
|
||||
@@ -46,7 +44,6 @@ const TeamRevamp: FC = () => {
|
||||
<Typography variant="h3">Teams</Typography>
|
||||
<div className={styles['wrapper-class']}>
|
||||
<TeamList />
|
||||
|
||||
<TeamDetails />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<PickerOptionProps> = [];
|
||||
|
||||
@@ -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<SelectPickerOptionProps | undefined>
|
||||
>;
|
||||
}
|
||||
|
||||
const CreateTeam: React.FC<CreateTeamProps> = ({ setTeam }) => {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const { modalOpen } = useSelector(selectSearchTeamData);
|
||||
@@ -44,7 +39,6 @@ const CreateTeam: React.FC<CreateTeamProps> = ({ 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)
|
||||
|
||||
62
src/Pages/TeamRevamp/partials/SlackDetails/Slack.module.scss
Normal file
62
src/Pages/TeamRevamp/partials/SlackDetails/Slack.module.scss
Normal file
@@ -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;
|
||||
}
|
||||
311
src/Pages/TeamRevamp/partials/SlackDetails/index.tsx
Normal file
311
src/Pages/TeamRevamp/partials/SlackDetails/index.tsx
Normal file
@@ -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<AppDispatch>();
|
||||
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<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
dispatchData({
|
||||
type: actionTypes.SET_INPUT,
|
||||
payload: '',
|
||||
});
|
||||
dispatchData({
|
||||
type: actionTypes.SET_PSEC_INPUT,
|
||||
payload: '',
|
||||
});
|
||||
}, [teamData]);
|
||||
|
||||
const handleslackChannelId = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
): 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<HTMLDivElement>;
|
||||
|
||||
const refPsecOncall = useOutsideClick({
|
||||
callback: handlePsecOncallOutsideClick,
|
||||
}) as MutableRefObject<HTMLDivElement>;
|
||||
|
||||
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<HTMLInputElement>): void => {
|
||||
dispatchData({
|
||||
type: actionTypes.SET_INPUT,
|
||||
payload: e.target.value,
|
||||
});
|
||||
};
|
||||
const handlePsecInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
): 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 (
|
||||
<div className={styles['team-details-wrapper']}>
|
||||
<Typography variant="h1" className={styles['team-name']}>
|
||||
{teamDetails?.name}
|
||||
</Typography>{' '}
|
||||
<Typography variant="h5" className={styles['member-div']}>
|
||||
SLACK DETAILS
|
||||
</Typography>{' '}
|
||||
<div className={styles['slack-details-wrapper']}>
|
||||
<div className={styles['team-channel-wrapper']}>
|
||||
<div className={styles['info-icon-wrapper']}>
|
||||
<Typography variant="h6">Team channel</Typography>
|
||||
<div className={styles['info-icon']}>
|
||||
<Tooltip
|
||||
text="Slack channel I.D. can be found at the bottom of
|
||||
the channel details window"
|
||||
withPointer
|
||||
position={'right'}
|
||||
>
|
||||
<AlertOutlineIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<BorderedInput
|
||||
inputLabel=""
|
||||
inputSize="medium"
|
||||
placeholder="Enter channel I.D"
|
||||
onChange={handleslackChannelId}
|
||||
containerClassName={styles['team-input-wrapper']}
|
||||
value={webChannelId}
|
||||
hintMsg="Channel name populates after entering id"
|
||||
disabled={
|
||||
!isUserParticipant(teamDetails) && !Role.includes('Admin')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles['on-call-wrapper']}>
|
||||
<div ref={refOncall}>
|
||||
<Typography variant="h6">Team on call</Typography>{' '}
|
||||
<div
|
||||
onClick={handleOncallPicker}
|
||||
className={styles['on-call-handler']}
|
||||
>
|
||||
<BorderedInput
|
||||
onChange={handleInputChange}
|
||||
placeholder="@oncall_handle"
|
||||
containerClassName={styles['search-input']}
|
||||
value={state.input || DEFAULT_TEAM_ONCALL}
|
||||
disabled={
|
||||
!isUserParticipant(teamDetails) && !Role.includes('Admin')
|
||||
}
|
||||
></BorderedInput>
|
||||
</div>
|
||||
{state.openOnCall && (
|
||||
<SelectPicker
|
||||
options={getBots()}
|
||||
onSelectionChange={handleOncallChange}
|
||||
selectedValue={state.oncall.value || DEFAULT_TEAM_ONCALL_ID}
|
||||
multiSelect={false}
|
||||
updateSearch={
|
||||
state.input || searchRef.current ? state.input : ''
|
||||
}
|
||||
wrapperClasses={styles['select-picker-wrapper']}
|
||||
></SelectPicker>
|
||||
)}
|
||||
</div>
|
||||
<div ref={refPsecOncall}>
|
||||
<Typography variant="h6">PSEC on call</Typography>
|
||||
<div
|
||||
onClick={handleOpenPsecOnCallPicker}
|
||||
className={styles['on-call-handler']}
|
||||
>
|
||||
<BorderedInput
|
||||
onChange={handlePsecInputChange}
|
||||
placeholder="@psec_oncall_handle"
|
||||
containerClassName={styles['search-input']}
|
||||
value={state.psecInput || DEFAULT_PSEC_ONCALL}
|
||||
disabled={
|
||||
!isUserParticipant(teamDetails) && !Role.includes('Admin')
|
||||
}
|
||||
></BorderedInput>
|
||||
</div>
|
||||
{state.openPsecOnCall && (
|
||||
<SelectPicker
|
||||
options={getBots()}
|
||||
onSelectionChange={handlePsecOncall}
|
||||
multiSelect={false}
|
||||
selectedValue={state.psecOncall.value || DEFAULT_TEAM_PSEC_ID}
|
||||
updateSearch={
|
||||
state.psecInput || searchRef.current ? state.psecInput : ''
|
||||
}
|
||||
wrapperClasses={styles['select-picker-wrapper']}
|
||||
></SelectPicker>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
managerEmail === userEmail ||
|
||||
isUserParticipant(teamDetails) ||
|
||||
Role.includes('Admin')
|
||||
? styles['update-details']
|
||||
: styles['update-details-disabled']
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={updateDetails}
|
||||
disabled={
|
||||
!state.slackChannelId && !state.psecInput && !state.input
|
||||
}
|
||||
>
|
||||
Update details
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamDetails;
|
||||
@@ -9,6 +9,7 @@
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
justify-content: space-around;
|
||||
gap: 12px;
|
||||
}
|
||||
.item-details {
|
||||
margin-top: 12px;
|
||||
|
||||
@@ -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<AppDispatch>();
|
||||
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 (
|
||||
<div className={styles['fallback-component']}>
|
||||
<FallbackComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className={styles['fallback-component']}>
|
||||
<FallbackComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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: <AlertIcon />,
|
||||
});
|
||||
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 = () => {
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
removeMemberHandler(state.selectedMember?.id.toString() || '');
|
||||
removeTeamMember(state.selectedMember?.id.toString() || '');
|
||||
handleResetDialog();
|
||||
}}
|
||||
className={styles['remove-button']}
|
||||
|
||||
85
src/Pages/TeamRevamp/useTeamApis.tsx
Normal file
85
src/Pages/TeamRevamp/useTeamApis.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { toast } from '@navi/web-ui/lib/primitives/Toast';
|
||||
import { AlertIcon } from '@navi/web-ui/lib/icons';
|
||||
import { AppDispatch } from '@src/store';
|
||||
import { ApiService } from '@src/services/api';
|
||||
import { fetchTeamDetails, fetchTeams } from '@src/slices/team1Slice';
|
||||
import {
|
||||
MAKE_MANAGER,
|
||||
REMOVE_TEAM_MEMBER,
|
||||
UPDATE_TEAM_DATA,
|
||||
} from './constants';
|
||||
import { useGetTeamDetailsConstants } from './util';
|
||||
|
||||
const useTeamApis = () => {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const { teamId } = useGetTeamDetailsConstants();
|
||||
|
||||
const addMemberHandler = (emailIds: string): void => {
|
||||
const endPoint = UPDATE_TEAM_DATA();
|
||||
const finalSlackData = emailIds.includes(',')
|
||||
? emailIds.split(',').map(item => item?.trim())
|
||||
: [emailIds];
|
||||
console.log(teamId, emailIds, finalSlackData);
|
||||
ApiService.post(endPoint, {
|
||||
id: teamId,
|
||||
workEmailIds: finalSlackData,
|
||||
})
|
||||
.then(response => {
|
||||
toast.success(response?.data?.data);
|
||||
dispatch(fetchTeamDetails(teamId?.toString() || ''));
|
||||
dispatch(fetchTeams());
|
||||
})
|
||||
.catch(error => {
|
||||
const toastMessage = `${
|
||||
error?.response?.data?.error?.message
|
||||
? `${error?.response?.data?.error?.message}`
|
||||
: 'Something went wrong, please try again later'
|
||||
}`;
|
||||
toast.error(toastMessage);
|
||||
});
|
||||
};
|
||||
const removeTeamMember = (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: <AlertIcon />,
|
||||
});
|
||||
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 makeManager = (memberId: string): void => {
|
||||
const endpoint = MAKE_MANAGER(teamId?.toString() || '', memberId);
|
||||
|
||||
ApiService.patch(endpoint, {})
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
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}`);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
addMemberHandler,
|
||||
removeTeamMember,
|
||||
makeManager,
|
||||
};
|
||||
};
|
||||
|
||||
export default useTeamApis;
|
||||
55
src/Pages/TeamRevamp/util.ts
Normal file
55
src/Pages/TeamRevamp/util.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { TeamState, selectSearchTeamData } from '@src/slices/team1Slice';
|
||||
export const useTeamDetails = () => {
|
||||
return useSelector(selectSearchTeamData);
|
||||
};
|
||||
export const useGetTeamDetailsConstants = () => {
|
||||
const userData = JSON.parse(localStorage.getItem('user-data') || '{}');
|
||||
const teamData = useTeamDetails();
|
||||
const teamDetails = teamData.teamDetails;
|
||||
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 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
|
||||
: '';
|
||||
return {
|
||||
teamId,
|
||||
userEmail,
|
||||
managerEmail,
|
||||
manager,
|
||||
teamMembers,
|
||||
teamDetails,
|
||||
teamData,
|
||||
DEFAULT_TEAM_ONCALL,
|
||||
DEFAULT_TEAM_ONCALL_ID,
|
||||
DEFAULT_TEAM_PSEC_ID,
|
||||
DEFAULT_PSEC_ONCALL,
|
||||
webChannelId,
|
||||
};
|
||||
};
|
||||
|
||||
export default useGetTeamDetailsConstants;
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
FETCH_TEAM_DATA,
|
||||
TeamsData,
|
||||
TeamsDetail,
|
||||
} from '@src/Pages/TeamRevamp/partials/constants';
|
||||
} from '@src/Pages/TeamRevamp/constants';
|
||||
import { ApiService } from '@src/services/api';
|
||||
import { RootState } from '@src/store';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user