feat: budget crud actions

This commit is contained in:
Max Koon
2025-12-06 23:02:28 -05:00
parent 76f2a43bd0
commit 27f6e627d4
20 changed files with 445 additions and 113 deletions

View File

@@ -1,3 +1,4 @@
import { relations } from "drizzle-orm";
import {
boolean,
decimal,
@@ -87,4 +88,17 @@ export const category = pgTable("category", {
createdBy: text("created_by").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
removedBy: text("removed_by"),
removedAt: timestamp("removed_at"),
});
export const budgetRelations = relations(budget, ({ many }) => ({
categories: many(category),
}));
export const categoryRelations = relations(category, ({ one }) => ({
budget: one(budget, {
fields: [category.budgetId],
references: [budget.id],
}),
}));

View File

@@ -8,10 +8,10 @@ type Tx = Transaction<Schema>;
export function createMutators(authData: AuthData | null) {
return {
link: {
async create() {},
async get(tx: Tx, { link_token }: { link_token: string }) {},
async updateTransactions() {},
async updateBalences() {},
async create() { },
async get(tx: Tx, { link_token }: { link_token: string }) { },
async updateTransactions() { },
async updateBalences() { },
async deleteAccounts(tx: Tx, { accountIds }: { accountIds: string[] }) {
isLoggedIn(authData);
for (const id of accountIds) {
@@ -40,7 +40,10 @@ export function createMutators(authData: AuthData | null) {
},
},
budget: {
async create(tx: Tx, { id }: { id: string }) {
async create(
tx: Tx,
{ id, categoryId }: { id: string; categoryId: string },
) {
isLoggedIn(authData);
await tx.mutate.budget.insert({
id,
@@ -48,6 +51,60 @@ export function createMutators(authData: AuthData | null) {
label: "New Budget",
createdBy: authData.user.id,
});
await tx.mutate.category.insert({
id: categoryId,
budgetId: id,
amount: 0,
every: "week",
order: 1000,
label: "My category",
color: "#f06",
createdBy: authData.user.id,
});
},
async delete(tx: Tx, { id }: { id: string }) {
isLoggedIn(authData);
await tx.mutate.budget.delete({
id,
});
},
async createCategory(
tx: Tx,
{
id,
budgetId,
order,
}: { id: string; budgetId: string; order?: number },
) {
isLoggedIn(authData);
tx.mutate.category.insert({
id,
budgetId,
amount: 0,
every: "week",
order: order || 0,
label: "My category",
color: "#f06",
createdBy: authData.user.id,
});
},
async deleteCategory(tx: Tx, { id }: { id: string }) {
isLoggedIn(authData);
tx.mutate.category.update({
id,
removedAt: new Date().getTime(),
removedBy: authData.user.id,
});
},
async updateCategory(
tx: Tx,
{ id, label }: { id: string; label: string },
) {
isLoggedIn(authData);
tx.mutate.category.update({
id,
label,
});
},
},
} as const;

View File

@@ -65,7 +65,11 @@ export const queries = {
z.tuple([]),
(authData: AuthData | null) => {
isLoggedIn(authData);
return builder.budget.limit(10);
return builder.budget
.related("categories", (q) =>
q.where("removedAt", "IS", null).orderBy("order", "desc"),
)
.limit(10);
},
),
getBudgetCategories: syncedQueryWithContext(

View File

@@ -273,6 +273,26 @@ export const schema = {
>,
serverName: "updated_at",
},
removedBy: {
type: "string",
optional: true,
customType: null as unknown as ZeroCustomType<
ZeroSchema,
"category",
"removedBy"
>,
serverName: "removed_by",
},
removedAt: {
type: "number",
optional: true,
customType: null as unknown as ZeroCustomType<
ZeroSchema,
"category",
"removedAt"
>,
serverName: "removed_at",
},
},
primaryKey: ["id"],
},
@@ -582,7 +602,28 @@ export const schema = {
serverName: "user",
},
},
relationships: {},
relationships: {
budget: {
categories: [
{
sourceField: ["id"],
destField: ["budgetId"],
destSchema: "category",
cardinality: "many",
},
],
},
category: {
budget: [
{
sourceField: ["budgetId"],
destField: ["id"],
destSchema: "budget",
cardinality: "one",
},
],
},
},
enableLegacyQueries: false,
enableLegacyMutators: false,
} as const;