This commit is contained in:
Aman Chaturvedi
2022-12-13 16:12:12 +05:30
parent c70e3552c9
commit 8a8e1e980f
7 changed files with 400 additions and 51 deletions

View File

@@ -0,0 +1,109 @@
import React, {useState} from 'react';
import {
View,
Text,
StyleSheet,
Animated,
Dimensions,
PanResponder,
} from 'react-native';
const {width} = Dimensions.get('window');
const gestureDelay = -35;
const SwipeableContainer = (props: any) => {
const [position, setPosition] = useState(new Animated.ValueXY());
let scrollViewEnabled = true;
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => false,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderTerminationRequest: (evt, gestureState) => false,
onPanResponderMove: (evt, gestureState) => {
if (gestureState.dx < 0) {
setScrollViewEnabled(false);
let newX = gestureState.dx + gestureDelay;
position.setValue({x: newX, y: 0});
}
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dx > -50) {
Animated.timing(position, {
toValue: {x: 0, y: 0},
duration: 150,
useNativeDriver: false,
}).start(() => {
setScrollViewEnabled(true);
});
} else {
Animated.timing(position, {
toValue: {x: -100, y: 0},
duration: 300,
useNativeDriver: false,
}).start(() => {
// props.success(props.text);
setScrollViewEnabled(true);
});
}
},
});
// this.panResponder = panResponder;
// this.state = {position};
const setScrollViewEnabled = (enabled: boolean) => {
if (scrollViewEnabled !== enabled) {
// props.setScrollEnabled(enabled);
scrollViewEnabled = enabled;
}
};
return (
<View style={styles.listItem}>
<Animated.View
style={[position.getLayout()]}
{...panResponder.panHandlers}>
<View style={styles.innerCell}>
{props.children}
</View>
<View style={styles.absoluteCell}>
<Text style={styles.absoluteCellText}>DELETE</Text>
</View>
</Animated.View>
</View>
);
};
const styles = StyleSheet.create({
listItem: {
height: 80,
marginRight: -100,
justifyContent: 'center',
backgroundColor: 'red',
position: 'relative'
},
absoluteCell: {
position: 'absolute',
// top: 0,
// bottom: 0,
right: 0,
width: 100,
flexDirection: 'row',
// justifyContent: 'flex-end',
alignItems: 'center',
},
absoluteCellText: {
margin: 16,
color: '#FFF',
},
innerCell: {
// width: width + 100,
height: 80,
marginRight: 100,
backgroundColor: 'yellow',
// justifyContent: 'center',
// alignItems: 'center',
},
});
export default SwipeableContainer;

View File

