diff --git a/api/src/graphql/monthlyBudgets.sdl.ts b/api/src/graphql/monthlyBudgets.sdl.ts
new file mode 100644
index 0000000..ca39a21
--- /dev/null
+++ b/api/src/graphql/monthlyBudgets.sdl.ts
@@ -0,0 +1,31 @@
+export const schema = gql`
+ type MonthlyBudgetCategory {
+ id: String!
+ name: String!
+ month: Int!
+ year: Int!
+ assigned: Float!
+ activity: Float!
+ available: Float!
+ }
+
+ type MonthlyBudgetGroup {
+ id: String!
+ name: String!
+ month: Int!
+ year: Int!
+ assigned: Float!
+ activity: Float!
+ available: Float!
+ subRows: [MonthlyBudgetCategory!]
+ }
+
+ type Query {
+ monthlyBudget(
+ id: String!
+ userId: String!
+ month: Int!
+ year: Int!
+ ): [MonthlyBudgetGroup!]! @requireAuth
+ }
+`
diff --git a/api/src/services/monthlyBudgets/monthlyBudgets.ts b/api/src/services/monthlyBudgets/monthlyBudgets.ts
new file mode 100644
index 0000000..08e78b6
--- /dev/null
+++ b/api/src/services/monthlyBudgets/monthlyBudgets.ts
@@ -0,0 +1,111 @@
+import type {
+ MonthlyBudgetGroupRelationResolvers,
+ QueryResolvers,
+} from 'types/graphql'
+
+import { db } from 'src/lib/db'
+
+export const monthlyBudget: QueryResolvers['monthlyBudget'] = async ({
+ id,
+ userId,
+ month,
+ year,
+}) => {
+ const query = await db.budget.findUnique({
+ where: {
+ id,
+ userId,
+ },
+ include: {
+ budgetCategoryGroups: {
+ orderBy: {
+ sortOrder: 'asc',
+ },
+ include: {
+ monthlyCategoryGroupActivities: {
+ where: {
+ month,
+ year,
+ },
+ },
+ budgetCategories: {
+ orderBy: {
+ sortOrder: 'asc',
+ },
+ include: {
+ monthlyBudgetPerCategories: {
+ where: {
+ month,
+ year,
+ },
+ include: {
+ monthlyCategoryActivity: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+
+ const res = query.budgetCategoryGroups.map((group) => {
+ return {
+ id: group.id,
+ name: group.name,
+ month,
+ year,
+ assigned:
+ group.monthlyCategoryGroupActivities[0]?.assigned.toNumber() || 0,
+ activity:
+ group.monthlyCategoryGroupActivities[0]?.activity.toNumber() || 0,
+ available:
+ group.monthlyCategoryGroupActivities[0]?.available.toNumber() || 0,
+ }
+ })
+
+ return res
+}
+
+export const MonthlyBudgetGroup: MonthlyBudgetGroupRelationResolvers = {
+ subRows: async (_obj, { root }) => {
+ const categoryGroups = await db.budgetCategoryGroup.findUnique({
+ where: { id: root.id },
+ include: {
+ budgetCategories: {
+ orderBy: {
+ sortOrder: 'asc',
+ },
+ include: {
+ monthlyBudgetPerCategories: {
+ where: {
+ month: root.month,
+ year: root.year,
+ },
+ include: {
+ monthlyCategoryActivity: true,
+ },
+ },
+ },
+ },
+ },
+ })
+
+ return categoryGroups.budgetCategories.map((category) => {
+ return {
+ id: category.id,
+ name: category.name,
+ month: root.month,
+ year: root.year,
+ assigned:
+ category.monthlyBudgetPerCategories[0]?.assigned.toNumber() || 0,
+ activity:
+ category.monthlyBudgetPerCategories[0]?.monthlyCategoryActivity.activity.toNumber() ||
+ 0,
+ available:
+ category.monthlyBudgetPerCategories[0]?.monthlyCategoryActivity.available.toNumber() ||
+ 0,
+ }
+ })
+ },
+}
diff --git a/web/src/components/MonthlyBudgetsCell/MonthlyBudgetsCell.tsx b/web/src/components/MonthlyBudgetsCell/MonthlyBudgetsCell.tsx
index 8e7beb5..9e5cb73 100644
--- a/web/src/components/MonthlyBudgetsCell/MonthlyBudgetsCell.tsx
+++ b/web/src/components/MonthlyBudgetsCell/MonthlyBudgetsCell.tsx
@@ -9,8 +9,6 @@ import {
DataTableSkeleton,
} from 'src/components/Spreadsheet/data-table'
-import { convertBudgetGQLIntoDisplayable } from './convertData'
-
export const QUERY = gql`
query FindBudgetByMonth(
$userId: String!
@@ -18,30 +16,18 @@ export const QUERY = gql`
$month: Int!
$year: Int!
) {
- budget(id: $budgetId, userId: $userId) {
- budgetCategoryGroups {
+ monthlyBudget(id: $budgetId, userId: $userId, month: $month, year: $year) {
+ id
+ category: name
+ assigned
+ activity
+ available
+ subRows {
id
- name
- sortOrder
- monthlyCategoryGroupActivity(month: $month, year: $year) {
- assigned
- activity
- available
- }
- budgetCategories {
- id
- name
- sortOrder
- monthlyBudgetPerCategory(month: $month, year: $year) {
- month
- year
- assigned
- monthlyCategoryActivity {
- activity
- available
- }
- }
- }
+ category: name
+ assigned
+ activity
+ available
}
}
}
@@ -57,11 +43,8 @@ export const Failure = ({ error }: CellFailureProps) => (
Error: {error?.message}
)
-export const Success = ({ budget }: CellSuccessProps) => {
- return (
-
- )
+export const Success = ({
+ monthlyBudget,
+}: CellSuccessProps) => {
+ return
}
diff --git a/web/src/components/MonthlyBudgetsCell/convertData.ts b/web/src/components/MonthlyBudgetsCell/convertData.ts
deleted file mode 100644
index 275da90..0000000
--- a/web/src/components/MonthlyBudgetsCell/convertData.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { FindBudgetByMonth } from 'types/graphql'
-
-import type { MonthlyBudget } from 'src/components/Spreadsheet/columns'
-
-type MonthlyBudgetGQL = FindBudgetByMonth['budget']
-type MonthlyBudgetGQLIntoDisplayable = (
- budget: MonthlyBudgetGQL
-) => MonthlyBudget[]
-
-// TODO do it on the server side
-export const convertBudgetGQLIntoDisplayable: MonthlyBudgetGQLIntoDisplayable =
- (budget: MonthlyBudgetGQL) => {
- const groups = budget.budgetCategoryGroups
- return groups.map((group) => {
- const categories = group.budgetCategories
- const subRows = categories.map((category) => {
- return {
- id: category.id,
- category: category.name,
- assigned: category.monthlyBudgetPerCategory[0]?.assigned || 0,
- activity:
- category.monthlyBudgetPerCategory[0]?.monthlyCategoryActivity
- .activity || 0,
- available:
- category.monthlyBudgetPerCategory[0]?.monthlyCategoryActivity
- .available || 0,
- }
- })
-
- return {
- id: group.id,
- category: group.name,
- assigned: group.monthlyCategoryGroupActivity[0]?.assigned || 0,
- activity: group.monthlyCategoryGroupActivity[0]?.activity || 0,
- available: group.monthlyCategoryGroupActivity[0]?.available || 0,
- subRows,
- }
- })
- }
diff --git a/web/src/components/Spreadsheet/cells.tsx b/web/src/components/Spreadsheet/cells.tsx
index 8b0c580..0eeaacc 100644
--- a/web/src/components/Spreadsheet/cells.tsx
+++ b/web/src/components/Spreadsheet/cells.tsx
@@ -2,9 +2,12 @@ import { useState, useEffect } from 'react'
import { type Row, type Getter } from '@tanstack/react-table'
import { ChevronDown, ChevronRight } from 'lucide-react'
+import type { FindBudgetByMonth } from 'types/graphql'
+import { useLocation } from '@redwoodjs/router'
import { useMutation } from '@redwoodjs/web'
+import { useAuth } from 'src/auth'
import { QUERY as FIND_THIS_MONTH_BUDGET } from 'src/components/MonthlyBudgetsCell'
import { useSelectedMonth, useSelectedYear } from 'src/lib/store'
@@ -93,12 +96,12 @@ export const CEditableCurrency = ({
setValue(initialValue)
}, [initialValue])
+ const userId = useAuth().currentUser?.id
+ const budgetId = useLocation().pathname.split('/').pop()
const month = useSelectedMonth()
const year = useSelectedYear()
- const [updateAssignedBudgetForCategory] = useMutation(UPDATE_BUDGET, {
- refetchQueries: [FIND_THIS_MONTH_BUDGET],
- })
+ const [updateAssignedBudgetForCategory] = useMutation(UPDATE_BUDGET)
const onBlur = () => {
setValue(format(value))
@@ -110,6 +113,59 @@ export const CEditableCurrency = ({
year,
input: { assigned: convertToFloat(value) },
},
+ update: (cache) => {
+ const existingData = cache.readQuery({
+ query: FIND_THIS_MONTH_BUDGET,
+ variables: {
+ month,
+ year,
+ budgetId,
+ userId,
+ },
+ })
+
+ const { parent, me } = (existingData?.monthlyBudget || []).reduce(
+ (acc, group) => {
+ if (!acc.parent) {
+ const subRow = group.subRows.find(
+ (subRow) => subRow.id === row.original.id
+ )
+ if (subRow) {
+ acc.parent = group
+ acc.me = subRow
+ }
+ }
+ return acc
+ },
+ { parent: null, me: null }
+ )
+
+ const changes = convertToFloat(value) - me.assigned
+
+ cache.modify({
+ id: cache.identify(me),
+ fields: {
+ assigned(cachedAssigned) {
+ return cachedAssigned + changes
+ },
+ available(cachedAvailable) {
+ return cachedAvailable + changes
+ },
+ },
+ })
+
+ cache.modify({
+ id: cache.identify(parent),
+ fields: {
+ assigned(cachedAssigned) {
+ return cachedAssigned + changes
+ },
+ available(cachedAvailable) {
+ return cachedAvailable + changes
+ },
+ },
+ })
+ },
})
}
}
diff --git a/web/src/components/Spreadsheet/columns.tsx b/web/src/components/Spreadsheet/columns.tsx
index e1ecc5f..1da8851 100644
--- a/web/src/components/Spreadsheet/columns.tsx
+++ b/web/src/components/Spreadsheet/columns.tsx
@@ -12,6 +12,7 @@ import { HExpand, HCheckbox } from './header'
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type MonthlyBudget = {
+ __typename?: string
id: string
category: string
assigned: number