diff --git a/http/budgeting.go b/http/budgeting.go index 9023aaa..f827dcf 100644 --- a/http/budgeting.go +++ b/http/budgeting.go @@ -60,6 +60,28 @@ func getDate(c *gin.Context) (time.Time, error) { return getFirstOfMonth(year, month, time.Now().Location()), nil } +func (h *Handler) autocompleteCategories(c *gin.Context) { + budgetID := c.Param("budgetid") + budgetUUID, err := uuid.Parse(budgetID) + if err != nil { + c.AbortWithError(http.StatusBadRequest, fmt.Errorf("budgetid missing from URL")) + return + } + + query := c.Request.URL.Query().Get("s") + searchParams := postgres.SearchCategoriesParams{ + BudgetID: budgetUUID, + Search: "%" + query + "%", + } + categories, err := h.Service.SearchCategories(c.Request.Context(), searchParams) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, categories) +} + func (h *Handler) autocompletePayee(c *gin.Context) { budgetID := c.Param("budgetid") budgetUUID, err := uuid.Parse(budgetID) diff --git a/http/http.go b/http/http.go index 3cb78c5..2ba5920 100644 --- a/http/http.go +++ b/http/http.go @@ -67,6 +67,7 @@ func (h *Handler) Serve() { authenticated.GET("/admin/clear-database", h.clearDatabase) authenticated.GET("/budget/:budgetid", h.budgeting) authenticated.GET("/budget/:budgetid/autocomplete/payees", h.autocompletePayee) + authenticated.GET("/budget/:budgetid/autocomplete/categories", h.autocompleteCategories) authenticated.DELETE("/budget/:budgetid", h.deleteBudget) authenticated.POST("/budget/:budgetid/import/ynab", h.importYNAB) authenticated.POST("/budget/:budgetid/settings/clear", h.clearBudget) diff --git a/postgres/categories.sql.go b/postgres/categories.sql.go index 9649011..7ddb062 100644 --- a/postgres/categories.sql.go +++ b/postgres/categories.sql.go @@ -116,3 +116,44 @@ func (q *Queries) GetCategoryGroups(ctx context.Context, budgetID uuid.UUID) ([] } return items, nil } + +const searchCategories = `-- name: SearchCategories :many +SELECT CONCAT(category_groups.name, ' : ', categories.name) as name, categories.id FROM categories +INNER JOIN category_groups ON categories.category_group_id = category_groups.id +WHERE category_groups.budget_id = $1 +AND categories.name LIKE $2 +ORDER BY category_groups.name, categories.name +` + +type SearchCategoriesParams struct { + BudgetID uuid.UUID + Search string +} + +type SearchCategoriesRow struct { + Name interface{} + ID uuid.UUID +} + +func (q *Queries) SearchCategories(ctx context.Context, arg SearchCategoriesParams) ([]SearchCategoriesRow, error) { + rows, err := q.db.QueryContext(ctx, searchCategories, arg.BudgetID, arg.Search) + if err != nil { + return nil, err + } + defer rows.Close() + var items []SearchCategoriesRow + for rows.Next() { + var i SearchCategoriesRow + if err := rows.Scan(&i.Name, &i.ID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/postgres/queries/categories.sql b/postgres/queries/categories.sql index 389406c..2ff245b 100644 --- a/postgres/queries/categories.sql +++ b/postgres/queries/categories.sql @@ -18,4 +18,12 @@ RETURNING *; SELECT categories.*, category_groups.name as group FROM categories INNER JOIN category_groups ON categories.category_group_id = category_groups.id WHERE category_groups.budget_id = $1 -ORDER BY category_groups.name, categories.name; \ No newline at end of file +ORDER BY category_groups.name, categories.name; + +-- name: SearchCategories :many +SELECT CONCAT(category_groups.name, ' : ', categories.name) as name, categories.id FROM categories +INNER JOIN category_groups ON categories.category_group_id = category_groups.id +WHERE category_groups.budget_id = @budget_id +AND categories.name LIKE @search +ORDER BY category_groups.name, categories.name; +--ORDER BY levenshtein(payees.name, $2); \ No newline at end of file diff --git a/web/src/pages/Account.vue b/web/src/pages/Account.vue index c7a4177..085d9b6 100644 --- a/web/src/pages/Account.vue +++ b/web/src/pages/Account.vue @@ -44,7 +44,7 @@ export default defineComponent({ - +