Skip to content

Commit

Permalink
edit transaction modal
Browse files Browse the repository at this point in the history
  • Loading branch information
itsbekas committed Jan 15, 2025
1 parent b3be579 commit b667847
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 2 deletions.
7 changes: 7 additions & 0 deletions frontend/app/components/TransactionsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState } from "react";
import cx from "clsx";
import { ScrollArea, Table } from "@mantine/core";
import classes from "~/styles/TransactionsTable.module.css";
import { EditTransactionModal } from "~/components/modals/EditTransactionModal";

type SubCategory = {
id: string;
Expand Down Expand Up @@ -58,6 +59,12 @@ export function TransactionsTable({
<Table.Td>{transaction.description}</Table.Td>
<Table.Td>{categoryName}</Table.Td>
<Table.Td>{transaction.amount.toFixed(2)}</Table.Td>
<Table.Td>
<EditTransactionModal
subCategories={categories}
transaction={transaction}
/>
</Table.Td>
</Table.Tr>
);
});
Expand Down
88 changes: 88 additions & 0 deletions frontend/app/components/modals/EditTransactionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useState } from "react";
import { useDisclosure } from "@mantine/hooks";
import { Modal, Button, Select, Group, ActionIcon } from "@mantine/core";
import { useFetcher } from "react-router";
import { IconDots } from "@tabler/icons-react";

type SubCategory = {
id: string;
name: string;
};

type Transaction = {
id: string;
account_id: string;
amount: number;
date: string;
description: string;
counterparty: string;
subcategory_id: string;
user_description: string;
};

type EditTransactionModalProps = {
subCategories: SubCategory[]; // List of available sub-categories
transaction: Transaction; // ID of the transaction being edited
};

export function EditTransactionModal({
subCategories,
transaction,
}: EditTransactionModalProps) {
const [opened, { open, close }] = useDisclosure(false);
const [selectedSubCategory, setSelectedSubCategory] = useState<string | null>(
null
);
const fetcher = useFetcher();

const handleSubmit = () => {
if (!selectedSubCategory) {
return;
}

// Use fetcher.submit with JSON encoding
fetcher.submit(
{
account_id: transaction.account_id,
transaction_id: transaction.id,
subcategory_id: selectedSubCategory,
action: "editTransaction",
},
{ method: "post", encType: "application/json" }
);

// Close modal on success (optionally handle fetcher.state or errors)
close();
};

return (
<>
<Modal opened={opened} onClose={close} title="Edit Transaction" centered>
<Select
label="Select Sub-Category"
placeholder="Choose a sub-category"
data={subCategories.map((subCategory) => ({
value: subCategory.id,
label: subCategory.name,
}))}
value={selectedSubCategory}
onChange={setSelectedSubCategory}
required
mb="sm"
/>
<Group align="right" mt="md">
<Button variant="default" onClick={close}>
Cancel
</Button>
<Button onClick={handleSubmit} loading={fetcher.state !== "idle"}>
Save
</Button>
</Group>
</Modal>

<ActionIcon onClick={open} size="compact-xs" variant="subtle">
<IconDots size={16} stroke={1.5} />
</ActionIcon>
</>
);
}
69 changes: 67 additions & 2 deletions frontend/app/routes/dashboard.finance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@ export async function action({ request }: { request: Request }) {

if (contentType === "application/json") {
const body = await request.json();
const { action, name, amount, category_id, bank_id, account_id } = body;
const {
action,
name,
amount,
category_id,
bank_id,
account_id,
transaction_id,
subcategory_id,
} = body;

switch (action) {
case "createCategory": {
Expand Down Expand Up @@ -220,6 +229,62 @@ export async function action({ request }: { request: Request }) {
}
}

case "editTransaction": {
if (!transaction_id || !subcategory_id) {
return new Response(
JSON.stringify({
error: "Transaction ID and Sub-category ID are required.",
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}

try {
const response = await fetchWithAuth(
`/finance/bank/${account_id}/transactions/${transaction_id}`,
{
method: "PUT",
body: JSON.stringify({
description: "",
subcategory_id,
amount: -100,
}),
},
request
);

if (!response.ok) {
const errorData = await response.json();
return new Response(
JSON.stringify({
error: errorData.message || "Failed to edit transaction.",
}),
{
status: response.status,
headers: { "Content-Type": "application/json" },
}
);
}

return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (err) {
console.error(err);
return new Response(
JSON.stringify({ error: "An unexpected error occurred." }),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
}

default:
return new Response(JSON.stringify({ error: "Invalid action type." }), {
status: 400,
Expand Down Expand Up @@ -301,7 +366,7 @@ export default function FinancePage({ loaderData }: Route.ComponentProps) {

<Grid.Col span={8}>
<Stack gap="md">
<BankBalances balances={balances} banks={banks} />;
<BankBalances balances={balances} banks={banks} />
<TransactionsTable
transactions={transactions}
categories={categories.flatMap((c) => c.subcategories)}
Expand Down
27 changes: 27 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@react-router/fs-routes": "^7.1.1",
"@react-router/node": "^7.1.1",
"@react-router/serve": "^7.1.1",
"@tabler/icons-react": "^3.28.1",
"isbot": "^5.1.17",
"react": "^19.0.0",
"react-dom": "^19.0.0",
Expand Down
6 changes: 6 additions & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,10 @@ export default defineConfig({
},
},
plugins: [reactRouter(), tsconfigPaths()],
resolve: {
alias: {
// /esm/icons/index.mjs only exports the icons statically, so no separate chunks are created
"@tabler/icons-react": "@tabler/icons-react/dist/esm/icons/index.mjs",
},
},
});

0 comments on commit b667847

Please sign in to comment.