From 27e1d855804f59fe7f7730d575eb013d31a00f8b Mon Sep 17 00:00:00 2001 From: Matt McCoy Date: Wed, 17 Apr 2024 12:06:46 -0400 Subject: [PATCH] fix: refactor task list to not call db for all labels separately --- backend/db/migrations/4.label.sql | 10 ++- backend/docs/docs.go | 39 +++++++++++ backend/docs/swagger.json | 39 +++++++++++ backend/docs/swagger.yaml | 26 ++++++++ backend/schema/task_labels/routes.go | 54 ++++++++++++++-- backend/schema/task_labels/transactions.go | 2 +- client/components/calendar/TaskInfoCard.tsx | 64 ++++++++----------- client/components/filter/FilterCircleCard.tsx | 2 +- client/screens/TaskList.tsx | 32 ++++++---- client/services/label.ts | 22 +++++++ 10 files changed, 234 insertions(+), 56 deletions(-) diff --git a/backend/db/migrations/4.label.sql b/backend/db/migrations/4.label.sql index bd4d8e8..5080b80 100644 --- a/backend/db/migrations/4.label.sql +++ b/backend/db/migrations/4.label.sql @@ -36,5 +36,13 @@ VALUES (3, 3, 'Financial'), (4, 4, 'Household'), (6, 5, 'Financial'), - (7, 5, 'Appointments') + (7, 5, 'Appointments'), + (8, 5, 'Financial'), + (9, 5, 'Appointments'), + (10, 5, 'Financial'), + (11, 5, 'Appointments'), + (12, 5, 'Financial'), + (13, 5, 'Appointments'), + (14, 5, 'Financial'), + (15, 5, 'Appointments') ; diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 35b5141..0a47db0 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -803,6 +803,45 @@ const docTemplate = `{ } } }, + "/tasks/labels/tasks": { + "get": { + "description": "gets the information about multiple labals given their task id", + "tags": [ + "task labels" + ], + "summary": "gets the information about multiple labels", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Task IDs", + "name": "taskIDs", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Task_Label" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + } + } + } + }, "/tasks/{tid}": { "get": { "description": "get a task given its id", diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index c4d5dfe..1d7ca39 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -796,6 +796,45 @@ } } }, + "/tasks/labels/tasks": { + "get": { + "description": "gets the information about multiple labals given their task id", + "tags": [ + "task labels" + ], + "summary": "gets the information about multiple labels", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Task IDs", + "name": "taskIDs", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Task_Label" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + } + } + } + }, "/tasks/{tid}": { "get": { "description": "get a task given its id", diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 000d37e..afd477c 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -961,6 +961,32 @@ paths: summary: Get Filtered Tasks tags: - tasks + /tasks/labels/tasks: + get: + description: gets the information about multiple labals given their task id + parameters: + - collectionFormat: csv + description: Task IDs + in: query + items: + type: string + name: taskIDs + required: true + type: array + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Task_Label' + type: array + "400": + description: Bad Request + schema: + type: string + summary: gets the information about multiple labels + tags: + - task labels /user: get: description: gets the information about multiple users given their user id diff --git a/backend/schema/task_labels/routes.go b/backend/schema/task_labels/routes.go index 87567be..c0eee76 100644 --- a/backend/schema/task_labels/routes.go +++ b/backend/schema/task_labels/routes.go @@ -1,7 +1,9 @@ package task_labels import ( + "carewallet/models" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5/pgxpool" @@ -13,11 +15,19 @@ type PgModel struct { func TaskGroup(v1 *gin.RouterGroup, c *PgModel) *gin.RouterGroup { - tasks := v1.Group(":tid/labels") + tasks := v1.Group("") { - tasks.POST("", c.addLabelToTask) - tasks.DELETE("", c.removeLabelFromTask) - tasks.GET("", c.getLabelsByTask) + labelByTaskID := tasks.Group(":tid/labels") + { + labelByTaskID.POST("", c.addLabelToTask) + labelByTaskID.DELETE("", c.removeLabelFromTask) + labelByTaskID.GET("", c.getLabelsByTask) + } + + labelByTaskIDs := tasks.Group("labels/tasks") + { + labelByTaskIDs.GET("", c.getLabelsByTasks) + } } return tasks @@ -109,3 +119,39 @@ func (pg *PgModel) removeLabelFromTask(c *gin.Context) { c.JSON(http.StatusOK, "") } + +type LabelsQuery struct { + TaskIDs []string `form:"taskIDs"` +} + +// getLabelsByTasks godoc +// +// @summary gets the information about multiple labels +// @description gets the information about multiple labals given their task id +// @tags task labels +// +// @param taskIDs query []string true "Task IDs" +// +// @success 200 {array} models.Task_Label +// @failure 400 {object} string +// @router /tasks/labels/tasks [GET] +func (pg *PgModel) getLabelsByTasks(c *gin.Context) { + + taskIDs := c.Query("taskIDs") + taskQuery := LabelsQuery{ + TaskIDs: strings.Split(taskIDs, ","), + } + + var labels []models.Task_Label + + for _, element := range taskQuery.TaskIDs { + label, err := getLabelsByTaskInDB(pg.Conn, element) + if err != nil { + c.JSON(http.StatusBadRequest, err.Error()) + return + } + labels = append(labels, label...) + } + + c.JSON(http.StatusOK, labels) +} diff --git a/backend/schema/task_labels/transactions.go b/backend/schema/task_labels/transactions.go index 2b392b4..7eb7b0e 100644 --- a/backend/schema/task_labels/transactions.go +++ b/backend/schema/task_labels/transactions.go @@ -21,7 +21,7 @@ func getLabelsByTaskInDB(conn *pgxpool.Pool, taskId string) ([]models.Task_Label for rows.Next() { task := models.Task_Label{} - err := rows.Scan(&task.GroupId, &task.TaskId, &task.LabelName) + err := rows.Scan(&task.TaskId, &task.GroupId, &task.LabelName) if err != nil { print(err, "error scanning tasks by query") diff --git a/client/components/calendar/TaskInfoCard.tsx b/client/components/calendar/TaskInfoCard.tsx index 6d8ddad..a912189 100644 --- a/client/components/calendar/TaskInfoCard.tsx +++ b/client/components/calendar/TaskInfoCard.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { Text, View } from 'react-native'; -import clsx from 'clsx'; +import { clsx } from 'clsx'; import moment from 'moment'; import Calendar from '../../assets/Date_today.svg'; import Time from '../../assets/Time.svg'; -import { useTaskById } from '../../services/task'; +import { Task } from '../../types/task'; +import { TaskLabel } from '../../types/taskLabel'; import { CategoryIconsMap, TaskTypeDescriptions, @@ -59,37 +60,26 @@ function categoryToBGColor(category: string) { } export function TaskInfoComponent({ - name, - id, - category, - status, - date + task, + taskLabels }: { - id: number; - name: string; - category: string; - status: string; - date: Date; + task: Task; + taskLabels: TaskLabel[] | undefined; }) { - const { task } = useTaskById(id.toString()); - const { taskLabels } = useTaskById(id.toString()); - - console.log(task?.task_type); - return ( - {name} + {task.task_title} - {`${status?.charAt(0)}${status?.slice(1).toLowerCase()}`} + {`${task.task_status?.charAt(0)}${task.task_status?.slice(1).toLowerCase()}`} @@ -97,44 +87,42 @@ export function TaskInfoComponent({ - {moment(date).format('MMMM DD')} + {moment(task.start_date).format('MMMM DD')} {CategoryIconsMap[TypeToCategoryMap[task?.task_type ?? 'Other']]} - {TaskTypeDescriptions[category]} + {TaskTypeDescriptions[task.task_type]} - {taskLabels?.map((label) => ( - - - {label.label_name} - - - ))} + {taskLabels && + taskLabels.map((label) => ( + + + {label.label_name} + + + ))} diff --git a/client/components/filter/FilterCircleCard.tsx b/client/components/filter/FilterCircleCard.tsx index 702c06f..fa83cc8 100644 --- a/client/components/filter/FilterCircleCard.tsx +++ b/client/components/filter/FilterCircleCard.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Text, View } from 'react-native'; -import clsx from 'clsx'; +import { clsx } from 'clsx'; export function FilterCircleCard({ selected, diff --git a/client/screens/TaskList.tsx b/client/screens/TaskList.tsx index fed8266..6e3b5e9 100644 --- a/client/screens/TaskList.tsx +++ b/client/screens/TaskList.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { + ActivityIndicator, Pressable, SafeAreaView, ScrollView, @@ -21,6 +22,7 @@ import { FilterBottomSheet } from '../components/filter/FilterBottomSheet'; import { useCareWalletContext } from '../contexts/CareWalletContext'; import { AppStackNavigation } from '../navigation/types'; import { useGroup } from '../services/group'; +import { useLabelsByTasks } from '../services/label'; import { useFilteredTasks } from '../services/task'; import { useUsers } from '../services/user'; import { Task } from '../types/task'; @@ -30,12 +32,12 @@ export default function TaskListScreen() { const navigator = useNavigation(); const [canPress, setCanPress] = useState(true); const [searchQuery, setSearchQuery] = useState(''); - const { tasks } = useFilteredTasks({ groupID: userGroup.groupID }); - // State variables for selected filter options - const [sortBy, setSortBy] = useState('All Tasks'); - console.log(sortBy); - // const [taskStatus, setTaskStatus] = useState([]); - // const [assignedTo, setAssignedTo] = useState([]); + const { tasks, tasksIsLoading } = useFilteredTasks({ + groupID: userGroup.groupID + }); + const { labels, labelsIsLoading } = useLabelsByTasks( + tasks?.map((task) => task.task_id) ?? [] + ); const snapToIndex = (index: number) => bottomSheetRef.current?.snapToIndex(index); @@ -109,11 +111,10 @@ export default function TaskListScreen() { }} > label.task_id === task.task_id + )} /> ); @@ -122,6 +123,15 @@ export default function TaskListScreen() { ); }; + if (tasksIsLoading || labelsIsLoading) { + return ( + + + Loading... + + ); + } + return ( diff --git a/client/services/label.ts b/client/services/label.ts index ae15706..6e680d5 100644 --- a/client/services/label.ts +++ b/client/services/label.ts @@ -17,6 +17,18 @@ const getLabelsByGroup = async (groupID: number): Promise => { return uniques; }; +const getLabelsByTasks = async (task_ids: number[]): Promise => { + if (task_ids.length === 0) return []; + const taskIDs = task_ids.map((id) => id); + const { data } = await axios.get(`${api_url}/tasks/labels/tasks`, { + params: { taskIDs: taskIDs.join(',') } + }); + + console.log('data: ', data); + + return data; +}; + export const useLabelsByGroup = (groupID: number) => { const { data: labels, isLoading: labelsIsLoading } = useQuery({ queryKey: ['labels', groupID], @@ -25,3 +37,13 @@ export const useLabelsByGroup = (groupID: number) => { return { labels, labelsIsLoading }; }; + +export const useLabelsByTasks = (task_ids: number[]) => { + const { data: labels, isLoading: labelsIsLoading } = useQuery({ + queryKey: ['labels', task_ids], + queryFn: () => getLabelsByTasks(task_ids), + enabled: task_ids.length > 0 + }); + + return { labels, labelsIsLoading }; +};