@@ -1,49 +1,63 @@
import TextInput from '../../../../RN-UI-LIB/src/components/TextInput';
import React, {useEffect} from 'react';
import {
ListRenderItemInfo, StyleSheet,
ListRenderItemInfo,
StyleSheet,
View,
VirtualizedList
VirtualizedList,
} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import {useDispatch, useSelector} from 'react-redux';
import Heading from '../../../../RN-UI-LIB/src/components/Heading';
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 {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import {COLORS} from '../../../../RN-UI-LIB/src/styles/colors';
import data from '../../../data/cases.json';
import { RootState } from '../../../store/store';
import { setCasesListData } from './allCasesSlice';
import { caseStatus, caseVerdict, Data, taskStatus, Type } from './interface';
import {RootState} from '../../../store/store';
import {resetTodoList, setCasesListData, setCasesListMapData, setIntermediateTodoList} from './allCasesSlice';
import {caseStatus, Data, Type} from './interface';
import ListItem from './ListItem';
import SearchIcon from '../../../../RN-UI-LIB/src/Icons/SearchIcon';
import InfoIcon from '../../../../RN-UI-LIB/src/Icons/InfoIcon';
import Button from '../../../../RN-UI-LIB/src/components/Button';
import { navigateToScreen } from '../../utlis/navigationUtlis';
interface IItem {
data: ListRenderItemInfo<Data>;
todoList: Array<Data>;
}
const Item: React.FC<IItem> = (props) => {
const Item: React.FC<IItem> = props => {
const propData = props.data.item;
const todoList = props.todoList;
let completedCount = 0;
todoList.forEach(todo =>{
if(todo.caseStatus === caseStatus.CLOSED){
todoList.forEach(todo => {
if (todo.caseStatus === caseStatus.CLOSED) {
completedCount++;
}
});
switch (propData.type) {
case Type.FILTER:
return (
<View style={[GenericStyles.ph16, styles.pv12, GenericStyles.whiteBackground]}>
<TextInput LeftComponent={<SearchIcon />} placeholder='Search by name, address' />
<View
style={[
GenericStyles.ph16,
styles.pv12,
GenericStyles.whiteBackground,
]}>
<TextInput
LeftComponent={<SearchIcon />}
placeholder="Search by name, address"
/>
</View>
);
case Type.CASES_HEADER:
return (
<View style={[GenericStyles.ph16, styles.pv12, GenericStyles.whiteBackground]}>
<View
style={[
GenericStyles.ph16,
styles.pv12,
GenericStyles.whiteBackground,
]}>
<Heading dark bold type={'h5'}>
Other cases
</Heading>
@@ -51,7 +65,12 @@ const Item: React.FC<IItem> = (props) => {
);
case Type.TODO_HEADER:
return (
<View style={[GenericStyles.ph16, styles.pv12, GenericStyles.whiteBackground]}>
<View
style={[
GenericStyles.ph16,
styles.pv12,
GenericStyles.whiteBackground,
]}>
<Heading dark bold type={'h5'}>
Today's to do list
</Heading>
@@ -74,37 +93,65 @@ const Item: React.FC<IItem> = (props) => {
</View>
);
default:
return <ListItem caseData ={propData} />;
return <ListItem caseData={propData} />;
}
};
const Seperator = () => <View style={styles.seperator} />;
export const Seperator = () => <View style={styles.seperator} />;
const getItem = (item: Array<Data>, index: number) => item[index];
export const getItem = (item: Array<Data>, index: number) => item[index];
const AllCases = () => {
const {compiledList, todoList} = useSelector((state: RootState) => state.allCases);
const {casesList, compiledList, todoList, newlyPinnedCases} = useSelector(
(state: RootState) => state.allCases,
);
const dispatch = useDispatch();
useEffect(() => {
dispatch(setCasesListData(data.allCases));
dispatch(setCasesListData(data.allCases))
}, [])
useEffect(() => {
dispatch(setCasesListMapData(casesList));
}, [casesList]);
const handleTodoProceedClick = () => {
dispatch(setIntermediateTodoList());
}
const handleCancelTodoList = () => {
dispatch(resetTodoList());
}
return (
<View>
<View style={GenericStyles.fill}>
<VirtualizedList
data={compiledList as Array<Data>}
stickyHeaderIndices={[0]}
initialNumToRender={4}
renderItem={row => (
<Item data={row} todoList={todoList} />
)}
renderItem={row => <Item data={row} todoList={todoList} />}
keyExtractor={item => item.caseId}
getItemCount={item => item.length}
getItem={getItem}
ItemSeparatorComponent={<Seperator />}
/>
{newlyPinnedCases ? (
<View
style={[
GenericStyles.row,
GenericStyles.justifyContentSpaceBetween,
GenericStyles.p16,
styles.actionBtns,
]}>
<Button
variant={'secondary'}
style={styles.fb45}
title={'Cancel'}
onPress={handleCancelTodoList}
/>
<Button style={styles.fb45} title={'Proceed'} onPress={handleTodoProceedClick}/>
</View>
): null}
</View>
);
};
@@ -123,6 +170,17 @@ const styles = StyleSheet.create({
borderRadius: 2.5,
backgroundColor: COLORS.TEXT.LIGHT,
},
actionBtns: {
borderTopColor: COLORS.BORDER.PRIMARY,
backgroundColor: COLORS.BACKGROUND.PRIMARY,
borderTopWidth: 1,
},
br8: {
borderRadius: 8,
},
fb45: {
flexBasis: '45%',
},
});
export default AllCases;

View File

@@ -18,11 +18,12 @@ import RoundCheckIcon from '../../../icons/RoundCheckIcon';
interface IListItem {
caseData: Data;
isTodoItem?: boolean;
compleatedList?: boolean;
}
const ListItem: React.FC<IListItem> = props => {
const {caseData, compleatedList} = props;
const {caseData, compleatedList, isTodoItem} = props;
const {type, caseId} = caseData;
const dispatch = useDispatch();
@@ -30,19 +31,25 @@ const ListItem: React.FC<IListItem> = props => {
return null;
}
const {casesListMap} = useSelector((state: RootState) => state.allCases);
const {intermediateTodoListMap} = useSelector(
(state: RootState) => state.allCases,
);
const handleAvatarClick = () => {
if (!type) {
dispatch(setPinnedRank(caseId));
if (
!isTodoItem &&
type === Type.CASE &&
caseData.caseStatus !== caseStatus.CLOSED
) {
dispatch(setPinnedRank(caseData));
}
};
const handleCaseClick = () => {
alert('case clicked')
}
alert('case clicked');
};
const caseSelected = casesListMap?.[caseId].pinnedRank;
const caseSelected = !isTodoItem && intermediateTodoListMap?.[caseId];
return (
// Todo kunal to add the page switching logic

View File

@@ -1,6 +1,7 @@
import {_map} from '@components/utlis/common';
import {createSlice} from '@reduxjs/toolkit';
import {caseStatus, caseVerdict, Data, taskStatus, Type} from './interface';
import {navigateToScreen} from '../../utlis/navigationUtlis';
import {caseStatus, Data, Type} from './interface';
type ICasesMap = {[key: string]: Data};
@@ -9,9 +10,14 @@ interface IAllCasesSlice {
todoList: Data[];
otherCasesList: Data[];
compiledList: Data[];
casesListMap: ICasesMap | null;
casesListMap: ICasesMap;
intermediateTodoList: Data[];
intermediateTodoListMap: ICasesMap;
initialPinnedRankCount: number;
pinnedRankCount: number;
loading: boolean;
newlyPinnedCases: number;
completedCases: number;
}
const initialState: IAllCasesSlice = {
@@ -19,9 +25,14 @@ const initialState: IAllCasesSlice = {
todoList: [],
otherCasesList: [],
compiledList: [],
casesListMap: null,
casesListMap: {},
intermediateTodoList: [],
intermediateTodoListMap: {},
initialPinnedRankCount: 0,
pinnedRankCount: 0,
loading: false,
newlyPinnedCases: 0,
completedCases: 0,
};
const allCasesSlice = createSlice({
@@ -29,21 +40,38 @@ const allCasesSlice = createSlice({
initialState,
reducers: {
setCasesListData: (state, action) => {
state.casesList = action.payload;
},
setCasesListMapData: (state, action) => {
const todoList: Data[] = [];
const otherCasesList: Data[] = [];
let completedCasesCount = 0;
const list: ICasesMap = action.payload.reduce(
(acc: ICasesMap, caseItem: Data) => {
acc[caseItem.caseId] = caseItem;
const caseId = caseItem.caseId;
if (caseItem.caseStatus === caseStatus.CLOSED) {
completedCasesCount++;
}
acc[caseId] = caseItem;
// acc[caseItem.caseId] = caseItem;
if (caseItem.pinnedRank) {
todoList.push(caseItem);
const caseItemTypeTodo = {...caseItem, type: Type.TODO};
// caseItem.type = Type.TODO;
todoList.push(caseItemTypeTodo);
} else {
otherCasesList.push(caseItem);
const caseItemTypeCase = {...caseItem, type: Type.CASE};
// caseItem.type = Type.CASE;
otherCasesList.push(caseItemTypeCase);
}
return acc;
},
{},
);
state.initialPinnedRankCount = todoList.length;
state.pinnedRankCount = todoList.length;
state.completedCases = completedCasesCount;
if (todoList.length) {
todoList.sort((caseItemA, caseItemB) => caseItemA.pinnedRank - caseItemB.pinnedRank);
todoList.unshift({
type: Type.TODO_HEADER,
caseId: -2,
@@ -64,25 +92,60 @@ const allCasesSlice = createSlice({
...otherCasesList,
];
state.casesListMap = list;
state.casesList = action.payload;
// state.casesList = action.payload;
state.otherCasesList = action.payload;
},
setPinnedRank: (state, action) => {
if (!state.casesListMap) {
return;
}
const caseId = action.payload;
const pinnedRank = state.casesListMap[caseId].pinnedRank;
if (pinnedRank) {
delete state.casesListMap[caseId].pinnedRank;
const caseId = action.payload.caseId;
const isCasePresent = state.intermediateTodoListMap[caseId];
if (isCasePresent) {
delete state.intermediateTodoListMap[caseId];
state.newlyPinnedCases--;
} else {
state.pinnedRankCount++;
state.casesListMap[caseId].pinnedRank = state.pinnedRankCount;
state.newlyPinnedCases++;
const selectedCase = {...action.payload};
selectedCase.pinnedRank = state.pinnedRankCount;
state.intermediateTodoListMap[caseId] = selectedCase;
}
},
setIntermediateTodoList: state => {
const list = Object.values(state.intermediateTodoListMap);
state.intermediateTodoList = list;
navigateToScreen('OTP');
},
resetTodoList: state => {
state.intermediateTodoList = [];
state.intermediateTodoListMap = {};
state.newlyPinnedCases = 0;
state.pinnedRankCount = state.initialPinnedRankCount;
},
setTodoList: state => {
state.newlyPinnedCases = 0;
const list = state.casesList.map(caseItem => {
const todoItem =
state.intermediateTodoListMap[caseItem.caseId];
return {
...caseItem,
pinnedRank: todoItem ? todoItem.pinnedRank : caseItem.pinnedRank,
};
});
state.casesList = list;
state.intermediateTodoList = [];
state.intermediateTodoListMap = {};
state.newlyPinnedCases = 0;
navigateToScreen('Login');
},
},
});
export const {setCasesListData, setPinnedRank} = allCasesSlice.actions;
export const {
setCasesListData,
setCasesListMapData,
setPinnedRank,
setIntermediateTodoList,
setTodoList,
resetTodoList,
} = allCasesSlice.actions;
export default allCasesSlice.reducer;

View File

@@ -4,6 +4,8 @@ export enum Type {
TODO_HEADER,
CASES_HEADER,
FILTER,
TODO,
CASE
}
export enum caseVerdict {

View File

@@ -0,0 +1,110 @@
import {
View,
StyleSheet,
VirtualizedList,
ListRenderItemInfo,
TouchableOpacity,
} from 'react-native';
import React, {useMemo} from 'react';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import CloseIcon from '../../../../RN-UI-LIB/src/Icons/CloseIcon';
import {COLORS} from '../../../../RN-UI-LIB/src/styles/colors';
import Heading from '../../../../RN-UI-LIB/src/components/Heading';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import {useDispatch, useSelector} from 'react-redux';
import {RootState} from '../../../store/store';
import {Data} from '../allCases/interface';
import ListItem from '../allCases/ListItem';
import {getItem, Seperator} from '../allCases/AllCases';
import Button from '../../../../RN-UI-LIB/src/components/Button';
import {resetTodoList, setTodoList} from '../allCases/allCasesSlice';
import {navigateToScreen} from '../../utlis/navigationUtlis';
import SwipeableContainer from '../../SwipeableContainer';
const TodoList = () => {
const {intermediateTodoList} = useSelector(
(state: RootState) => state.allCases,
);
const dispatch = useDispatch();
const handleCreateTodoList = () => {
dispatch(setTodoList());
};
const handleCancelTodoList = () => {
dispatch(resetTodoList());
navigateToScreen('Login');
};
return (
<View style={[GenericStyles.fill, styles.todoListContainer]}>
<View style={[GenericStyles.p16, styles.header]}>
<TouchableOpacity
activeOpacity={0.7}
onPress={handleCancelTodoList}>
<CloseIcon color={COLORS.TEXT.LIGHT} />
</TouchableOpacity>
<Heading dark type="h3" style={styles.headingText}>
Sharing a message of your visit
</Heading>
<Text light>
The selected customer(s) will recieve a message about your
visit. Are you sure you want to proceed?
</Text>
</View>
<VirtualizedList
style={{height: 500}}
data={intermediateTodoList as Array<Data>}
initialNumToRender={4}
renderItem={(row: ListRenderItemInfo<Data>) => (
<SwipeableContainer>
<ListItem caseData={row.item} isTodoItem />
</SwipeableContainer>
)}
keyExtractor={item => item.caseId}
getItemCount={item => item.length}
getItem={getItem}
ItemSeparatorComponent={<Seperator />}
/>
<View
style={[
GenericStyles.row,
GenericStyles.justifyContentSpaceBetween,
GenericStyles.p16,
styles.actionBtns,
]}>
<Button
title={'Create to do list'}
onPress={handleCreateTodoList}
style={GenericStyles.w100}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
todoListContainer: {
backgroundColor: COLORS.BACKGROUND.PRIMARY,
},
header: {
borderBottomWidth: 1,
borderBottomColor: COLORS.BORDER.PRIMARY,
},
headingText: {
marginTop: 16,
marginBottom: 8,
},
separator: {
backgroundColor: COLORS.BORDER.PRIMARY,
height: 2,
},
actionBtns: {
borderTopColor: COLORS.BORDER.PRIMARY,
backgroundColor: COLORS.BACKGROUND.PRIMARY,
borderTopWidth: 1,
},
});
export default TodoList;

View File

@@ -28,7 +28,7 @@ const persistConfig = {
key: 'root',
version: 1,
storage: AsyncStorage,
whitelist: ['case', 'common'],
whitelist: ['case', 'common', 'allCases'],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);