parent
7e735e3e7d
commit
6360b9c2e3
|
|
@ -9,4 +9,4 @@ const ENV = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set to 'prod' when deploying
|
// Set to 'prod' when deploying
|
||||||
export default ENV.dev;
|
export default ENV.prod;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ const AttendanceScreen = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Periodic Location Tracking when Checked In
|
// Periodic Location Tracking when Checked In
|
||||||
/*
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let interval;
|
let interval;
|
||||||
if (currentSession && !currentSession.checkOutTime) {
|
if (currentSession && !currentSession.checkOutTime) {
|
||||||
|
|
@ -72,12 +71,13 @@ const AttendanceScreen = () => {
|
||||||
lat: coords.latitude,
|
lat: coords.latitude,
|
||||||
lng: coords.longitude
|
lng: coords.longitude
|
||||||
});
|
});
|
||||||
|
console.log("Periodic route location sent: ", coords.latitude, coords.longitude);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Periodic tracking failed", error);
|
console.error("Periodic tracking failed", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
sendLocation(); // Initial
|
sendLocation(); // Initial call
|
||||||
interval = setInterval(sendLocation, 5 * 60 * 1000); // 5 mins
|
interval = setInterval(sendLocation, 60 * 1000); // Poll location every 60 seconds (1 minute) to map detailed route map
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (interval) {
|
if (interval) {
|
||||||
|
|
@ -86,7 +86,6 @@ const AttendanceScreen = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [currentSession]);
|
}, [currentSession]);
|
||||||
*/
|
|
||||||
|
|
||||||
// Request Location Permission
|
// Request Location Permission
|
||||||
const requestLocationPermission = async () => {
|
const requestLocationPermission = async () => {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -94,10 +94,15 @@ const TasksScreen = ({ navigation }) => {
|
||||||
const [activeCategory, setActiveCategory] = useState('today');
|
const [activeCategory, setActiveCategory] = useState('today');
|
||||||
const [inlineRemarks, setInlineRemarks] = useState({});
|
const [inlineRemarks, setInlineRemarks] = useState({});
|
||||||
const [submitting, setSubmitting] = useState(null); // holds activity id being submitted
|
const [submitting, setSubmitting] = useState(null); // holds activity id being submitted
|
||||||
|
const [selectedTypeFilter, setSelectedTypeFilter] = useState(null);
|
||||||
|
|
||||||
const fetchTasks = async () => {
|
const fetchTasks = async () => {
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams({ userId: userInfo.id, status: 'PENDING' });
|
const queryObj = { status: 'PENDING' };
|
||||||
|
if (userInfo && userInfo.role !== 'ADMIN') {
|
||||||
|
queryObj.userId = userInfo.id;
|
||||||
|
}
|
||||||
|
const params = new URLSearchParams(queryObj);
|
||||||
const res = await api.get(`/followups?${params.toString()}`);
|
const res = await api.get(`/followups?${params.toString()}`);
|
||||||
setAllActivities(res.data);
|
setAllActivities(res.data);
|
||||||
setCategorized(categorizeActivities(res.data));
|
setCategorized(categorizeActivities(res.data));
|
||||||
|
|
@ -143,7 +148,11 @@ const TasksScreen = ({ navigation }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentCat = CATEGORIES.find(c => c.key === activeCategory);
|
const currentCat = CATEGORIES.find(c => c.key === activeCategory);
|
||||||
const currentItems = categorized[activeCategory] || [];
|
const currentCategoryTotalCount = (categorized[activeCategory] || []).length;
|
||||||
|
const currentItems = (categorized[activeCategory] || []).filter(item => {
|
||||||
|
if (!selectedTypeFilter) return true;
|
||||||
|
return item.type === selectedTypeFilter;
|
||||||
|
});
|
||||||
|
|
||||||
const renderItem = ({ item }) => {
|
const renderItem = ({ item }) => {
|
||||||
const type = item.type || 'FOLLOWUP';
|
const type = item.type || 'FOLLOWUP';
|
||||||
|
|
@ -253,7 +262,7 @@ const TasksScreen = ({ navigation }) => {
|
||||||
styles.catBtn,
|
styles.catBtn,
|
||||||
isActive && { backgroundColor: 'white' },
|
isActive && { backgroundColor: 'white' },
|
||||||
]}
|
]}
|
||||||
onPress={() => setActiveCategory(cat.key)}
|
onPress={() => { setActiveCategory(cat.key); setSelectedTypeFilter(null); }}
|
||||||
>
|
>
|
||||||
<Text style={[styles.catBtnText, isActive && { color: cat.color }]}>
|
<Text style={[styles.catBtnText, isActive && { color: cat.color }]}>
|
||||||
{cat.label}
|
{cat.label}
|
||||||
|
|
@ -267,6 +276,54 @@ const TasksScreen = ({ navigation }) => {
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Event Type Filter Row */}
|
||||||
|
<View style={{ backgroundColor: '#f8fafc', paddingTop: 8, paddingBottom: 4 }}>
|
||||||
|
<ScrollView
|
||||||
|
horizontal
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
style={styles.typeFilterScroll}
|
||||||
|
contentContainerStyle={styles.typeFilterScrollContent}
|
||||||
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.typeFilterBtn,
|
||||||
|
!selectedTypeFilter && styles.typeFilterBtnActive
|
||||||
|
]}
|
||||||
|
onPress={() => setSelectedTypeFilter(null)}
|
||||||
|
>
|
||||||
|
<Text style={[styles.typeFilterBtnText, !selectedTypeFilter && styles.typeFilterBtnTextActive]}>
|
||||||
|
All ({currentCategoryTotalCount})
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
{Object.entries(TYPE_ICONS).map(([type, icon]) => {
|
||||||
|
const count = (categorized[activeCategory] || []).filter(item => item.type === type).length;
|
||||||
|
if (count === 0) return null;
|
||||||
|
const isActive = selectedTypeFilter === type;
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={type}
|
||||||
|
style={[
|
||||||
|
styles.typeFilterBtn,
|
||||||
|
isActive && styles.typeFilterBtnActive,
|
||||||
|
isActive && { borderColor: TYPE_COLORS[type] || '#64748b' }
|
||||||
|
]}
|
||||||
|
onPress={() => setSelectedTypeFilter(isActive ? null : type)}
|
||||||
|
>
|
||||||
|
<Text style={styles.typeFilterIcon}>{icon}</Text>
|
||||||
|
<Text style={[styles.typeFilterBtnText, isActive && styles.typeFilterBtnTextActive]}>
|
||||||
|
{type.replace(/_/g, ' ')}
|
||||||
|
</Text>
|
||||||
|
<View style={[styles.typeFilterCount, isActive ? { backgroundColor: TYPE_COLORS[type] || '#64748b' } : { backgroundColor: '#e2e8f0' }]}>
|
||||||
|
<Text style={[styles.typeFilterCountText, isActive && { color: 'white' }]}>
|
||||||
|
{count}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* Section Header */}
|
{/* Section Header */}
|
||||||
<View style={[styles.sectionBanner, { backgroundColor: currentCat.bg, borderLeftColor: currentCat.color }]}>
|
<View style={[styles.sectionBanner, { backgroundColor: currentCat.bg, borderLeftColor: currentCat.color }]}>
|
||||||
<Text style={[styles.sectionBannerTitle, { color: currentCat.color }]}>
|
<Text style={[styles.sectionBannerTitle, { color: currentCat.color }]}>
|
||||||
|
|
@ -387,6 +444,25 @@ const styles = StyleSheet.create({
|
||||||
emptyIcon: { fontSize: 52, marginBottom: 14 },
|
emptyIcon: { fontSize: 52, marginBottom: 14 },
|
||||||
emptyTitle: { fontSize: 18, fontWeight: '900', color: '#1e293b' },
|
emptyTitle: { fontSize: 18, fontWeight: '900', color: '#1e293b' },
|
||||||
emptySub: { fontSize: 13, color: '#64748b', marginTop: 6, textAlign: 'center' },
|
emptySub: { fontSize: 13, color: '#64748b', marginTop: 6, textAlign: 'center' },
|
||||||
|
|
||||||
|
// Type filters
|
||||||
|
typeFilterScroll: { paddingHorizontal: 16, marginBottom: 4 },
|
||||||
|
typeFilterScrollContent: { gap: 8, paddingRight: 32 },
|
||||||
|
typeFilterBtn: {
|
||||||
|
flexDirection: 'row', alignItems: 'center', gap: 6,
|
||||||
|
paddingHorizontal: 12, paddingVertical: 6,
|
||||||
|
borderRadius: 20, backgroundColor: 'white',
|
||||||
|
borderWidth: 1, borderColor: '#e2e8f0',
|
||||||
|
},
|
||||||
|
typeFilterBtnActive: {
|
||||||
|
borderColor: Colors.primary,
|
||||||
|
backgroundColor: '#fff7ed',
|
||||||
|
},
|
||||||
|
typeFilterIcon: { fontSize: 13 },
|
||||||
|
typeFilterBtnText: { fontSize: 11, fontWeight: '700', color: '#64748b', textTransform: 'capitalize' },
|
||||||
|
typeFilterBtnTextActive: { color: '#1e293b', fontWeight: '800' },
|
||||||
|
typeFilterCount: { minWidth: 16, height: 16, borderRadius: 8, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 4 },
|
||||||
|
typeFilterCountText: { fontSize: 9, fontWeight: '900', color: '#475569' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default TasksScreen;
|
export default TasksScreen;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue