diff --git a/backend/db/migrations/3.task.sql b/backend/db/migrations/3.task.sql index 1565c2b..86bc854 100644 --- a/backend/db/migrations/3.task.sql +++ b/backend/db/migrations/3.task.sql @@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS task ( created_date timestamp NOT NULL, -- add default val with current timestamp? start_date timestamp, end_date timestamp, + quick_task BOOLEAN DEFAULT FALSE, notes varchar, repeating BOOLEAN DEFAULT FALSE, repeating_interval varchar, @@ -39,12 +40,15 @@ CREATE TABLE IF NOT EXISTS task_assignees ( FOREIGN KEY (assigned_by) REFERENCES users (user_id) ); -INSERT INTO task (task_title, group_id, created_by, created_date, start_date, end_date, notes, task_status, task_type) +INSERT INTO task (task_title, group_id, created_by, created_date, start_date, end_date, notes, task_status, task_type, quick_task) VALUES - ('task 1', 1, 'user2', '2024-02-03 10:45:00', '2024-02-05 10:00:00', '2024-02-05 11:00:00', 'Pick up medication from pharmacy', 'INCOMPLETE', 'med_mgmt'), - ('task 2', 2, 'user3', '2024-02-20 23:59:59', '2024-02-10 14:30:00', NULL, 'Schedule doctor appointment', 'INCOMPLETE', 'other'), - ('task 3', 3, 'user4', '2020-02-05 11:00:00', NULL, '2024-02-20 23:59:59', 'Submit insurance claim', 'PARTIAL', 'financial'), - ('task 4', 4, 'user1', '2006-01-02 15:04:05', NULL, NULL, 'Refill water pitcher', 'COMPLETE', 'other') + ('task 1', 1, 'user2', '2024-02-03 10:45:00', '2024-02-05 10:00:00', '2024-02-05 11:00:00', 'Pick up medication from pharmacy', 'INCOMPLETE', 'med_mgmt', FALSE), + ('task 2', 2, 'user3', '2024-02-20 23:59:59', '2024-02-10 14:30:00', NULL, 'Schedule doctor appointment', 'INCOMPLETE', 'other', FALSE), + ('task 3', 3, 'user4', '2020-02-05 11:00:00', NULL, '2024-02-20 23:59:59', 'Submit insurance claim', 'PARTIAL', 'financial', FALSE), + ('task 4', 4, 'user1', '2006-01-02 15:04:05', NULL, NULL, 'Refill water pitcher', 'COMPLETE', 'other', TRUE), + ('task 5', 5, 'user1', '2024-02-05 11:00:00', '2024-02-05 11:00:00', '2024-02-05 11:00:00', 'Get medications', 'INCOMPLETE', 'dr_appt', TRUE), + ('task 6', 5, 'user2', '2024-02-05 11:00:00', '2024-02-05 11:00:00', '2024-02-05 11:00:00', 'File Papers', 'INCOMPLETE', 'med_mgmt', TRUE), + ('task 7', 5, 'user3', '2024-02-05 11:00:00', '2024-02-05 11:00:00', '2024-02-05 11:00:00', 'Send check to Drs', 'INCOMPLETE', 'financial', TRUE) ; INSERT INTO task_assignees (task_id, user_id, assignment_status, assigned_by, assigned_date) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 471098b..823c6de 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -520,6 +520,11 @@ const docTemplate = `{ "name": "groupID", "in": "query" }, + { + "type": "string", + "name": "quickTask", + "in": "query" + }, { "type": "string", "name": "startDate", @@ -1171,6 +1176,9 @@ const docTemplate = `{ "notes": { "type": "string" }, + "quick_task": { + "type": "boolean" + }, "repeating": { "type": "boolean" }, @@ -1312,6 +1320,9 @@ const docTemplate = `{ "notes": { "type": "string" }, + "quick_task": { + "type": "boolean" + }, "repeating": { "type": "boolean" }, diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 4a43d2d..412bdeb 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -513,6 +513,11 @@ "name": "groupID", "in": "query" }, + { + "type": "string", + "name": "quickTask", + "in": "query" + }, { "type": "string", "name": "startDate", @@ -1164,6 +1169,9 @@ "notes": { "type": "string" }, + "quick_task": { + "type": "boolean" + }, "repeating": { "type": "boolean" }, @@ -1305,6 +1313,9 @@ "notes": { "type": "string" }, + "quick_task": { + "type": "boolean" + }, "repeating": { "type": "boolean" }, diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 6ff3091..fbd55f7 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -88,6 +88,8 @@ definitions: type: integer notes: type: string + quick_task: + type: boolean repeating: type: boolean repeating_end_date: @@ -180,6 +182,8 @@ definitions: type: integer notes: type: string + quick_task: + type: boolean repeating: type: boolean repeating_end_date: @@ -759,6 +763,9 @@ paths: - in: query name: groupID type: string + - in: query + name: quickTask + type: string - in: query name: startDate type: string diff --git a/backend/models/task.go b/backend/models/task.go index 512e6ef..6f23880 100644 --- a/backend/models/task.go +++ b/backend/models/task.go @@ -12,6 +12,7 @@ type Task struct { CreatedDate time.Time `json:"created_date"` StartDate *time.Time `json:"start_date"` EndDate *time.Time `json:"end_date"` + QuickTask bool `json:"quick_task"` Notes *string `json:"notes"` Repeating bool `json:"repeating"` RepeatingInterval *string `json:"repeating_interval"` diff --git a/backend/schema/tasks/routes.go b/backend/schema/tasks/routes.go index ff83402..a042495 100644 --- a/backend/schema/tasks/routes.go +++ b/backend/schema/tasks/routes.go @@ -71,6 +71,7 @@ type TaskQuery struct { TaskType string `form:"taskType"` StartDate string `form:"startDate"` EndDate string `form:"endDate"` + QuickTask string `form:"quickTask"` } // GetFilteredTasks godoc @@ -194,6 +195,7 @@ type TaskBody struct { CreatedDate time.Time `json:"created_date"` StartDate *time.Time `json:"start_date"` EndDate *time.Time `json:"end_date"` + QuickTask bool `json:"quick_task"` Notes *string `json:"notes"` Repeating bool `json:"repeating"` RepeatingInterval *string `json:"repeating_interval"` diff --git a/backend/schema/tasks/task_test.go b/backend/schema/tasks/task_test.go index 16462b4..63f2b98 100644 --- a/backend/schema/tasks/task_test.go +++ b/backend/schema/tasks/task_test.go @@ -49,6 +49,7 @@ func TestTaskGroup(t *testing.T) { TaskType: "other", StartDate: "", EndDate: "", + QuickTask: "", } w := httptest.NewRecorder() @@ -59,6 +60,7 @@ func TestTaskGroup(t *testing.T) { query.Set("taskType", getRequest.TaskType) query.Set("startDate", getRequest.StartDate) query.Set("endDate", getRequest.EndDate) + query.Set("quickTask", getRequest.QuickTask) req, _ := http.NewRequest("GET", "/tasks/filtered?"+query.Encode(), nil) router.ServeHTTP(w, req) @@ -76,8 +78,9 @@ func TestTaskGroup(t *testing.T) { return } start_date_1 := time.Date(2024, 2, 10, 14, 30, 0, 0, time.UTC) - note := "Schedule doctor appointment" - note2 := "Refill water pitcher" + notes_1 := "Schedule doctor appointment" + notes_2 := "Refill water pitcher" + expectedTasks := []models.Task{ { TaskID: 2, @@ -87,7 +90,8 @@ func TestTaskGroup(t *testing.T) { CreatedDate: time.Date(2024, 2, 20, 23, 59, 59, 0, time.UTC), StartDate: &start_date_1, EndDate: nil, - Notes: ¬e, + QuickTask: false, + Notes: ¬es_1, Repeating: false, RepeatingInterval: nil, RepeatingEndDate: nil, @@ -103,10 +107,11 @@ func TestTaskGroup(t *testing.T) { CreatedDate: time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), StartDate: nil, EndDate: nil, - Notes: ¬e2, Repeating: false, RepeatingInterval: nil, RepeatingEndDate: nil, + QuickTask: true, + Notes: ¬es_2, TaskStatus: "COMPLETE", TaskType: "other", TaskInfo: nil, @@ -220,6 +225,7 @@ func TestTaskGroup(t *testing.T) { CreatedDate: time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), StartDate: nil, EndDate: nil, + QuickTask: true, Notes: ¬e, Repeating: false, RepeatingInterval: nil, @@ -267,6 +273,7 @@ func TestTaskGroup(t *testing.T) { RepeatingEndDate: nil, TaskStatus: "COMPLETE", TaskType: "other", + QuickTask: true, TaskInfo: nil, }, } @@ -290,10 +297,10 @@ func TestTaskGroup(t *testing.T) { TaskID: 1, GroupID: 1, CreatedBy: "user1", - CreatedDate: time.Now().UTC(), StartDate: &startDate, EndDate: &endDate, Notes: ¬es, + QuickTask: false, Repeating: repeating, RepeatingInterval: &repeatingInterval, RepeatingEndDate: &repeatingEndDate, diff --git a/backend/schema/tasks/transactions.go b/backend/schema/tasks/transactions.go index b6294a3..bc3f444 100644 --- a/backend/schema/tasks/transactions.go +++ b/backend/schema/tasks/transactions.go @@ -19,9 +19,10 @@ func GetTasksByQueryFromDB(pool *pgx.Conn, filterQuery TaskQuery) ([]models.Task filterQuery.TaskStatus, filterQuery.TaskType, filterQuery.StartDate, - filterQuery.EndDate} + filterQuery.EndDate, + filterQuery.QuickTask} - field_names := []string{"task_id =", "task_title =", "group_id =", "created_by =", "task_status =", "task_type =", "start_date >=", "end_date <="} + field_names := []string{"task_id =", "task_title =", "group_id =", "created_by =", "task_status =", "task_type =", "start_date >=", "end_date <=", "quick_task ="} var query string var args []interface{} @@ -48,7 +49,7 @@ func GetTasksByQueryFromDB(pool *pgx.Conn, filterQuery TaskQuery) ([]models.Task for rows.Next() { task := models.Task{} - err := rows.Scan(&task.TaskID, &task.TaskTitle, &task.GroupID, &task.CreatedBy, &task.CreatedDate, &task.StartDate, &task.EndDate, &task.Notes, &task.Repeating, &task.RepeatingInterval, &task.RepeatingEndDate, &task.TaskStatus, &task.TaskType, &task.TaskInfo) + err := rows.Scan(&task.TaskID, &task.TaskTitle, &task.GroupID, &task.CreatedBy, &task.CreatedDate, &task.StartDate, &task.EndDate, &task.QuickTask, &task.Notes, &task.Repeating, &task.RepeatingInterval, &task.RepeatingEndDate, &task.TaskStatus, &task.TaskType, &task.TaskInfo) if err != nil { print(err.Error(), "error scanning tasks by query") @@ -144,7 +145,7 @@ func GetTasksByAssignedFromDB(pool *pgx.Conn, userIDs []string) ([]models.Task, // Get all tasks by task ID var task models.Task for _, task_id := range task_ids { - err := pool.QueryRow("SELECT * FROM task WHERE task_id = $1;", task_id).Scan(&task.TaskID, &task.TaskTitle, &task.GroupID, &task.CreatedBy, &task.CreatedDate, &task.StartDate, &task.EndDate, &task.Notes, &task.Repeating, &task.RepeatingInterval, &task.RepeatingEndDate, &task.TaskStatus, &task.TaskType, &task.TaskInfo) + err := pool.QueryRow("SELECT * FROM task WHERE task_id = $1;", task_id).Scan(&task.TaskID, &task.TaskTitle, &task.GroupID, &task.CreatedBy, &task.CreatedDate, &task.StartDate, &task.EndDate, &task.QuickTask, &task.Notes, &task.Repeating, &task.RepeatingInterval, &task.RepeatingEndDate, &task.TaskStatus, &task.TaskType, &task.TaskInfo) if err != nil { print(err, "error querying task by ID") return nil, err @@ -159,7 +160,7 @@ func GetTasksByAssignedFromDB(pool *pgx.Conn, userIDs []string) ([]models.Task, // CreateTaskInDB creates a new task in the database and returns its ID func CreateTaskInDB(pool *pgx.Conn, newTask models.Task) (int, error) { query := ` - INSERT INTO task (group_id, created_by, created_date, start_date, end_date, notes, repeating, repeating_interval, repeating_end_date, task_status, task_type, task_info, task_title) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING task_id` + INSERT INTO task (group_id, created_by, created_date, start_date, end_date, quick_task, notes, repeating, repeating_interval, repeating_end_date, task_status, task_type, task_info, task_title) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING task_id` var newTaskID int err := pool.QueryRow( @@ -169,6 +170,7 @@ func CreateTaskInDB(pool *pgx.Conn, newTask models.Task) (int, error) { time.Now(), // Assuming created_date should be the current timestamp newTask.StartDate, newTask.EndDate, + newTask.QuickTask, newTask.Notes, newTask.Repeating, newTask.RepeatingInterval, @@ -206,7 +208,21 @@ func GetTaskByID(pool *pgx.Conn, taskID int) (models.Task, error) { var task models.Task err := pool.QueryRow(query, taskID).Scan( - &task.TaskID, &task.TaskTitle, &task.GroupID, &task.CreatedBy, &task.CreatedDate, &task.StartDate, &task.EndDate, &task.Notes, &task.Repeating, &task.RepeatingInterval, &task.RepeatingEndDate, &task.TaskStatus, &task.TaskType, &task.TaskInfo, + &task.TaskID, + &task.TaskTitle, + &task.GroupID, + &task.CreatedBy, + &task.CreatedDate, + &task.StartDate, + &task.EndDate, + &task.QuickTask, + &task.Notes, + &task.Repeating, + &task.RepeatingInterval, + &task.RepeatingEndDate, + &task.TaskStatus, + &task.TaskType, + &task.TaskInfo, ) return task, err } diff --git a/client/components/QuickTaskCard.tsx b/client/components/QuickTaskCard.tsx new file mode 100644 index 0000000..302a985 --- /dev/null +++ b/client/components/QuickTaskCard.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Text, View } from 'react-native'; + +interface QuickTaskCardProps { + name: string; + label: string; +} + +function QuickTaskCard({ name, label }: QuickTaskCardProps): JSX.Element { + return ( + + + + + {name} + + + + {label} + + + + ); +} + +export { QuickTaskCard }; diff --git a/client/screens/QuickTasks.tsx b/client/screens/QuickTasks.tsx new file mode 100644 index 0000000..6fca2c1 --- /dev/null +++ b/client/screens/QuickTasks.tsx @@ -0,0 +1,92 @@ +// components/TasksPopup.tsx +import React, { useCallback, useMemo, useRef } from 'react'; +import { ActivityIndicator, FlatList, Text, View } from 'react-native'; + +import BottomSheet, { + BottomSheetBackdrop, + BottomSheetBackdropProps +} from '@gorhom/bottom-sheet/'; +import { GestureHandlerRootView } from 'react-native-gesture-handler'; +import { Button } from 'react-native-paper'; + +import { QuickTaskCard } from '../components/QuickTaskCard'; +import { useCareWalletContext } from '../contexts/CareWalletContext'; +import { useFilteredTasks } from '../services/task'; + +function QuickTasks(): JSX.Element { + const { group } = useCareWalletContext(); + const { tasks, tasksIsLoading } = useFilteredTasks({ + groupID: group.groupID.toString(), + quickTask: true + }); + + const snapPoints = useMemo(() => ['70%'], []); + const bottomSheetRef = useRef(null); + + const handleOpenPress = () => { + bottomSheetRef.current?.expand(); + console.log(tasks); + }; + const handleClosePress = () => bottomSheetRef.current?.close(); + + const renderBackdrop = useCallback( + (props: BottomSheetBackdropProps) => ( + + ), + [] + ); + + // Todo: Look into if there is a change for this + const taskTypeDescriptions: Record = { + med_mgmt: 'Medication Management', + dr_appt: 'Doctor Appointment', + financial: 'Financial Task', + other: 'Other Task' + }; + + if (tasksIsLoading) { + return ( + + + Loading Tasks... + + ); + } + + return ( + + + + + Today's Quick Tasks + + } + keyExtractor={(item) => item.task_id.toString()} + renderItem={({ item }) => ( + + )} + /> + + + ); +} + +export { QuickTasks }; diff --git a/client/services/task.ts b/client/services/task.ts new file mode 100644 index 0000000..d9be515 --- /dev/null +++ b/client/services/task.ts @@ -0,0 +1,43 @@ +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; + +import { TaskLabel } from '../types/label'; +import { Task } from '../types/task'; +import { api_url } from './api-links'; + +type TaskQueryParams = { + taskID?: string; + groupID?: string; + createdBy?: string; + taskStatus?: string; + taskType?: string; + startDate?: string; + endDate?: string; + quickTask?: boolean; +}; + +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 { data: tasks, isLoading: tasksIsLoading } = useQuery({ + queryKey: ['filteredTaskList', queryParams], + queryFn: () => getFilteredTasks(queryParams), + refetchInterval: 20000 + }); + return { + tasks, + tasksIsLoading + }; +}; diff --git a/client/types/label.ts b/client/types/label.ts new file mode 100644 index 0000000..3a3e99c --- /dev/null +++ b/client/types/label.ts @@ -0,0 +1,5 @@ +export interface TaskLabel { + task_id: number; + group_id: number; + label_name: string; +} diff --git a/client/types/task.ts b/client/types/task.ts new file mode 100644 index 0000000..3e3da09 --- /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; + repeating: boolean; + repeating_interval?: string | null; + repeating_end_date?: string | null; + quick_task: boolean; + task_status: string; + task_type: string; + task_info?: string | null; +}