INFRA-2867 | integrate product and assigner-responder api

This commit is contained in:
dhruvjoshi
2024-03-07 18:14:19 +05:30
parent 96a542fe73
commit 76373fd6b3
8 changed files with 287 additions and 131 deletions

View File

@@ -19,8 +19,8 @@ export const CREATE_INCIDENT = createURL('/houston/create-incident-v3');
export const PRODUCT_LIST = createURL('/houston/user/products'); export const PRODUCT_LIST = createURL('/houston/user/products');
export const FETCH_ASSIGNER_AND_RESPONDER = (payload: number[]) => { export const FETCH_ASSIGNER_AND_RESPONDER = (payload: number[]) => {
console.log(payload, 'payload'); const queryParams = payload.join('&productID=');
return `${URL_PREFIX}/product/assigner-responder?productIds=${payload}`; return `${URL_PREFIX}/product/assigner-and-responder?productID=${queryParams}`;
}; };
export const TimeSpan = [ export const TimeSpan = [
@@ -89,6 +89,8 @@ export const actionTypes = {
export const initialState = { export const initialState = {
open: false, open: false,
teams: [], teams: [],
assigners: [],
responders: [],
title: '', title: '',
severity: [], severity: [],
selectedSeverity: [], selectedSeverity: [],
@@ -100,6 +102,7 @@ export const initialState = {
isTitleValid: true, isTitleValid: true,
isDescriptionValid: true, isDescriptionValid: true,
}; };
export const reducer = (state, action) => { export const reducer = (state, action) => {
switch (action.type) { switch (action.type) {
case actionTypes.SET_TEAMS: case actionTypes.SET_TEAMS:

View File

@@ -152,4 +152,7 @@
color: var(--navi-color-blue-base) !important; color: var(--navi-color-blue-base) !important;
} }
} }
}
.create-incident-single-selector{
width: 335px !important;
} }

View File

@@ -25,12 +25,17 @@ import {
reducer, reducer,
} from '@src/Pages/Dashboard/constants'; } from '@src/Pages/Dashboard/constants';
import styles from './CreateIncident.module.scss'; import styles from './CreateIncident.module.scss';
import { Team } from '@src/Pages/Dashboard/type'; import { Product, Team } from '@src/Pages/Dashboard/type';
import useClickStream from '@src/services/clickStream'; import useClickStream from '@src/services/clickStream';
import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values';
import ProductSelector from './ProductSelector'; import ProductSelector from './ProductSelector';
import IncidentSingleSelector from './IncidentSingleSelector'; import IncidentSingleSelector from './IncidentSingleSelector';
import {
updateFilterConfig,
updateProductList,
updateAssignerAndResponder,
} from './CreateIncidentHelper';
import useIncidentFormApi from '../useDashboardApi';
interface CreateIncidentFormProps { interface CreateIncidentFormProps {
startIncidentSearch: () => void; startIncidentSearch: () => void;
handleDrawerClose: () => void; handleDrawerClose: () => void;
@@ -42,10 +47,13 @@ const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
const [state, dispatch] = useReducer(reducer, initialState); const [state, dispatch] = useReducer(reducer, initialState);
const [loading, setIsLoading] = useState(false); const [loading, setIsLoading] = useState(false);
const { fireEvent } = useClickStream(); const { fireEvent } = useClickStream();
const { fetchFilterConfig, fetchProductList, fetchAssignerAndResponder } =
useIncidentFormApi();
const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY;
const ref = useOutsideClick({ const ref = useOutsideClick({
callback: handleClickOutside, callback: handleClickOutside,
}) as MutableRefObject<HTMLDivElement>; }) as MutableRefObject<HTMLDivElement>;
function handleClickOutside() { function handleClickOutside() {
dispatch({ type: actionTypes.SET_OPEN_SELECT, payload: false }); dispatch({ type: actionTypes.SET_OPEN_SELECT, payload: false });
} }
@@ -72,10 +80,11 @@ const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
dispatch({ type: actionTypes.SET_TITLE, payload: inputValue }); dispatch({ type: actionTypes.SET_TITLE, payload: inputValue });
validateTitle(inputValue); validateTitle(inputValue);
}; };
const handleTeamChange = ( const handleTeamChange = (val: SelectPickerOptionProps[]) => {
val: SelectPickerOptionProps | SelectPickerOptionProps[],
) => {
dispatch({ type: actionTypes.SET_SELECTED_TEAM, payload: val }); dispatch({ type: actionTypes.SET_SELECTED_TEAM, payload: val });
if (val?.length === 0) return;
const productIds = val?.map(team => team.value as number);
fetchAssignerAndResponderData(productIds);
}; };
const handleAssignerChange = (val: SelectPickerValue) => { const handleAssignerChange = (val: SelectPickerValue) => {
dispatch({ type: actionTypes.SET_SELECTED_ASSIGNER, payload: val }); dispatch({ type: actionTypes.SET_SELECTED_ASSIGNER, payload: val });
@@ -94,97 +103,104 @@ const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
validateDescription(description); validateDescription(description);
}; };
const fetchData = async () => { // const fetchAssignerAndResponder = (product: number[]) => {
try { // appDispatch(fetchAssignerAndResponderData(product))
const filterConfigResponse = await ApiService.get(FETCH_FILTER_CONFIG); // .unwrap()
const filterConfigData = filterConfigResponse?.data?.data; // .then(assignerResponderResponse => {
// const assignerResponderData = assignerResponderResponse?.data;
if (!filterConfigData) { // dispatch({
toast.error( // type: actionTypes.SET_ASSIGNERS,
'Something went wrong with filter config. Please try again later', // payload: assignerResponderData?.assignerTeam?.teams || [],
); // });
return;
}
dispatch({ // dispatch({
type: actionTypes.SET_SEVERITY, // type: actionTypes.SET_RESPONDERS,
payload: filterConfigData[1]?.filter_data || [], // payload: assignerResponderData?.responderTeam?.teams || [],
}); // });
const productListResponse = await ApiService.get(PRODUCT_LIST); // dispatch({
const productListData = productListResponse?.data; // type: actionTypes.SET_SELECTED_ASSIGNER,
// payload: assignerResponderData?.assignerTeam?.defaultTeam?.id ?? '',
// });
dispatch({ // dispatch({
type: actionTypes.SET_TEAMS, // type: actionTypes.SET_SELECTED_RESPONDER,
payload: productListData?.products || [], // payload: assignerResponderData?.responderTeam?.defaultTeam?.id ?? '',
}); // });
// });
// };
dispatch({ // useEffect(() => {
type: actionTypes.SET_SELECTED_TEAM, // appDispatch(fetchFilterConfigData())
payload: [productListData?.defaultProduct] || [], // .unwrap()
}); // .then(filterConfigResponse => {
// const filterConfigData = filterConfigResponse?.data;
// if (!filterConfigData) {
// toast.error(
// 'Something went wrong with filter config. Please try again later',
// );
// return;
// }
// dispatch({
// type: actionTypes.SET_SEVERITY,
// payload: filterConfigData[1]?.filter_data || [],
// });
// });
await fetchAssignerAndResponder([ // appDispatch(fetchProductListData())
productListData?.defaultProduct?.product_id, // .unwrap()
]); // .then(productListResponse => {
} catch (error) { // const productListData = productListResponse?.data;
toast.error('Something went wrong. Please try again later'); // dispatch({
console.error(error); // type: actionTypes.SET_TEAMS,
} // payload: productListData?.products || [],
// });
// dispatch({
// type: actionTypes.SET_SELECTED_TEAM,
// payload: [productListData?.defaultProduct] || [],
// });
// fetchAssignerAndResponder([
// productListData?.defaultProduct?.product_id,
// ]);
// });
// }, []);
const fetchAssignerAndResponderData = async (product: number[]) => {
const assignerResponderData = await fetchAssignerAndResponder(product);
updateAssignerAndResponder(assignerResponderData, dispatch);
}; };
const fetchData = async () => {
const fetchAssignerAndResponder = async (product: number[]) => { setIsLoading(true);
try { const filterConfigData = await fetchFilterConfig();
const assignerResponderResponse = await ApiService.get( const productListData = await fetchProductList();
FETCH_ASSIGNER_AND_RESPONDER(product), updateFilterConfig(filterConfigData, dispatch);
); updateProductList(productListData, dispatch);
const assignerResponderData = assignerResponderResponse?.data; fetchAssignerAndResponderData([
productListData?.defaultProduct?.product_id,
dispatch({ ]);
type: actionTypes.SET_ASSIGNERS, setIsLoading(false);
payload: assignerResponderData?.assignerTeam?.teams || [],
});
dispatch({
type: actionTypes.SET_RESPONDERS,
payload: assignerResponderData?.responderTeam?.teams || [],
});
dispatch({
type: actionTypes.SET_SELECTED_ASSIGNER,
payload: assignerResponderData?.assignerTeam?.defaultTeam?.id ?? '',
});
dispatch({
type: actionTypes.SET_SELECTED_RESPONDER,
payload: assignerResponderData?.responderTeam?.defaultTeam?.id ?? '',
});
} catch (error) {
toast.error(
'Something went wrong with assigner and responder data. Please try again later',
);
console.error(error);
}
}; };
useEffect(() => { useEffect(() => {
console.log('called');
fetchData(); fetchData();
console.log('fetchData');
}, []); }, []);
useEffect(() => { //TODO change contract to give label and value
if (state.selectedTeam?.product_id) { const getLabelValueProducts = (state: Product[]) => {
console.log(state.selectedTeam);
fetchAssignerAndResponder(state.selectedTeam.product_id);
}
}, [state.selectedTeam]);
const getLabelValueOptions = (state: Team[]) => {
return state?.map(team => ({ return state?.map(team => ({
label: team?.product_name, label: team?.product_name,
value: team?.product_id, value: team?.product_id,
})); }));
}; };
const getLabelValueTeams = state => {
return state?.map(team => ({
label: team?.name,
value: team?.id,
}));
};
const clearDrawer = () => { const clearDrawer = () => {
dispatch({ type: actionTypes.CLEAR_DRAWER }); dispatch({ type: actionTypes.CLEAR_DRAWER });
handleDrawerClose(); handleDrawerClose();
@@ -271,7 +287,7 @@ const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
<div className={styles['Team-wrapper']}> <div className={styles['Team-wrapper']}>
<div className={styles['name-wrapper']}>Product</div> <div className={styles['name-wrapper']}>Product</div>
<ProductSelector <ProductSelector
options={getLabelValueOptions(state.teams)} options={getLabelValueProducts(state.teams)}
handleSelectionChange={handleTeamChange} handleSelectionChange={handleTeamChange}
selected={state.selectedTeam} selected={state.selectedTeam}
/> />
@@ -279,7 +295,7 @@ const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
<div className={styles['Team-wrapper']}> <div className={styles['Team-wrapper']}>
<div className={styles['name-wrapper']}>Assigner</div> <div className={styles['name-wrapper']}>Assigner</div>
<IncidentSingleSelector <IncidentSingleSelector
options={getLabelValueOptions(state.assigner)} options={getLabelValueTeams(state.assigners)}
handleSelectionChange={handleAssignerChange} handleSelectionChange={handleAssignerChange}
selected={state.assigner} selected={state.assigner}
/> />
@@ -287,7 +303,7 @@ const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
<div className={styles['Team-wrapper']}> <div className={styles['Team-wrapper']}>
<div className={styles['name-wrapper']}>Responder</div> <div className={styles['name-wrapper']}>Responder</div>
<IncidentSingleSelector <IncidentSingleSelector
options={getLabelValueOptions(state.responder)} options={getLabelValueTeams(state.responders)}
handleSelectionChange={handleResponderChange} handleSelectionChange={handleResponderChange}
selected={state.responder} selected={state.responder}
/> />

View File

@@ -0,0 +1,57 @@
import { actionTypes } from '../constants';
export const updateFilterConfig = (
filterConfigData: any,
dispatch: React.Dispatch<any>,
) => {
if (filterConfigData) {
dispatch({
type: actionTypes.SET_SEVERITY,
payload: filterConfigData[1]?.filter_data || [],
});
}
};
export const updateProductList = (
productListData: any,
dispatch: React.Dispatch<any>,
) => {
if (productListData) {
dispatch({
type: actionTypes.SET_TEAMS,
payload: productListData?.products || [],
});
dispatch({
type: actionTypes.SET_SELECTED_TEAM,
payload: [productListData?.defaultProduct] || [],
});
}
};
export const updateAssignerAndResponder = (
assignerResponderData: any,
dispatch: React.Dispatch<any>,
) => {
if (assignerResponderData) {
dispatch({
type: actionTypes.SET_ASSIGNERS,
payload: assignerResponderData?.assignerTeam?.teams,
});
dispatch({
type: actionTypes.SET_RESPONDERS,
payload: assignerResponderData?.responderTeam?.teams,
});
dispatch({
type: actionTypes.SET_SELECTED_ASSIGNER,
payload: assignerResponderData?.assignerTeam?.defaultTeam?.id ?? '',
});
dispatch({
type: actionTypes.SET_SELECTED_RESPONDER,
payload: assignerResponderData?.responderTeam?.defaultTeam?.id ?? '',
});
}
};

View File

@@ -3,6 +3,7 @@ import {
SelectPickerOptionProps, SelectPickerOptionProps,
SelectPickerValue, SelectPickerValue,
} from '@navi/web-ui/lib/components/SelectPicker/types'; } from '@navi/web-ui/lib/components/SelectPicker/types';
import styles from './CreateIncident.module.scss';
interface IncidentSingleSelectorProps { interface IncidentSingleSelectorProps {
options: SelectPickerOptionProps[]; options: SelectPickerOptionProps[];
@@ -22,6 +23,7 @@ const IncidentSingleSelector = (props: IncidentSingleSelectorProps) => {
updateClearAllCallback={handleClearCallback} updateClearAllCallback={handleClearCallback}
variant="bordered" variant="bordered"
placeholder="Select Team" placeholder="Select Team"
containerClassName={styles['create-incident-single-selector']}
/> />
); );
}; };

View File

@@ -8,10 +8,20 @@ export interface filterItem {
filter_data: Team[] | null; filter_data: Team[] | null;
selection_config: SelectionType; selection_config: SelectionType;
} }
export interface Team { export interface FilterData{
product_id: string; label: string;
value: string;
}
export interface Product{
product_id: number;
product_name: string; product_name: string;
} }
export interface Team {
label: string;
value: string;
}
// export
export interface FilterResult { export interface FilterResult {
severity_ids: SelectPickerOptionProps[]; severity_ids: SelectPickerOptionProps[];
team_ids: SelectPickerOptionProps[]; team_ids: SelectPickerOptionProps[];

View File

@@ -1,5 +1,7 @@
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { AppDispatch } from '@src/store'; import { AppDispatch } from '@src/store';
import { toast } from '@navi/web-ui/lib/primitives/Toast';
import { import {
fetchAssignerAndResponderData, fetchAssignerAndResponderData,
fetchFilterConfigData, fetchFilterConfigData,
@@ -9,25 +11,48 @@ import {
const useIncidentFormApi = () => { const useIncidentFormApi = () => {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const fetchFilterConfig = () => { const fetchFilterConfig = async () => {
dispatch(fetchFilterConfigData()); try {
const response = await dispatch(fetchFilterConfigData()).unwrap();
return response?.data;
} catch (error) {
toast.error(
'Something went wrong with filter config. Please try again later',
);
console.error(error);
return null;
}
}; };
const fetchProductList = () => { const fetchProductList = async () => {
dispatch(fetchProductListData()); try {
const response = await dispatch(fetchProductListData()).unwrap();
return response?.data;
} catch (error) {
toast.error(
'Something went wrong with fetching the product list. Please try again later',
);
console.error(error);
return null;
}
}; };
const fetchAssignerAndResponder = async (productList: any) => { const fetchAssignerAndResponder = async (product: number[]) => {
dispatch(fetchAssignerAndResponderData(productList)); try {
}; const response = await dispatch(
fetchAssignerAndResponderData(product),
const fetchData = async () => { ).unwrap();
fetchFilterConfig(); return response?.data;
fetchProductList(); } catch (error) {
toast.error(
'Something went wrong with assigner and responder data. Please try again later',
);
console.error(error);
return null;
}
}; };
return { return {
fetchData,
fetchFilterConfig, fetchFilterConfig,
fetchProductList, fetchProductList,
fetchAssignerAndResponder, fetchAssignerAndResponder,

View File

@@ -2,23 +2,70 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { toast } from '@navi/web-ui/lib/primitives/Toast'; import { toast } from '@navi/web-ui/lib/primitives/Toast';
import { ApiService } from '@src/services/api'; import { ApiService } from '@src/services/api';
import { RootState } from '@src/store'; import { RootState } from '@src/store';
import {
filterItem,
FilterData,
Team,
Product,
} from '@src/Pages/Dashboard/type';
import { import {
FETCH_ASSIGNER_AND_RESPONDER, FETCH_ASSIGNER_AND_RESPONDER,
FETCH_FILTER_CONFIG, FETCH_FILTER_CONFIG,
PRODUCT_LIST, PRODUCT_LIST,
} from '@src/Pages/Dashboard/constants'; } from '@src/Pages/Dashboard/constants';
interface FilterListResponse {
data: filterItem[];
}
// Interface for product item
// Interface for product list response
interface ProductListResponse {
data: {
defaultProduct: Product;
products: Product[];
};
}
// Interface for team item
interface TeamItem {
id: number;
name: string;
}
// Interface for assigner and responder response
interface Assigner {
defaultTeam: TeamItem;
teams: TeamItem[];
}
interface Responder {
defaultTeam: TeamItem;
teams: TeamItem[];
}
interface AssignerResponderResponse {
data: {
assignerTeam: Assigner;
responderTeam: Responder;
};
}
interface InitialState {
filterConfigData: FilterListResponse;
productListData: ProductListResponse;
assignerResponderData: AssignerResponderResponse;
error: string;
isFetching: boolean;
}
const initialState = { const initialState = {
incidentForm: { filterConfigData: {} as FilterListResponse,
filterConfigData: {}, productListData: {} as ProductListResponse,
productListData: {}, assignerResponderData: {} as AssignerResponderResponse,
assignerResponderData: {}, error: '',
error: '', isFetching: false,
isFetching: false,
},
}; };
// Define async thunk actions
export const fetchFilterConfigData = createAsyncThunk( export const fetchFilterConfigData = createAsyncThunk(
'incidentForm/fetchFilterConfigData', 'incidentForm/fetchFilterConfigData',
async () => { async () => {
@@ -40,56 +87,49 @@ export const fetchAssignerAndResponderData = createAsyncThunk(
}, },
); );
// Define slice
const incidentFormSlice = createSlice({ const incidentFormSlice = createSlice({
name: 'incidentForm', name: 'incidentForm',
initialState, initialState,
reducers: { reducers: {},
// Additional reducers can be defined here
},
extraReducers: builder => { extraReducers: builder => {
builder builder
.addCase(fetchFilterConfigData.pending, state => { .addCase(fetchFilterConfigData.pending, state => {
state.incidentForm.isFetching = true; state.isFetching = true;
state.incidentForm.error = ''; state.error = '';
}) })
.addCase(fetchFilterConfigData.fulfilled, (state, action) => { .addCase(fetchFilterConfigData.fulfilled, (state, action) => {
state.incidentForm.isFetching = false; state.isFetching = false;
state.incidentForm.filterConfigData = action.payload; state.filterConfigData = action.payload;
}) })
.addCase(fetchFilterConfigData.rejected, (state, action) => { .addCase(fetchFilterConfigData.rejected, (state, action) => {
state.incidentForm.isFetching = false; state.isFetching = false;
state.incidentForm.error = state.error = action.error.message ?? 'Something went wrong';
action.error.message ?? 'Something went wrong';
}) })
.addCase(fetchProductListData.pending, state => { .addCase(fetchProductListData.pending, state => {
state.incidentForm.isFetching = true; state.isFetching = true;
state.incidentForm.error = ''; state.error = '';
}) })
.addCase(fetchProductListData.fulfilled, (state, action) => { .addCase(fetchProductListData.fulfilled, (state, action) => {
state.incidentForm.isFetching = false; state.isFetching = false;
state.incidentForm.productListData = action.payload; state.productListData = action.payload;
}) })
.addCase(fetchProductListData.rejected, (state, action) => { .addCase(fetchProductListData.rejected, (state, action) => {
state.incidentForm.isFetching = false; state.isFetching = false;
state.incidentForm.error = state.error = action.error.message ?? 'Something went wrong';
action.error.message ?? 'Something went wrong';
}) })
.addCase(fetchAssignerAndResponderData.pending, state => { .addCase(fetchAssignerAndResponderData.pending, state => {
state.incidentForm.isFetching = true; state.isFetching = true;
state.incidentForm.error = ''; state.error = '';
}) })
.addCase(fetchAssignerAndResponderData.fulfilled, (state, action) => { .addCase(fetchAssignerAndResponderData.fulfilled, (state, action) => {
state.incidentForm.isFetching = false; state.isFetching = false;
state.incidentForm.assignerResponderData = action.payload; state.assignerResponderData = action.payload;
}) })
.addCase(fetchAssignerAndResponderData.rejected, (state, action) => { .addCase(fetchAssignerAndResponderData.rejected, (state, action) => {
state.incidentForm.isFetching = false; state.isFetching = false;
state.incidentForm.error = state.error = action.error.message ?? 'Something went wrong';
action.error.message ?? 'Something went wrong';
}); });
}, },
}); });
// Export reducer
export default incidentFormSlice.reducer; export default incidentFormSlice.reducer;