From 0f25379abe0eebebb0e9961d2a97961c5010fb5b Mon Sep 17 00:00:00 2001 From: Aman Chaturvedi Date: Thu, 22 Dec 2022 10:40:01 +0530 Subject: [PATCH] todo list changes --- src/action/dataActions.ts | 62 ++++++++---- src/components/floatingInfoText/index.tsx | 49 ++++++++++ src/components/utlis/apiHelper.ts | 5 +- src/components/utlis/navigationUtlis.ts | 3 + src/reducer/allCasesSlice.ts | 63 +++++++----- src/screens/allCases/AllCases.tsx | 65 ++++--------- src/screens/allCases/CasesActionButtons.tsx | 101 ++++++++++++++++++++ src/screens/allCases/ListItem.tsx | 43 ++++++--- src/screens/allCases/index.tsx | 54 +++++++++-- src/screens/allCases/interface.ts | 2 +- src/screens/caseDetails/CaseDetails.tsx | 3 +- src/screens/todoList/TodoList.tsx | 27 +++++- 12 files changed, 362 insertions(+), 115 deletions(-) create mode 100644 src/components/floatingInfoText/index.tsx create mode 100644 src/screens/allCases/CasesActionButtons.tsx diff --git a/src/action/dataActions.ts b/src/action/dataActions.ts index e5f8e2bb..5e311aef 100644 --- a/src/action/dataActions.ts +++ b/src/action/dataActions.ts @@ -1,24 +1,52 @@ -import {Dispatch} from 'redux'; import axiosInstance, {ApiKeys, getApiUrl} from '../components/utlis/apiHelper'; -import {setCasesListData} from '../reducer/allCasesSlice'; +import { + resetTodoList, + setCasesListData, + setLoading, + setTodoListOffline, +} from '../reducer/allCasesSlice'; import {ICaseItem} from '../screens/allCases/interface'; +import {AppDispatch} from '../store/store'; -export const getAllCases = () => (dispatch: Dispatch) => { +export const getAllCases = () => (dispatch: AppDispatch) => { const url = getApiUrl(ApiKeys.ALL_CASES); - axiosInstance.get(url).then(async response => { - const caseDetails = await getAllCaseDetails(response.data) - dispatch(setCasesListData({allCases : response.data, details: caseDetails })); + dispatch(setLoading(true)); + axiosInstance.get(url).then(response => { + dispatch(getAllCaseDetails(response.data)); }); }; -export const getAllCaseDetails = async (data: Array) => { - const caseList = data.map(caseItem => caseItem.caseReferenceId); - const url = getApiUrl(ApiKeys.CASE_DETAIL); - try { - const result = await axiosInstance.get(url, {params: {caseIds: caseList.join(',')}}) - console.log(result) - return await result.data; - } catch (error) { - return {}; - } -}; +export const getAllCaseDetails = + (data: Array) => (dispatch: AppDispatch) => { + const caseList = data.map(caseItem => caseItem.caseReferenceId); + const url = getApiUrl(ApiKeys.CASE_DETAIL); + axiosInstance + .get(url, { + params: {caseIds: caseList.join(',')}, + }) + .then(response => { + dispatch( + setCasesListData({allCases: data, details: response.data}), + ); + dispatch(resetTodoList()); + }) + .catch(err => console.log(err)); + }; + +export const postPinnedList = + (pinnedCases: ICaseItem[], updatedCaseList: ICaseItem[]) => + (dispatch: AppDispatch) => { + dispatch(setLoading(true)); + const payload = pinnedCases + .map(caseItem => caseItem.caseReferenceId) + .join('&pins='); + const url = getApiUrl(ApiKeys.PINNED_CASES); + axiosInstance + .post(url + `?pins=${payload}`) + .then(response => { + dispatch(getAllCases()); + }) + .catch(err => { + dispatch(setTodoListOffline(updatedCaseList)); + }); + }; diff --git a/src/components/floatingInfoText/index.tsx b/src/components/floatingInfoText/index.tsx new file mode 100644 index 00000000..61b62a2b --- /dev/null +++ b/src/components/floatingInfoText/index.tsx @@ -0,0 +1,49 @@ +import {View, StyleSheet} from 'react-native'; +import React from 'react'; +import {GenericStyles, getShadowStyle} from '../../../RN-UI-LIB/src/styles'; +import Text from '../../../RN-UI-LIB/src/components/Text'; +import {COLORS} from '../../../RN-UI-LIB/src/styles/colors'; +import InfoIcon from '../../../RN-UI-LIB/src/Icons/InfoIcon'; + +interface IFloatingInfoText { + top?: number; + bottom?: number; + message: string; +} + +const FloatingInfoText: React.FC = ({message, top, bottom}) => { + return ( + + + + + {message} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + width: '100%', + }, + textContainer: { + paddingHorizontal: 30, + paddingVertical: 3, + borderRadius: 4, + backgroundColor: COLORS.BACKGROUND.BLUE, + }, + text: { + color: COLORS.BASE.BLUE, + marginLeft: 10 + }, +}); + +export default FloatingInfoText; \ No newline at end of file diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 3f0050b9..f1b8ef22 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -17,7 +17,8 @@ export enum ApiKeys { GENERATE_OTP, VERIFY_OTP, ALL_CASES, - CASE_DETAIL + CASE_DETAIL, + PINNED_CASES } const API_URLS: Record = {} as Record; @@ -25,6 +26,7 @@ API_URLS[ApiKeys.GENERATE_OTP] = '/auth/otp/generate'; API_URLS[ApiKeys.VERIFY_OTP] = '/auth/otp/verify'; API_URLS[ApiKeys.ALL_CASES] = '/cases/all-cases'; API_URLS[ApiKeys.CASE_DETAIL] = '/cases/get-cases'; +API_URLS[ApiKeys.PINNED_CASES] = '/cases/pin'; const MOCK_API_URLS: Record = {} as Record; @@ -74,7 +76,6 @@ const errorsToRetry = []; const axiosInstance = axios.create(); axiosInstance.interceptors.request.use(request => { - console.log(request) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.retry = request?.retry < 4 ? request.retry : 3; diff --git a/src/components/utlis/navigationUtlis.ts b/src/components/utlis/navigationUtlis.ts index c4553614..ae8b5fed 100644 --- a/src/components/utlis/navigationUtlis.ts +++ b/src/components/utlis/navigationUtlis.ts @@ -5,6 +5,9 @@ export const navigationRef: RefObject = React.createRef(); // if screen already exists then navigate to it otherwise push screen export const navigateToScreen = (name: string, params: object = {}) => { + if(navigationRef.current?.getCurrentRoute()?.name === name) { + return; + } navigationRef.current?.navigate(name, params); } diff --git a/src/reducer/allCasesSlice.ts b/src/reducer/allCasesSlice.ts index f0982a42..53562373 100644 --- a/src/reducer/allCasesSlice.ts +++ b/src/reducer/allCasesSlice.ts @@ -2,9 +2,7 @@ import {_map} from '../../RN-UI-LIB/src/utlis/common'; import {Search} from '../../RN-UI-LIB/src/utlis/search'; import {createSlice} from '@reduxjs/toolkit'; import {navigateToScreen} from '../components/utlis/navigationUtlis'; -import { - ICaseItem, -} from '../screens/allCases/interface'; +import {ICaseItem} from '../screens/allCases/interface'; export type ICasesMap = {[key: string]: ICaseItem}; @@ -14,6 +12,8 @@ interface IAllCasesSlice { casesListMap: ICasesMap; intermediateTodoList: ICaseItem[]; intermediateTodoListMap: ICasesMap; + selectedTodoListMap: ICasesMap; + selectedTodoListCount: number; initialPinnedRankCount: number; pinnedRankCount: number; loading: boolean; @@ -29,6 +29,8 @@ const initialState: IAllCasesSlice = { casesListMap: {}, intermediateTodoList: [], intermediateTodoListMap: {}, + selectedTodoListCount: 0, + selectedTodoListMap: {}, initialPinnedRankCount: 0, pinnedRankCount: 0, loading: false, @@ -42,13 +44,13 @@ const getPinnedListDetails = (casesList: ICaseItem[]) => { let maxPinnedRank = 0; const pinnedList: ICaseItem[] = []; casesList.forEach(caseItem => { - const {pinnedRank} = caseItem; - if (!pinnedRank) { + const {pinRank} = caseItem; + if (pinRank === null || pinRank === undefined) { return; } pinnedList.push(caseItem); - if (pinnedRank > maxPinnedRank) { - maxPinnedRank = pinnedRank; + if (pinRank > maxPinnedRank) { + maxPinnedRank = pinRank; } }); return {pinnedList, maxPinnedRank}; @@ -58,6 +60,9 @@ const allCasesSlice = createSlice({ name: 'cases', initialState, reducers: { + setLoading: (state, action) => { + state.loading = action.payload; + }, setCasesListData: (state, action) => { const {allCases, details} = action.payload; if (details?.length) { @@ -72,6 +77,8 @@ const allCasesSlice = createSlice({ const {pinnedList, maxPinnedRank} = getPinnedListDetails(allCases); state.pinnedList = pinnedList; state.pinnedRankCount = maxPinnedRank; + state.loading = false; + navigateToScreen('Home') }, setPinnedRank: (state, action) => { const caseId = action.payload.caseReferenceId; @@ -83,14 +90,14 @@ const allCasesSlice = createSlice({ state.pinnedRankCount++; state.newlyPinnedCases++; const selectedCase = {...action.payload}; - selectedCase.pinnedRank = state.pinnedRankCount; + selectedCase.pinRank = state.pinnedRankCount; state.intermediateTodoListMap[caseId] = selectedCase; } }, proceedToTodoList: state => { state.intermediateTodoList = Object.values( state.intermediateTodoListMap, - ).sort((caseA, caseB) => caseA.pinnedRank - caseB.pinnedRank); + ).sort((caseA, caseB) => caseA.pinRank - caseB.pinRank); navigateToScreen('TodoList'); }, deleteIntermediateTodoListItem: (state, action) => { @@ -116,34 +123,42 @@ const allCasesSlice = createSlice({ state.newlyPinnedCases = 0; state.pinnedRankCount = state.initialPinnedRankCount; }, - setTodoList: state => { - state.newlyPinnedCases = 0; - const list = state.casesList.map(caseItem => { - const pinnedItem = - state.intermediateTodoListMap[caseItem.caseReferenceId]; - return { - ...caseItem, - pinnedRank: pinnedItem - ? pinnedItem.pinnedRank - : caseItem.pinnedRank, - }; - }); - state.casesList = list; + setTodoListOffline: (state, action) => { + state.casesList = action.payload; state.intermediateTodoListMap = {}; state.newlyPinnedCases = 0; navigateToScreen('Home'); }, + setSelectedTodoListMap: (state, action) => { + const caseId = action.payload.caseReferenceId; + const isCasePresent = state.selectedTodoListMap[caseId]; + if (isCasePresent) { + delete state.selectedTodoListMap[caseId]; + state.selectedTodoListCount--; + } else { + state.selectedTodoListCount++; + const selectedCase = {...action.payload}; + state.selectedTodoListMap[caseId] = selectedCase; + } + }, + resetSelectedTodoList: (state) => { + state.selectedTodoListCount = 0; + state.selectedTodoListMap = {}; + } }, }); export const { + setLoading, setCasesListData, setPinnedRank, - setTodoList, + setTodoListOffline, resetTodoList, filterData, proceedToTodoList, - deleteIntermediateTodoListItem + deleteIntermediateTodoListItem, + setSelectedTodoListMap, + resetSelectedTodoList } = allCasesSlice.actions; export default allCasesSlice.reducer; diff --git a/src/screens/allCases/AllCases.tsx b/src/screens/allCases/AllCases.tsx index 9ee3bb07..7ff726c5 100644 --- a/src/screens/allCases/AllCases.tsx +++ b/src/screens/allCases/AllCases.tsx @@ -1,27 +1,24 @@ import React, {useEffect, useMemo, useState} from 'react'; import {StyleSheet, View, VirtualizedList} from 'react-native'; -import {useDispatch, useSelector} from 'react-redux'; +import {useSelector} from 'react-redux'; import Text from '../../../RN-UI-LIB/src/components/Text'; import {GenericStyles} from '../../../RN-UI-LIB/src/styles'; import {COLORS} from '../../../RN-UI-LIB/src/styles/colors'; - -import Button from '../../../RN-UI-LIB/src/components/Button'; -import {navigateToScreen} from '../../components/utlis/navigationUtlis'; -import {proceedToTodoList, resetTodoList} from '../../reducer/allCasesSlice'; import {RootState} from '../../store/store'; import {CaseStatuses, ICaseItem, CaseTypes} from './interface'; import {COMPLETED_STATUSES, ListHeaderItems} from './contants'; import CaseItem from './CaseItem'; +import CasesActionButtons from './CasesActionButtons'; +import FloatingInfoText from '../../components/floatingInfoText'; export const Separator = () => ; export const getItem = (item: Array, index: number) => item[index]; const AllCases = () => { - const {casesList, newlyPinnedCases} = useSelector( + const {casesList, newlyPinnedCases, selectedTodoListCount} = useSelector( (state: RootState) => state.allCases, ); - const dispatch = useDispatch(); const pendingCases = useMemo( () => @@ -54,10 +51,13 @@ const AllCases = () => { //extracting data for todo list and other cases filteredList.forEach(caseItem => { - if (caseItem.pinnedRank) { - pinnedList.push({...caseItem, type: CaseTypes.TODO}); - } else { + if ( + caseItem.pinRank === null || + caseItem.pinRank === undefined + ) { otherList.push({...caseItem, type: CaseTypes.CASE}); + } else { + pinnedList.push({...caseItem, type: CaseTypes.TODO}); } }); @@ -73,11 +73,6 @@ const AllCases = () => { compiledList.push(...otherList); } - pinnedList.sort( - (caseItemA, caseItemB) => - caseItemA.pinnedRank - caseItemB.pinnedRank, - ); - let completedPinnedCasesCount = 0; pinnedList.forEach(todo => { if (todo.caseStatus === CaseStatuses.CLOSED) { @@ -95,14 +90,6 @@ const AllCases = () => { }; }, [filteredList]); - const handleTodoProceedClick = () => { - dispatch(proceedToTodoList()); - }; - - const handleCancelTodoList = () => { - dispatch(resetTodoList()); - }; - const handleSearchChange = (query: string) => { const filterList = pendingCases.filter(caseItem => caseItem); }; @@ -132,27 +119,14 @@ const AllCases = () => { ) : ( Nothing to show )} - {newlyPinnedCases ? ( - -