diff --git a/client/components/DropDownItem.tsx b/client/components/DropDownItem.tsx
new file mode 100644
index 0000000..7aa866f
--- /dev/null
+++ b/client/components/DropDownItem.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+
+const DropdownItem = ({ label }: { label: string }) => {
+ return (
+
+ {label}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10
+ },
+ dropdownLabel: {
+ fontSize: 18,
+ color: 'care-wallet-black',
+ marginRight: 10
+ },
+ line: {
+ flex: 1,
+ height: 1,
+ backgroundColor: 'gray'
+ }
+});
+
+export default DropdownItem;
diff --git a/client/components/FilterModal.tsx b/client/components/FilterModal.tsx
new file mode 100644
index 0000000..4c6695e
--- /dev/null
+++ b/client/components/FilterModal.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import {
+ Dimensions,
+ Modal,
+ Pressable,
+ StyleSheet,
+ Text,
+ View
+} from 'react-native';
+
+import DropdownItem from './DropDownItem';
+
+const windowHeight = Dimensions.get('window').height;
+
+const FilterModal = ({
+ isVisible,
+ onClose
+}: {
+ isVisible: boolean;
+ onClose: () => void;
+}) => {
+ return (
+
+
+
+ Filters
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ modalContainer: {
+ justifyContent: 'center',
+ alignItems: 'flex-start', // Align items to the left
+ backgroundColor: 'white',
+ borderRadius: 20,
+ padding: 35,
+ height: windowHeight / 2,
+ marginTop: windowHeight / 2
+ },
+ modalText: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ textAlign: 'left',
+ color: 'black',
+ marginBottom: 20
+ },
+ filterOptions: {
+ padding: 20
+ },
+ dropdownSeparator: {
+ borderBottomWidth: 1,
+ borderBottomColor: 'grey',
+ marginVertical: 10
+ }
+});
+
+export default FilterModal;
diff --git a/client/components/TaskInfoCard.tsx b/client/components/TaskInfoCard.tsx
new file mode 100644
index 0000000..b471399
--- /dev/null
+++ b/client/components/TaskInfoCard.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+
+const TaskInfoComponent = ({
+ name,
+ label,
+ category,
+ type,
+ date
+}: {
+ name: string;
+ label: string;
+ category: string;
+ type: string;
+ date: Date;
+}) => {
+ const formattedStartDate = date ? new Date(date).toLocaleDateString() : 'N/A';
+
+ return (
+
+
+ {`Task #${name}`}
+ {label}
+
+ {`${category} | ${type}`}
+ {`${formattedStartDate}`}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ borderRadius: 10,
+ borderWidth: 1,
+ borderColor: '#000000',
+ padding: 10,
+ margin: 10,
+ backgroundColor: '#FFFFFF'
+ },
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 10
+ },
+ taskNumber: {
+ fontWeight: 'bold',
+ alignSelf: 'flex-end'
+ },
+ label: {
+ alignSelf: 'flex-start'
+ },
+ categoryType: {
+ marginTop: 10
+ }
+});
+
+export default TaskInfoComponent;
diff --git a/client/navigation/AppStackBottomTabNavigator.tsx b/client/navigation/AppStackBottomTabNavigator.tsx
index 6793920..2421f5a 100644
--- a/client/navigation/AppStackBottomTabNavigator.tsx
+++ b/client/navigation/AppStackBottomTabNavigator.tsx
@@ -9,6 +9,7 @@ import Home from '../assets/bottom-nav/home.svg';
import User from '../assets/bottom-nav/user.svg';
import MedicationList from '../screens/MedicationList';
import Profile from '../screens/Profile';
+import TaskList from '../screens/TaskList';
const AppStackBottomTab = createBottomTabNavigator();
@@ -36,7 +37,7 @@ export function AppStackBottomTabNavigator() {
tabBarIcon: ({ color }) => ,
tabBarLabel: () =>
}}
- component={MedicationList}
+ component={TaskList}
/>
(
+ {}
+ );
+ const { tasks, tasksIsLoading } = useFilteredTasks(queryParams);
+ const [isFilterModalVisible, setIsFilterModalVisible] = useState(false);
+
+ // Fetch task labels for each task (2d array list)
+ useEffect(() => {
+ const fetchTaskLabels = async () => {
+ const labels: { [taskId: string]: string[] } = {};
+ if (tasks) {
+ await Promise.all(
+ tasks.map(async (task) => {
+ const labelsForTask = await getTaskLabels(task.task_id.toString());
+ labels[task.task_id.toString()] = labelsForTask.map(
+ (label) => label.label_name
+ );
+ })
+ );
+ }
+ setTaskLabels(labels);
+ };
+
+ if (tasks) {
+ fetchTaskLabels();
+ }
+ }, [tasks]);
+
+ // Filter tasks based on search query in multiple fields and labels
+ const filteredTasks = tasks?.filter((task) => {
+ const taskFieldsMatch = [
+ 'task_id',
+ 'task_status',
+ 'task_type',
+ 'notes'
+ ].some((field) =>
+ task?.[field]
+ ?.toString()
+ .toLowerCase()
+ .includes(searchQuery.toLowerCase())
+ );
+
+ const labelMatch = taskLabels[task?.task_id?.toString()]?.some((label) =>
+ label.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ return taskFieldsMatch || labelMatch;
+ });
+
+ // Filter tasks based on categories
+ const pastDueTasks = tasks?.filter(
+ (task) => task?.end_date || '' < String(new Date())
+ );
+ const inProgressTasks = tasks?.filter(
+ (task) => task?.task_status === 'PARTIAL'
+ );
+ const inFutureTasks = tasks?.filter(
+ (task) => task?.start_date || '' > String(new Date())
+ );
+ const completeTasks = tasks?.filter(
+ (task) => task?.task_status === 'COMPLETE'
+ );
+ const incompleteTasks = tasks?.filter(
+ (task) => task?.task_status === 'INCOMPLETE'
+ );
+
+ // Abstraction to render each section
+ const renderSection = (tasks: Task[], title: string) => {
+ return (
+
+ {title}
+ {tasks.map((task, index) => {
+ return (
+
+ );
+ })}
+
+ );
+ };
+
+ return (
+
+
+ {
+ setSearchQuery(text);
+ }}
+ />
+ setIsFilterModalVisible(true)}
+ >
+ Filter
+
+
+
+ Task List (all tasks of all time)
+
+ {renderSection(filteredTasks || [], 'All Tasks')}
+ {renderSection(pastDueTasks || [], 'Past Due')}
+ {renderSection(inProgressTasks || [], 'In Progress')}
+ {renderSection(inFutureTasks || [], 'Future')}
+ {renderSection(completeTasks || [], 'Done')}
+ {renderSection(incompleteTasks || [], 'Marked as Incomplete')}
+ setIsFilterModalVisible(false)}
+ />
+
+ );
+}
+
+// TODO: Migrate this to tailwind
+const styles = StyleSheet.create({
+ container: {
+ padding: 20
+ },
+ searchContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10
+ },
+ searchInput: {
+ flex: 1,
+ height: 40,
+ borderColor: 'gray',
+ borderWidth: 1,
+ borderRadius: 20,
+ marginRight: 10,
+ padding: 8,
+ overflow: 'hidden'
+ },
+ filterButton: {
+ backgroundColor: 'gray',
+ borderRadius: 5,
+ padding: 8
+ },
+ filterButtonText: {
+ color: 'white'
+ }
+});
\ No newline at end of file
diff --git a/client/services/task.ts b/client/services/task.ts
new file mode 100644
index 0000000..cc5e167
--- /dev/null
+++ b/client/services/task.ts
@@ -0,0 +1,43 @@
+import { useQuery, useQueryClient } from '@tanstack/react-query';
+import axios from 'axios';
+
+import { Task } from '../types/task';
+import { TaskLabel } from '../types/label';
+import { api_url } from './api-links';
+
+type TaskQueryParams = {
+ taskID?: string;
+ groupID?: string;
+ createdBy?: string;
+ taskStatus?: string;
+ taskType?: string;
+ startDate?: string;
+ endDate?: string;
+};
+
+const getFilteredTasks = async (queryParams: TaskQueryParams): Promise => {
+ const { data } = await axios.get(`${api_url}/tasks/filtered?`, {
+ params: queryParams,
+ });
+ return data;
+};
+
+export const getTaskLabels = async (taskID: string): Promise => {
+ const { data } = await axios.get(`${api_url}/tasks/${taskID}/labels`);
+ return data;
+};
+
+export const useFilteredTasks = (queryParams: TaskQueryParams) => {
+ const queryClient = useQueryClient();
+
+ const { data: tasks, isLoading: tasksIsLoading } = useQuery({
+ queryKey: ['filteredTaskList', queryParams],
+ queryFn: () => getFilteredTasks(queryParams),
+ refetchInterval: 20000,
+ });
+
+ return {
+ tasks,
+ tasksIsLoading,
+ };
+};
\ No newline at end of file
diff --git a/client/types/label.ts b/client/types/label.ts
new file mode 100644
index 0000000..c91852b
--- /dev/null
+++ b/client/types/label.ts
@@ -0,0 +1,5 @@
+export interface TaskLabel {
+ task_id: number;
+ group_id: number;
+ label_name: string;
+}
\ No newline at end of file
diff --git a/client/types/task.ts b/client/types/task.ts
new file mode 100644
index 0000000..104f5d7
--- /dev/null
+++ b/client/types/task.ts
@@ -0,0 +1,16 @@
+export interface Task {
+ task_id: number;
+ group_id: number;
+ created_by: string;
+ created_date: string;
+ start_date?: string | null;
+ end_date?: string | null;
+ notes?: string | null;
+ repeating: boolean;
+ repeating_interval?: string | null;
+ repeating_end_date?: string | null;
+ task_status: string;
+ task_type: string;
+ task_info?: string | null;
+ [key: string]: string | number | boolean | null | undefined; // Index signature for string indexing
+};
\ No newline at end of file