From efb5f61e243476b437dd41adae91840aaad8bdd1 Mon Sep 17 00:00:00 2001 From: Laith Date: Thu, 13 Mar 2025 04:03:57 +0300 Subject: [PATCH 1/7] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20moved=20balan?= =?UTF-8?q?ce=20logic=20to=20BalanceContext=20for=20cleaner=20state=20mana?= =?UTF-8?q?gement,=20dropped=20quickdb=20to=20pstg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lockb | Bin 318798 -> 359436 bytes docker-compose.yml | 2 +- drizzle.config.ts | 27 +++ drizzle/0000_melodic_purifiers.sql | 12 ++ drizzle/0001_yellow_thena.sql | 5 + drizzle/0002_icy_phil_sheldon.sql | 1 + drizzle/meta/0000_snapshot.json | 89 ++++++++ drizzle/meta/0001_snapshot.json | 98 +++++++++ drizzle/meta/0002_snapshot.json | 98 +++++++++ drizzle/meta/_journal.json | 27 +++ package.json | 14 ++ src/app/api/auth/itsme/route.ts | 37 ++-- src/app/api/auth/login/route.ts | 28 ++- src/app/api/auth/token/route.ts | 25 +++ src/app/api/transactions/route.ts | 255 +++++++++-------------- src/components/extra/NewTransaction.tsx | 263 +++++++++++++----------- src/components/ui/button.tsx | 58 ++++++ src/components/ui/calendar.tsx | 75 +++++++ src/components/ui/date.tsx | 192 +++++++++++++++++ src/components/ui/form.tsx | 167 +++++++++++++++ src/components/ui/label.tsx | 24 +++ src/components/ui/popover.tsx | 48 +++++ src/components/ui/scroll-area.tsx | 58 ++++++ src/contexts/BalanceContext.tsx | 52 ++--- src/lib/db.ts | 25 +++ src/schema/dbSchema.ts | 37 ++++ src/schema/transactionForm.ts | 45 ++-- 27 files changed, 1411 insertions(+), 351 deletions(-) create mode 100644 drizzle.config.ts create mode 100644 drizzle/0000_melodic_purifiers.sql create mode 100644 drizzle/0001_yellow_thena.sql create mode 100644 drizzle/0002_icy_phil_sheldon.sql create mode 100644 drizzle/meta/0000_snapshot.json create mode 100644 drizzle/meta/0001_snapshot.json create mode 100644 drizzle/meta/0002_snapshot.json create mode 100644 drizzle/meta/_journal.json create mode 100644 src/app/api/auth/token/route.ts create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/date.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/lib/db.ts create mode 100644 src/schema/dbSchema.ts diff --git a/bun.lockb b/bun.lockb index 1b62af36423e2a48a93a987a80286de669da5482..591e85563a795cdc9bd26164feee033e7975ed3e 100644 GIT binary patch delta 86824 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 K(BTHj-~1174}Kj0 diff --git a/docker-compose.yml b/docker-compose.yml index 044469e..c3fdd35 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: oopsbudgeter: - image: ghcr.io/oopsapps/oopsbudgeter:latest + image: iconical/oopsbudgeter:latest ports: - "3333:3000" environment: diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..7f92dc8 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Laith Alkhaddam aka Iconical or Sleepyico. + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import "dotenv/config"; +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + out: "./drizzle", + schema: "./src/schema/dbSchema.ts", + dialect: "postgresql", + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); diff --git a/drizzle/0000_melodic_purifiers.sql b/drizzle/0000_melodic_purifiers.sql new file mode 100644 index 0000000..1dfbc8a --- /dev/null +++ b/drizzle/0000_melodic_purifiers.sql @@ -0,0 +1,12 @@ +CREATE TABLE "balance" ( + "id" serial PRIMARY KEY NOT NULL, + "amount" real DEFAULT 0 NOT NULL +); +--> statement-breakpoint +CREATE TABLE "transactions" ( + "id" serial PRIMARY KEY NOT NULL, + "type" text NOT NULL, + "amount" real NOT NULL, + "description" text, + "date" timestamp DEFAULT now() +); diff --git a/drizzle/0001_yellow_thena.sql b/drizzle/0001_yellow_thena.sql new file mode 100644 index 0000000..def5fa1 --- /dev/null +++ b/drizzle/0001_yellow_thena.sql @@ -0,0 +1,5 @@ +CREATE TYPE "public"."TransactionType" AS ENUM('income', 'expense');--> statement-breakpoint +ALTER TABLE "transactions" ALTER COLUMN "type" SET DATA TYPE TransactionType;--> statement-breakpoint +ALTER TABLE "transactions" ALTER COLUMN "type" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "transactions" ALTER COLUMN "date" DROP DEFAULT;--> statement-breakpoint +ALTER TABLE "transactions" ALTER COLUMN "date" SET NOT NULL; \ No newline at end of file diff --git a/drizzle/0002_icy_phil_sheldon.sql b/drizzle/0002_icy_phil_sheldon.sql new file mode 100644 index 0000000..5149e61 --- /dev/null +++ b/drizzle/0002_icy_phil_sheldon.sql @@ -0,0 +1 @@ +ALTER TABLE "balance" RENAME TO "currentBalance"; \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..649dd59 --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,89 @@ +{ + "id": "e63dd89c-b041-48cb-b8ca-98dd2c1ca136", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.balance": { + "name": "balance", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.transactions": { + "name": "transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..6e9d90c --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,98 @@ +{ + "id": "8706f857-006e-48fb-9887-e2f38e0c49ad", + "prevId": "e63dd89c-b041-48cb-b8ca-98dd2c1ca136", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.balance": { + "name": "balance", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.transactions": { + "name": "transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "TransactionType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.TransactionType": { + "name": "TransactionType", + "schema": "public", + "values": [ + "income", + "expense" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..acc0fc4 --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,98 @@ +{ + "id": "107362f2-b762-4112-ac7d-57cedc4b52cd", + "prevId": "8706f857-006e-48fb-9887-e2f38e0c49ad", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.currentBalance": { + "name": "currentBalance", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.transactions": { + "name": "transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "TransactionType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.TransactionType": { + "name": "TransactionType", + "schema": "public", + "values": [ + "income", + "expense" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..202e55e --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,27 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1741735509151, + "tag": "0000_melodic_purifiers", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1741817547280, + "tag": "0001_yellow_thena", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1741819338790, + "tag": "0002_icy_phil_sheldon", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index ab06917..069d43d 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,18 @@ "@hookform/resolvers": "^4.1.3", "@iconify/react": "^5.2.0", "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-slot": "^1.1.2", "better-sqlite3": "^11.8.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "dotenv": "^16.4.7", + "drizzle-kit": "^0.30.5", + "drizzle-orm": "^0.40.0", + "drizzle-zod": "^0.7.0", "input-otp": "^1.4.2", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.477.0", @@ -24,14 +33,17 @@ "mysql2": "^3.13.0", "next": "15.2.1", "next-themes": "^0.4.4", + "pg": "^8.14.0", "quick.db": "^9.1.7", "react": "^19.0.0", + "react-day-picker": "8.10.1", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", "react-to-print": "^3.0.5", "sonner": "^2.0.1", "tailwind-merge": "^3.0.2", "tailwindcss-animate": "^1.0.7", + "timescape": "^0.7.1", "tsx": "^4.19.3", "vaul": "^1.1.2", "write-file-atomic": "^6.0.0", @@ -40,8 +52,10 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", + "@types/date-fns": "^2.6.3", "@types/jsonwebtoken": "^9.0.9", "@types/node": "^20", + "@types/pg": "^8.11.11", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", diff --git a/src/app/api/auth/itsme/route.ts b/src/app/api/auth/itsme/route.ts index 72daddf..c37174d 100644 --- a/src/app/api/auth/itsme/route.ts +++ b/src/app/api/auth/itsme/route.ts @@ -21,30 +21,29 @@ import { cookies } from "next/headers"; const SECRET = process.env.JWT_SECRET as string; -export const config = { - runtime: "nodejs", -}; - export async function GET() { - const token = (await cookies()).get("authToken"); - if (!token) { - return NextResponse.json({ message: "Not authenticated" }, { status: 401 }); - } - try { - const decoded = jwt.verify(token?.value, SECRET); - return NextResponse.json({ message: "Authenticated", user: decoded }); - } catch (err) { - if (err instanceof Error) { - return NextResponse.json( - { message: "Invalid or expired token", error: err.message }, - { status: 401 } - ); - } else { + const cookieStore = await cookies(); + const token = cookieStore.get("authToken")?.value; + + if (!token) { return NextResponse.json( - { message: "Invalid or expired token", error: "Unknown error" }, + { message: "Not authenticated" }, { status: 401 } ); } + + // Verify JWT Token + const decoded = jwt.verify(token, SECRET); + + return NextResponse.json({ + message: "Authenticated", + user: decoded, + }); + } catch (err) { + return NextResponse.json( + { message: "Invalid or expired token", error: (err as Error).message }, + { status: 401 } + ); } } diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 50870c4..1e8f9f3 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -17,32 +17,42 @@ import { NextResponse } from "next/server"; import jwt from "jsonwebtoken"; +import { cookies } from "next/headers"; const SECRET = process.env.JWT_SECRET as string; +const PASSCODE = process.env.PASSCODE as string; export async function POST(request: Request) { - const { passcode } = await request.json(); + try { + const cookieStore = await cookies(); + const { passcode } = await request.json(); - const correctPasscode = process.env.PASSCODE; + if (passcode !== PASSCODE) { + return NextResponse.json( + { message: "Incorrect passcode" }, + { status: 401 } + ); + } - if (passcode === correctPasscode) { const token = jwt.sign({ user: "authenticated" }, SECRET, { expiresIn: "7d", }); - const response = NextResponse.json({ message: "Login successful" }); - response.cookies.set("authToken", token, { + cookieStore.set({ + name: "authToken", + value: token, httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "strict", maxAge: 7 * 24 * 60 * 60, + path: "/", }); - return response; - } else { + return NextResponse.json({ message: "Login successful", token }); + } catch (error) { return NextResponse.json( - { message: "Incorrect passcode" }, - { status: 401 } + { message: "Server error", error: (error as Error).message }, + { status: 500 } ); } } diff --git a/src/app/api/auth/token/route.ts b/src/app/api/auth/token/route.ts new file mode 100644 index 0000000..83c1254 --- /dev/null +++ b/src/app/api/auth/token/route.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Laith Alkhaddam aka Iconical or Sleepyico. + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { cookies } from "next/headers"; +import { NextResponse } from "next/server"; + +export async function GET() { + const cookieStore = await cookies(); + const token = cookieStore.get("authToken")?.value || null; + return NextResponse.json({ token }); +} diff --git a/src/app/api/transactions/route.ts b/src/app/api/transactions/route.ts index ba7047d..15972db 100644 --- a/src/app/api/transactions/route.ts +++ b/src/app/api/transactions/route.ts @@ -1,13 +1,13 @@ /* * Copyright (c) 2025 Laith Alkhaddam aka Iconical or Sleepyico. * All rights reserved. - + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - + * * http://www.apache.org/licenses/LICENSE-2.0 - + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,208 +15,139 @@ * limitations under the License. */ -import { Transaction } from "@/types/Transaction"; +import { db } from "@/lib/db"; +import { transactions } from "@/schema/dbSchema"; +import { eq } from "drizzle-orm"; import { cookies } from "next/headers"; -import { NextResponse } from "next/server"; -import { QuickDB } from "quick.db"; +import { NextRequest, NextResponse } from "next/server"; import jwt from "jsonwebtoken"; -const db = new QuickDB(); const SECRET = process.env.JWT_SECRET as string; -const BASE_URL = process.env.NEXT_PUBLIC_URL as string; - -export async function GET() { - const response = new NextResponse(); - - response.headers.set("Access-Control-Allow-Origin", BASE_URL); - response.headers.set("Access-Control-Allow-Methods", "GET, POST, DELETE"); - response.headers.set( - "Access-Control-Allow-Headers", - "Content-Type, Authorization" - ); +async function verifyToken(req: NextRequest) { const cookieStore = await cookies(); - const token = cookieStore.get("authToken"); - - if (!token) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } + const tokenFromCookie = cookieStore.get("authToken")?.value; - try { - const decoded = jwt.verify(token.value, SECRET); + const authHeader = req.headers.get("Authorization"); + const tokenFromHeader = + authHeader && authHeader.startsWith("Bearer ") + ? authHeader.split(" ")[1] + : null; - const transactions = await db.get("transactions"); - const currentBalance = await db.get("balance"); + const token = tokenFromHeader || tokenFromCookie; - response.headers.set("Content-Type", "application/json"); + if (!token) return { authorized: false, error: "Unauthorized" }; - return new NextResponse( - JSON.stringify({ - user: decoded, - transactions, - currentBalance, - }), - { status: 200 } - ); + try { + const decoded = jwt.verify(token, SECRET); + return { authorized: true, user: decoded }; } catch (err) { - if (err instanceof Error) { - response.headers.set("Content-Type", "application/json"); - - return NextResponse.json( - { message: "Invalid or expired token", error: err.message }, - { status: 401 } - ); - } else { - response.headers.set("Content-Type", "application/json"); - return NextResponse.json( - { message: "Invalid or expired token", error: "Unknown error" }, - { status: 401 } - ); - } + return { + authorized: false, + error: `Invalid or expired token, ${(err as Error).message}`, + }; } } -export async function POST(request: Request) { - const response = new NextResponse(); - response.headers.set("Access-Control-Allow-Origin", BASE_URL); - response.headers.set("Access-Control-Allow-Methods", "GET, POST, DELETE"); - response.headers.set( - "Access-Control-Allow-Headers", - "Content-Type, Authorization" - ); +export async function GET(req: NextRequest) { + const { authorized, user, error } = await verifyToken(req); - const cookieStore = await cookies(); - const token = cookieStore.get("authToken"); - - if (!token) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); + if (!authorized) { + return NextResponse.json({ message: error }, { status: 401 }); } try { - const { type, amount, description, date }: Transaction = - await request.json(); - - let currentBalance = (await db.get("balance")) || 0; + const transactionsList = await db.select().from(transactions); - if (type === "income") { - currentBalance += amount; - } else if (type === "expense") { - currentBalance -= amount; - } - - let transactionId = (await db.get("transactionId")) || 0; - transactionId++; - - const transaction: Transaction = { - tid: transactionId, - type, - amount, - description: description || "No description provided", - date, - }; - - const transactions = (await db.get("transactions")) || []; - - transactions.push(transaction); + return NextResponse.json({ + user, + transactions: transactionsList, + }); + } catch (err) { + return NextResponse.json( + { + message: "Failed to fetch transactions", + error: (err as Error).message, + }, + { status: 500 } + ); + } +} - await db.set("transactions", transactions); - await db.set("transactionId", transactionId); +export async function POST(req: NextRequest) { + const { authorized, user, error } = await verifyToken(req); - await db.set("balance", currentBalance); + if (!authorized) { + return NextResponse.json({ message: error }, { status: 401 }); + } - return new NextResponse( - JSON.stringify({ + try { + const { type, amount, description, date } = await req.json(); + + const newTransaction = await db + .insert(transactions) + .values({ + type, + amount, + description: description || "No description provided", + date: new Date(date), + }) + .returning(); + + return NextResponse.json( + { + user, message: "Transaction added", - transaction, - currentBalance, - }), + transaction: newTransaction[0], + }, { status: 201 } ); } catch (err) { - if (err instanceof Error) { - response.headers.set("Content-Type", "application/json"); - return NextResponse.json( - { message: "Failed to add a new transaction:", error: err.message }, - { status: 401 } - ); - } else { - response.headers.set("Content-Type", "application/json"); - return NextResponse.json( - { message: "Failed to add a new transaction:", error: "Unknown error" }, - { status: 401 } - ); - } + return NextResponse.json( + { message: "Failed to add transaction", error: (err as Error).message }, + { status: 500 } + ); } } -export async function DELETE(request: Request) { - const response = new NextResponse(); - - response.headers.set("Access-Control-Allow-Origin", BASE_URL); - response.headers.set("Access-Control-Allow-Methods", "GET, POST, DELETE"); - response.headers.set( - "Access-Control-Allow-Headers", - "Content-Type, Authorization" - ); - - const cookieStore = await cookies(); - const token = cookieStore.get("authToken"); +export async function DELETE(req: NextRequest) { + const { authorized, user, error } = await verifyToken(req); - if (!token) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); + if (!authorized) { + return NextResponse.json({ message: error }, { status: 401 }); } try { - const { tid }: { tid: number } = await request.json(); + const { id } = await req.json(); - const transactions = (await db.get("transactions")) || []; + const transactionToDelete = await db + .select() + .from(transactions) + .where(eq(transactions.id, id)); - const transactionToDelete = transactions.find( - (transaction: Transaction) => transaction.tid === tid - ); - - if (!transactionToDelete) { - return new NextResponse( - JSON.stringify({ message: "Transaction not found" }), + if (!transactionToDelete.length) { + return NextResponse.json( + { message: "Transaction not found" }, { status: 404 } ); } - const updatedTransactions = transactions.filter( - (transaction: Transaction) => transaction.tid !== tid - ); - - let currentBalance = await db.get("balance"); - - if (transactionToDelete.type === "income") { - currentBalance -= transactionToDelete.amount; - } else if (transactionToDelete.type === "expense") { - currentBalance += transactionToDelete.amount; - } - - await db.set("transactions", updatedTransactions); - await db.set("balance", currentBalance); + await db.delete(transactions).where(eq(transactions.id, id)); - return new NextResponse( - JSON.stringify({ + return NextResponse.json( + { + user, message: "Transaction deleted successfully", - currentBalance, - }), + }, { status: 200 } ); } catch (err) { - if (err instanceof Error) { - response.headers.set("Content-Type", "application/json"); - return NextResponse.json( - { message: "Deletion was not successful", error: err.message }, - { status: 401 } - ); - } else { - response.headers.set("Content-Type", "application/json"); - return NextResponse.json( - { message: "Deletion was not successful", error: "Unknown error" }, - { status: 401 } - ); - } + return NextResponse.json( + { + message: "Failed to delete transaction", + error: (err as Error).message, + }, + { status: 500 } + ); } } diff --git a/src/components/extra/NewTransaction.tsx b/src/components/extra/NewTransaction.tsx index 89f42c5..86ade21 100644 --- a/src/components/extra/NewTransaction.tsx +++ b/src/components/extra/NewTransaction.tsx @@ -20,77 +20,60 @@ import React, { useState } from "react"; import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer"; import HoverEffect from "../effects/HoverEffect"; import { Icon } from "@iconify/react"; -import { transactionSchema } from "@/schema/transactionForm"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useBalance } from "@/contexts/BalanceContext"; import { Input } from "../ui/input"; import { Textarea } from "../ui/textarea"; - -interface TransactionFormData { - type: "income" | "expense"; - amount: number; - description?: string; - date?: string; -} +import { + insertTransactionSchema, + insertTransactionType, +} from "@/schema/transactionForm"; +import { format } from "date-fns"; +import { Form, FormItem, FormField, FormLabel } from "../ui/form"; +import { DatetimePicker } from "../ui/date"; export default function NewTransaction() { const [successMessage, setSuccessMessage] = useState(""); - const { setCurrentBalance } = useBalance(); + const { refreshBalance } = useBalance(); const [type, setType] = useState<"income" | "expense">("income"); const [popupVisible, setPopupVisible] = useState(false); const handleToggle = (selectedType: "income" | "expense") => { - console.log("Selected type:", selectedType); setType(selectedType); - setValue("type", selectedType); + form.setValue("type", selectedType); }; - const showPopup = () => { - setPopupVisible(true); - setTimeout(() => { - setPopupVisible(false); - }, 3000); + const showPopup = (message: string) => { + setSuccessMessage(message); + setPopupVisible(true); + setTimeout(() => setPopupVisible(false), 3000); }; - const { - register, - handleSubmit, - formState: { errors }, - setValue, - reset, - } = useForm({ - resolver: zodResolver(transactionSchema), + const form = useForm({ + resolver: zodResolver(insertTransactionSchema), defaultValues: { - date: new Date().toISOString().slice(0, 16), + date: format(new Date(), "yyyy-MM-dd'T'HH:mm:ss"), }, }); - const onSubmit = async (data: TransactionFormData) => { - const formData = { ...data }; - + const onSubmit = async (data: insertTransactionType) => { try { const response = await fetch("/api/transactions", { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify(formData), + body: JSON.stringify(data), }); - const result = await response.json(); if (response.ok) { - setSuccessMessage("Transaction added successfully!"); - setCurrentBalance(result.currentBalance); - reset(); - showPopup(); - } else { - setSuccessMessage(`Error: ${result.message}`); - showPopup(); + refreshBalance(); + form.reset(); + showPopup("Transaction added successfully!"); } } catch (err) { - setSuccessMessage(`Something went wrong: ${err}`); - showPopup(); + showPopup(`Something went wrong: ${err}`); } }; @@ -104,90 +87,134 @@ export default function NewTransaction() {

-
-
- - -
- - - -
-
- - - value === "" ? 0 : parseFloat(value), - })} + + + ( + + + + + + + )} + /> + +
+ ( + + Amount + + value === "" ? value : parseFloat(value), + })} + /> + + )} /> -
-
- -