Give Income a Null Category
Open:
client/src/components/EntryForm.jsx
server/src/routes/transactions.js
server/data/db.json
The starter stores the text Income in the category field for income transactions. That is a data-model shortcut: category is meant to organize expenses, while type already tells us whether the transaction is income or expense.
Make the relationship explicit:
{
type: "income",
category: null,
}
An expense still has a real category:
{
type: "expense",
category: "Food",
}
When the type changes to income, clear the category in the form:
function setType(type) {
setForm((current) => ({
...current,
type,
category: type === "income"
? ""
: (current.category || categories[0]?.name || ""),
}));
}
When the request object is built, convert that empty form category into null:
const entry = {
date: form.date,
description: form.description.trim(),
category: form.type === "income" ? null : form.category,
amount: Number(form.amount),
type: form.type,
};
The server should apply the same rule when it creates the stored object:
const transaction = {
id: randomUUID(),
date: req.body.date,
description: req.body.description.trim(),
category: req.body.type === "income"
? null
: req.body.category.trim(),
amount: req.body.amount,
type: req.body.type,
};
Update the seeded income records in server/data/db.json too. Existing data has to obey the new rule, not just future entries.
This simplifies later calculations: expense grouping can ignore income because there is literally no income category to consider.
Test it
Add a new income entry and inspect the response in the Network panel.
Then inspect server/data/db.json. The new record should have:
{
"type": "income",
"category": null
}
Add an expense and confirm that its category still contains a real category name.
Checkpoint
The transaction model now expresses meaning directly: income has no category, while every expense points to an expense category.