├── frontend
├── src
│ ├── constants
│ │ └── constant.js
│ ├── app
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── GeistVF.woff
│ │ │ └── GeistMonoVF.woff
│ │ ├── records
│ │ │ └── page.jsx
│ │ ├── globals.css
│ │ ├── layout.js
│ │ ├── page.jsx
│ │ ├── sign-up
│ │ │ └── page.jsx
│ │ └── dashboard
│ │ │ └── page.jsx
│ └── components
│ │ ├── svg
│ │ ├── BlueDot.jsx
│ │ ├── GreendDot.jsx
│ │ ├── Logo.jsx
│ │ ├── LogoBig.jsx
│ │ ├── LeftArrow.jsx
│ │ ├── RightArrow.jsx
│ │ ├── DownArrow.jsx
│ │ ├── index.jsx
│ │ ├── PlusIcon.jsx
│ │ ├── OpenEye.jsx
│ │ ├── BluePlusBig.jsx
│ │ ├── BluePlusIcon.jsx
│ │ ├── CloseIcon.jsx
│ │ ├── HomeIcon.jsx
│ │ ├── DrinkIcon.jsx
│ │ ├── CloseEye.jsx
│ │ ├── ArrowCircleUp.jsx
│ │ ├── ArrowCircleDown.jsx
│ │ ├── ShoppingIcon.jsx
│ │ ├── GeldBig.jsx
│ │ ├── Geld.jsx
│ │ ├── TaxiIcon.jsx
│ │ ├── FoodDrinkIcon.jsx
│ │ └── GiftIcon.jsx
│ │ ├── ui
│ │ ├── RecordLoader.jsx
│ │ ├── Category.jsx
│ │ ├── Loader.jsx
│ │ ├── CategoryOption.jsx
│ │ ├── Record.jsx
│ │ ├── ChooseCategory.jsx
│ │ ├── AddCategory.jsx
│ │ ├── HeaderAddRecord.jsx
│ │ └── AddRecord.jsx
│ │ ├── icons
│ │ ├── Vignette.jsx
│ │ ├── index.jsx
│ │ ├── IntersectSquare.jsx
│ │ ├── Home.jsx
│ │ ├── Pencil.jsx
│ │ ├── HouseLine.jsx
│ │ ├── ImageSquare.jsx
│ │ ├── Globe.jsx
│ │ ├── Exclude.jsx
│ │ ├── Microphone.jsx
│ │ ├── NumberSeven.jsx
│ │ ├── Anchor.jsx
│ │ ├── HourGlass.jsx
│ │ ├── Leaf.jsx
│ │ ├── Watch.jsx
│ │ ├── Peace.jsx
│ │ ├── Plus.jsx
│ │ ├── NotePad.jsx
│ │ ├── Question.jsx
│ │ ├── ListPlus.jsx
│ │ ├── Paper.jsx
│ │ ├── NumberFive.jsx
│ │ ├── Orange.jsx
│ │ ├── ExcellLogo.jsx
│ │ ├── Road.jsx
│ │ ├── Exam.jsx
│ │ ├── IdentificationBadge.jsx
│ │ ├── IdentificationCard.jsx
│ │ ├── Ladder.jsx
│ │ ├── BezierCurve.jsx
│ │ └── BaseBall.jsx
│ │ ├── components
│ │ └── Header.jsx
│ │ └── pages
│ │ ├── Dashboard.jsx
│ │ └── Records.jsx
├── public
│ ├── Noise.png
│ ├── vercel.svg
│ ├── window.svg
│ ├── file.svg
│ ├── globe.svg
│ └── next.svg
├── jsconfig.json
├── postcss.config.mjs
├── next.config.mjs
├── .gitignore
├── package.json
├── tailwind.config.js
└── README.md
├── README.md
└── backend
├── .env
├── .gitignore
├── package.json
└── server.js
/frontend/src/constants/constant.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crown7209/Geld/HEAD/README.md
--------------------------------------------------------------------------------
/frontend/public/Noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crown7209/Geld/HEAD/frontend/public/Noise.png
--------------------------------------------------------------------------------
/frontend/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crown7209/Geld/HEAD/frontend/src/app/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crown7209/Geld/HEAD/frontend/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crown7209/Geld/HEAD/frontend/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/backend/.env:
--------------------------------------------------------------------------------
1 | DATABASE_URL="postgresql://neondb_owner:jFEhGl9n8gVz@ep-little-bread-a17afw29.ap-southeast-1.aws.neon.tech/neondb?sslmode=require"
--------------------------------------------------------------------------------
/frontend/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/frontend/src/app/records/page.jsx:
--------------------------------------------------------------------------------
1 | import { RecordsPage } from "@/components/pages/Records";
2 |
3 | const Records = () => {
4 | return ;
5 | };
6 |
7 | export default Records;
8 |
--------------------------------------------------------------------------------
/frontend/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | env: {
4 | BACKEND_URL: process.env.BACKEND_URL || "",
5 | },
6 | };
7 |
8 | export default nextConfig;
9 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/BlueDot.jsx:
--------------------------------------------------------------------------------
1 | export const BlueDot = () => {
2 | return (
3 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/GreendDot.jsx:
--------------------------------------------------------------------------------
1 | export const GreenDot = () => {
2 | return (
3 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/frontend/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 | * {
8 | box-sizing: border-box;
9 | transition: all 0.5s;
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/RecordLoader.jsx:
--------------------------------------------------------------------------------
1 | export const RecordLoader = () => {
2 | return (
3 |
4 |
5 |
6 |
7 | Loading...
8 |
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/Category.jsx:
--------------------------------------------------------------------------------
1 | import { CloseEye, OpenEye } from "../svg";
2 |
3 | export const Category = ({ category }) => {
4 | return (
5 |
6 |
11 |
12 | {category.name}
13 |
14 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/Logo.jsx:
--------------------------------------------------------------------------------
1 | export const Logo = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/LogoBig.jsx:
--------------------------------------------------------------------------------
1 | export const LogoBig = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "1.0.0",
4 | "main": "server.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "start": "nodemon server.js"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "type": "module",
13 | "description": "",
14 | "dependencies": {
15 | "@neondatabase/serverless": "^0.10.1",
16 | "body-parser": "^1.20.3",
17 | "cors": "^2.8.5",
18 | "dotenv": "^16.4.5",
19 | "express": "^4.21.1",
20 | "fs": "^0.0.1-security",
21 | "nodemon": "^3.1.7"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for commiting if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "chart.js": "^4.4.5",
13 | "formik": "^2.4.6",
14 | "next": "15.0.0",
15 | "react": "^18.3.1",
16 | "react-dom": "^18.3.1",
17 | "react-toastify": "^10.0.6",
18 | "tailwind-scrollbar-hide": "^1.1.7",
19 | "yup": "^1.4.0"
20 | },
21 | "devDependencies": {
22 | "daisyui": "^4.12.13",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/LeftArrow.jsx:
--------------------------------------------------------------------------------
1 | export const LeftArrow = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/RightArrow.jsx:
--------------------------------------------------------------------------------
1 | export const RightArrow = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/DownArrow.jsx:
--------------------------------------------------------------------------------
1 | export const DownArrow = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/Loader.jsx:
--------------------------------------------------------------------------------
1 | import { GeldBig, LogoBig } from "../svg";
2 |
3 | export const Loader = () => {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Түр хүлээнэ үү...
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/index.jsx:
--------------------------------------------------------------------------------
1 | export * from "./ArrowCircleDown";
2 | export * from "./ArrowCircleUp";
3 | export * from "./BlueDot";
4 | export * from "./BluePlusIcon";
5 | export * from "./FoodDrinkIcon";
6 | export * from "./GreendDot";
7 | export * from "./HomeIcon";
8 | export * from "./LeftArrow";
9 | export * from "./Logo";
10 | export * from "./PlusIcon";
11 | export * from "./RightArrow";
12 | export * from "./DownArrow";
13 | export * from "./CloseIcon";
14 | export * from "./BluePlusBig";
15 | export * from "./GiftIcon";
16 | export * from "./DrinkIcon";
17 | export * from "./TaxiIcon";
18 | export * from "./ShoppingIcon";
19 | export * from "./CloseEye";
20 | export * from "./OpenEye";
21 | export * from "./Geld";
22 | export * from "./GeldBig";
23 | export * from "./LogoBig";
24 |
--------------------------------------------------------------------------------
/frontend/src/app/layout.js:
--------------------------------------------------------------------------------
1 | import localFont from "next/font/local";
2 | import "./globals.css";
3 |
4 | const geistSans = localFont({
5 | src: "./fonts/GeistVF.woff",
6 | variable: "--font-geist-sans",
7 | weight: "100 900",
8 | });
9 | const geistMono = localFont({
10 | src: "./fonts/GeistMonoVF.woff",
11 | variable: "--font-geist-mono",
12 | weight: "100 900",
13 | });
14 |
15 | export const metadata = {
16 | title: "Create Next App",
17 | description: "Generated by create next app",
18 | };
19 |
20 | export default function RootLayout({ children }) {
21 | return (
22 |
23 |
26 | {children}
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/CategoryOption.jsx:
--------------------------------------------------------------------------------
1 | export const CategoryOption = ({ category }) => {
2 | return (
3 |
4 |
18 |
19 | {category?.name}
20 |
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Vignette.jsx:
--------------------------------------------------------------------------------
1 | export const Vignette = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/index.jsx:
--------------------------------------------------------------------------------
1 | export * from "./Home";
2 | export * from "./Anchor";
3 | export * from "./BaseBall";
4 | export * from "./BezierCurve";
5 | export * from "./Exam";
6 | export * from "./ExcellLogo";
7 | export * from "./Exclude";
8 | export * from "./Globe";
9 | export * from "./HourGlass";
10 | export * from "./HouseLine";
11 | export * from "./IdentificationBadge";
12 | export * from "./IdentificationCard";
13 | export * from "./ImageSquare";
14 | export * from "./IntersectSquare";
15 | export * from "./Ladder";
16 | export * from "./Leaf";
17 | export * from "./ListPlus";
18 | export * from "./Microphone";
19 | export * from "./NotePad";
20 | export * from "./NumberFive";
21 | export * from "./NumberSeven";
22 | export * from "./Orange";
23 | export * from "./Paper";
24 | export * from "./Peace";
25 | export * from "./Pencil";
26 | export * from "./Plus";
27 | export * from "./Question";
28 | export * from "./Road";
29 | export * from "./Vignette";
30 | export * from "./Watch";
31 |
--------------------------------------------------------------------------------
/frontend/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/PlusIcon.jsx:
--------------------------------------------------------------------------------
1 | export const PlusIcon = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/OpenEye.jsx:
--------------------------------------------------------------------------------
1 | export const OpenEye = () => {
2 | return (
3 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/BluePlusBig.jsx:
--------------------------------------------------------------------------------
1 | export const BluePlusBig = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/IntersectSquare.jsx:
--------------------------------------------------------------------------------
1 | export const IntersectSquare = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/BluePlusIcon.jsx:
--------------------------------------------------------------------------------
1 | export const BluePlusIcon = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/CloseIcon.jsx:
--------------------------------------------------------------------------------
1 | export const CloseIcon = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Home.jsx:
--------------------------------------------------------------------------------
1 | export const Home = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Pencil.jsx:
--------------------------------------------------------------------------------
1 | export const Pencil = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/HouseLine.jsx:
--------------------------------------------------------------------------------
1 | export const HouseLine = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/ImageSquare.jsx:
--------------------------------------------------------------------------------
1 | export const ImageSquare = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Globe.jsx:
--------------------------------------------------------------------------------
1 | export const Globe = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | colors: {
11 | background: "var(--background)",
12 | foreground: "var(--foreground)",
13 | },
14 | fontFamily: {
15 | roboto: ["Roboto", "sans-serif"],
16 | },
17 | },
18 | },
19 | plugins: [require("daisyui")],
20 | daisyui: {
21 | themes: false, // false: only light + dark | true: all themes | array: specific themes like this ["light", "dark", "cupcake"]
22 | darkTheme: "light", // name of one of the included themes for dark mode
23 | base: true, // applies background color and foreground color for root element by default
24 | styled: true, // include daisyUI colors and design decisions for all components
25 | utils: true, // adds responsive and modifier utility classes
26 | prefix: "", // prefix for daisyUI classnames (components, modifiers and responsive class names. Not colors)
27 | logs: true, // Shows info about daisyUI version and used config in the console when building your CSS
28 | themeRoot: ":root", // The element that receives theme color CSS variables
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/HomeIcon.jsx:
--------------------------------------------------------------------------------
1 | export const HomeIcon = () => {
2 | return (
3 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Exclude.jsx:
--------------------------------------------------------------------------------
1 | export const Exclude = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Microphone.jsx:
--------------------------------------------------------------------------------
1 | export const Microphone = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/NumberSeven.jsx:
--------------------------------------------------------------------------------
1 | export const NumberSeven = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/DrinkIcon.jsx:
--------------------------------------------------------------------------------
1 | export const DrinkIcon = () => {
2 | return (
3 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Anchor.jsx:
--------------------------------------------------------------------------------
1 | export const Anchor = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/CloseEye.jsx:
--------------------------------------------------------------------------------
1 | export const CloseEye = () => {
2 | return (
3 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/HourGlass.jsx:
--------------------------------------------------------------------------------
1 | export const HourGlass = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 | # Geld
38 | # turshilt
39 | # turshilt
40 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Leaf.jsx:
--------------------------------------------------------------------------------
1 | export const Leaf = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Watch.jsx:
--------------------------------------------------------------------------------
1 | export const Watch = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/ArrowCircleUp.jsx:
--------------------------------------------------------------------------------
1 | export const ArrowCircleUp = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/ArrowCircleDown.jsx:
--------------------------------------------------------------------------------
1 | export const ArrowCircleDown = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Peace.jsx:
--------------------------------------------------------------------------------
1 | export const Peace = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Plus.jsx:
--------------------------------------------------------------------------------
1 | export const Plus = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/ShoppingIcon.jsx:
--------------------------------------------------------------------------------
1 | export const ShoppingIcon = () => {
2 | return (
3 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/NotePad.jsx:
--------------------------------------------------------------------------------
1 | export const NotePad = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Question.jsx:
--------------------------------------------------------------------------------
1 | export const Question = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/GeldBig.jsx:
--------------------------------------------------------------------------------
1 | export const GeldBig = () => {
2 | return (
3 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/Geld.jsx:
--------------------------------------------------------------------------------
1 | export const Geld = () => {
2 | return (
3 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/ListPlus.jsx:
--------------------------------------------------------------------------------
1 | export const ListPlus = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Paper.jsx:
--------------------------------------------------------------------------------
1 | export const Paper = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/NumberFive.jsx:
--------------------------------------------------------------------------------
1 | export const NumberFive = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Orange.jsx:
--------------------------------------------------------------------------------
1 | export const Orange = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/ExcellLogo.jsx:
--------------------------------------------------------------------------------
1 | export const ExcellLogo = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/TaxiIcon.jsx:
--------------------------------------------------------------------------------
1 | export const TaxiIcon = () => {
2 | return (
3 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/Record.jsx:
--------------------------------------------------------------------------------
1 | export const Record = ({ record, category, dataCategory }) => {
2 | const formatDate = (isoDate) => {
3 | const date = new Date(isoDate);
4 | const hours = String(date.getHours()).padStart(2, "0");
5 | const minutes = String(date.getMinutes()).padStart(2, "0");
6 | return `${hours}:${minutes}`;
7 | };
8 |
9 | return (
10 |
11 | {dataCategory.map((category, index) => {
12 | if (category.id === record.category_id)
13 | return (
14 |
15 |
29 |
30 |
31 | {category?.name}
32 |
33 |
34 | {formatDate(record.createdat)}
35 |
36 |
37 |
38 | );
39 | })}
40 |
41 |
48 | {record?.transaction_type === "EXP" ? "-" : "+"}
49 |
50 |
57 | {record?.amount}
58 | ₮
59 |
60 |
61 |
62 | );
63 | };
64 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/FoodDrinkIcon.jsx:
--------------------------------------------------------------------------------
1 | export const FoodDrinkIcon = () => {
2 | return (
3 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Road.jsx:
--------------------------------------------------------------------------------
1 | export const Road = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/components/Header.jsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { HeaderAddRecord } from "../ui/HeaderAddRecord";
3 | import { useRouter } from "next/navigation";
4 | import { toast } from "react-toastify";
5 | import { Logo } from "../svg";
6 |
7 | export const Header = ({ dashboard, records }) => {
8 | const router = useRouter();
9 | const SignOut = () => {
10 | localStorage.removeItem("isLoggedIn");
11 | router.push("/");
12 | toast.success("Successfully signed out");
13 | };
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 | Dashboard
30 |
31 |
32 |
38 | Records
39 |
40 |
41 |
42 |
43 |
46 | {/*
*/}
64 |
65 |
66 |
67 |
68 | );
69 | };
70 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Exam.jsx:
--------------------------------------------------------------------------------
1 | export const Exam = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/IdentificationBadge.jsx:
--------------------------------------------------------------------------------
1 | export const IdentificationBadge = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/IdentificationCard.jsx:
--------------------------------------------------------------------------------
1 | export const IdentificationCard = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/Ladder.jsx:
--------------------------------------------------------------------------------
1 | export const Ladder = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/BezierCurve.jsx:
--------------------------------------------------------------------------------
1 | export const BezierCurve = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/ChooseCategory.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { BluePlusBig, DownArrow } from "../svg";
3 | import { CategoryOption } from "./CategoryOption";
4 |
5 | export const ChooseCategory = ({ onCategoryChange }) => {
6 | const [dataCategory, setDataCategory] = useState([]);
7 | const [open, setOpen] = useState(false);
8 | const [selectedOption, setSelectedOption] = useState(null);
9 |
10 | const toggling = () => setOpen(!open);
11 |
12 | const onOptionClicked = (category) => () => {
13 | setSelectedOption(category);
14 | onCategoryChange(category);
15 | setOpen(false);
16 | };
17 |
18 | const fetchCategoryData = async () => {
19 | try {
20 | const response = await fetch("http://localhost:5000/category");
21 |
22 | if (!response.ok) {
23 | throw new Error(`HTTP error! Status: ${response.status}`);
24 | }
25 |
26 | const category = await response.json();
27 | setDataCategory(category.data);
28 | } catch (error) {
29 | console.error(error);
30 | }
31 | };
32 |
33 | useEffect(() => {
34 | fetchCategoryData();
35 | }, []);
36 |
37 | return (
38 | <>
39 |
40 |
45 |
50 | {selectedOption?.name || "Choose"}
51 |
52 |
57 |
58 |
59 | {open && (
60 |
61 |
74 | {dataCategory?.map((category) => {
75 | return (
76 |
85 | );
86 | })}
87 |
88 | )}
89 |
90 | >
91 | );
92 | };
93 |
--------------------------------------------------------------------------------
/frontend/src/components/svg/GiftIcon.jsx:
--------------------------------------------------------------------------------
1 | export const GiftIcon = () => {
2 | return (
3 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/app/page.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { useEffect, useState } from "react";
5 | import { useFormik } from "formik";
6 | import * as Yup from "yup";
7 | import { toast } from "react-toastify";
8 | import { Geld, Logo } from "@/components/svg";
9 | import { useRouter } from "next/navigation";
10 | import { Loader } from "@/components/ui/Loader";
11 |
12 | export default function Home() {
13 | const [isLoading, setIsLoading] = useState();
14 | const [errorMessage, setErrorMessage] = useState("");
15 | const router = useRouter();
16 |
17 | const formik = useFormik({
18 | initialValues: {
19 | email: "",
20 | password: "",
21 | },
22 | validationSchema: Yup.object({
23 | email: Yup.string().email("Invalid email address").required("Required"),
24 | password: Yup.string()
25 | .min(8, "Password must be at least 8 characters")
26 | .required("Required"),
27 | }),
28 |
29 | onSubmit: async (values) => {
30 | setErrorMessage("");
31 | try {
32 | setIsLoading(true);
33 | const response = await fetch("http://localhost:5000/", {
34 | method: "POST",
35 | headers: {
36 | "Content-Type": "application/json",
37 | },
38 | body: JSON.stringify(values),
39 | });
40 |
41 | const data = await response.json();
42 |
43 | if (response.ok) {
44 | toast.success("Login successful!");
45 | localStorage.setItem("isLoggedIn", "true");
46 | router.push("/dashboard");
47 | } else {
48 | setErrorMessage(data.message || "Invalid credentials");
49 | }
50 | setIsLoading(false);
51 | } catch (error) {
52 | setErrorMessage("Network error");
53 | }
54 | },
55 | });
56 |
57 | useEffect(() => {
58 | setIsLoading(false);
59 | const isLoggedIn = localStorage.getItem("isLoggedIn");
60 | if (isLoggedIn) {
61 | toast.success("You already login");
62 | router.push("/dashboard");
63 | }
64 | }, [router]);
65 |
66 | if (isLoading === true) {
67 | return ;
68 | }
69 |
70 | return (
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Welcome Back
81 |
82 |
83 | Welcome back, Please enter your details
84 |
85 |
86 |
115 |
116 |
117 | Don't have an account?
118 |
119 |
123 | Sign up
124 |
125 |
126 |
127 |
128 |
129 |
130 | );
131 | }
132 |
--------------------------------------------------------------------------------
/backend/server.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import bodyParser from "body-parser";
3 | import cors from "cors";
4 | import dotenv from "dotenv";
5 | import { neon } from "@neondatabase/serverless";
6 | dotenv.config();
7 |
8 | const app = express();
9 | const PORT = 5000;
10 | const sql = neon(`${process.env.DATABASE_URL}`);
11 |
12 | app.use(bodyParser.json());
13 | app.use(cors());
14 |
15 | app.get("/", async (request, response) => {
16 | try {
17 | const users = await sql`SELECT * FROM users`;
18 | response.json({ success: true, statusCode: 201, data: users });
19 | } catch (error) {
20 | response.json({ success: false, error: error });
21 | }
22 | });
23 |
24 | app.get("/records", async (request, response) => {
25 | try {
26 | const records = await sql`SELECT * FROM "record" ORDER BY createdat DESC`;
27 | response.json({ success: true, statusCode: 201, data: records });
28 | } catch (error) {
29 | response.json({ success: false, error: error });
30 | }
31 | });
32 |
33 | app.get("/category", async (request, response) => {
34 | try {
35 | const category = await sql`SELECT * FROM category`;
36 | response.json({ success: true, statusCode: 201, data: category });
37 | } catch (error) {
38 | response.json({ success: false, error: error });
39 | }
40 | });
41 |
42 | ////////////////////////////////////////////////////////////////////////////////////////////////////////
43 |
44 | app.post("/sign-up", async (request, response) => {
45 | const { name, email, password } = request.body;
46 |
47 | try {
48 | const existingUser = await sql`SELECT * FROM users WHERE email = ${email}`;
49 |
50 | if (existingUser.length > 0) {
51 | return response.status(400).json({ message: "User already exists" });
52 | }
53 |
54 | const newUser = await sql`
55 | INSERT INTO users (name, email, password)
56 | VALUES (${name}, ${email}, ${password})
57 | RETURNING id, email
58 | `;
59 |
60 | response
61 | .status(201)
62 | .json({ message: "User created successfully", user: newUser[0] });
63 | } catch (error) {
64 | response
65 | .status(500)
66 | .json({ message: "Internal server error during create user" });
67 | }
68 | });
69 |
70 | app.post("/", async (request, response) => {
71 | const { email, password } = request.body;
72 |
73 | try {
74 | const user = await sql`SELECT * FROM users WHERE email = ${email}`;
75 | if (user.length === 0) {
76 | return response
77 | .status(400)
78 | .json({ message: "email or password not match" });
79 | }
80 |
81 | if (user[0].password !== password) {
82 | return response.status(400).json({ message: "password not match" });
83 | }
84 |
85 | response.status(200).json({ message: "Login successful", user: user[0] });
86 | } catch (error) {
87 | response
88 | .status(500)
89 | .json({ message: "Internal server error during login user" });
90 | }
91 | });
92 |
93 | ////////////////////////////////////////////////////////////////////////////////////////////////////////
94 |
95 | app.post("/record", async (request, response) => {
96 | const {
97 | user_id,
98 | name,
99 | amount,
100 | transaction_type,
101 | description,
102 | category_id,
103 | createdat,
104 | } = request.body;
105 |
106 | try {
107 | const newRecord = await sql`
108 | INSERT INTO record (user_id, name, amount, transaction_type, description, category_id, createdat )
109 | VALUES ( ${user_id}, ${name}, ${amount}, ${transaction_type}, ${description}, ${category_id}, ${createdat}) RETURNING *`;
110 |
111 | response
112 | .status(201)
113 | .json({ message: "Record created successfully", record: newRecord });
114 | } catch (error) {
115 | response
116 | .status(500)
117 | .json({ message: "Internal server error during create record" });
118 | }
119 | });
120 |
121 | app.post("/category", async (request, response) => {
122 | const { name, category_icon, icon_color } = request.body;
123 |
124 | try {
125 | const newCategory = await sql`
126 | INSERT INTO category (name, category_icon, icon_color )
127 | VALUES (${name}, ${category_icon}, ${icon_color}) RETURNING *`;
128 |
129 | response.status(201).json({ success: "True", statusCode: 201 });
130 | } catch (error) {
131 | response.status(500).json({ success: "False", statusCode: 500 });
132 | }
133 | });
134 |
135 | app.delete("/category", async (request, response) => {
136 | const { id } = request.body;
137 |
138 | try {
139 | const deleteCategory = await sql`
140 | DELETE FROM category WHERE id = ${id}`;
141 |
142 | response.status(201).json({ success: "True", statusCode: 201 });
143 | } catch (error) {
144 | response.status(500).json({ success: "False", statusCode: 500 });
145 | }
146 | });
147 |
148 | app.listen(PORT, () => {
149 | console.log(`http://localhost:${PORT}`);
150 | });
151 |
--------------------------------------------------------------------------------
/frontend/src/app/sign-up/page.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { useEffect, useState } from "react";
5 | import { useFormik } from "formik";
6 | import { toast } from "react-toastify";
7 | import * as Yup from "yup";
8 | import { Geld, Logo } from "@/components/svg";
9 | import { useRouter } from "next/navigation";
10 |
11 | const SignUpPage = () => {
12 | const [errorMessage, setErrorMessage] = useState("");
13 | const router = useRouter();
14 |
15 | const formik = useFormik({
16 | initialValues: {
17 | name: "",
18 | email: "",
19 | password: "",
20 | },
21 | validationSchema: Yup.object({
22 | name: Yup.string().required("Required"),
23 | email: Yup.string().email("Invalid email address").required("Required"),
24 | password: Yup.string()
25 | .min(8, "Password must be at least 8 characters")
26 | .required("Required"),
27 | }),
28 | onSubmit: async (values) => {
29 | setErrorMessage("");
30 | try {
31 | const response = await fetch("http://localhost:5000/sign-up", {
32 | method: "POST",
33 | headers: {
34 | "Content-Type": "application/json",
35 | },
36 | body: JSON.stringify(values),
37 | });
38 | const data = await response.json();
39 |
40 | if (response.ok) {
41 | router.push("/");
42 | } else {
43 | setErrorMessage(data.message || "Error occurred");
44 | }
45 | } catch (error) {
46 | setErrorMessage("Network error");
47 | }
48 | },
49 | });
50 |
51 | useEffect(() => {
52 | const isLoggedIn = localStorage.getItem("isLoggedIn");
53 | if (isLoggedIn) {
54 | toast.success("You already login");
55 | router.push("/dashboard");
56 | }
57 | }, [router]);
58 |
59 | return (
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | Create Geld account
70 |
71 |
72 | Sign up below to create your Wallet account
73 |
74 |
75 |
119 |
120 |
121 | Already have an account?
122 |
123 |
127 | Log in
128 |
129 |
130 |
131 |
132 |
133 |
134 | );
135 | };
136 |
137 | export default SignUpPage;
138 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/AddCategory.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { BluePlusBig, BluePlusIcon, CloseIcon } from "../svg";
3 | import { useFormik } from "formik";
4 | import { DownArrow } from "../svg";
5 | import { colors, icons } from "./Data";
6 | import { Home } from "../icons";
7 |
8 | export const AddCategory = ({ onAddCategory }) => {
9 | const [errorMessage, setErrorMessage] = useState("");
10 | const [open, setOpen] = useState(false);
11 | const [selectedOption, setSelectedOption] = useState(null);
12 | const [selectedColor, setSelectedColor] = useState(null);
13 | const [selectedPath, setSelectedPath] = useState("");
14 |
15 | const toggling = () => setOpen(!open);
16 |
17 | const onOptionClicked = (value) => () => {
18 | setSelectedOption(value.icon);
19 | setSelectedPath(value.path);
20 | };
21 |
22 | const onColorClicked = (value) => () => {
23 | setSelectedColor(value.color);
24 | setOpen(false);
25 | };
26 |
27 | const formik = useFormik({
28 | initialValues: {
29 | name: "",
30 | },
31 |
32 | onSubmit: async (values) => {
33 | const requestData = {
34 | ...values,
35 | category_icon: selectedPath,
36 | icon_color: selectedColor,
37 | };
38 | try {
39 | const response = await fetch("http://localhost:5000/category", {
40 | method: "POST",
41 | headers: {
42 | "Content-Type": "application/json",
43 | },
44 | body: JSON.stringify(requestData),
45 | });
46 | const data = await response.json();
47 |
48 | onAddCategory();
49 | if (!response.ok)
50 | throw new Error(`HTTP error! status: ${response.status}`);
51 | console.log(data);
52 | } catch (error) {
53 | setErrorMessage("Network error");
54 | }
55 | },
56 | });
57 | return (
58 | <>
59 |
71 |
156 | >
157 | );
158 | };
159 |
--------------------------------------------------------------------------------
/frontend/src/components/icons/BaseBall.jsx:
--------------------------------------------------------------------------------
1 | export const BaseBall = () => {
2 | return (
3 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/components/pages/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Header } from "../components/Header";
4 | import { ArrowCircleUp } from "../svg/ArrowCircleUp";
5 | import { ArrowCircleDown } from "../svg/ArrowCircleDown";
6 | import { BlueDot } from "../svg/BlueDot";
7 | import { GreenDot } from "../svg/GreendDot";
8 | import { HomeIcon } from "../svg/HomeIcon";
9 |
10 | export const DashboardPage = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Your Income
23 |
24 |
25 |
26 |
27 |
28 |
29 | 1,200,000
30 |
31 |
32 | ₮
33 |
34 |
35 |
36 | Your Income Amount
37 |
38 |
39 |
40 |
41 |
42 | 32% from last month
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Total Expenses
53 |
54 |
55 |
56 |
57 |
58 |
59 | -1,200,000
60 |
61 |
62 | ₮
63 |
64 |
65 |
66 | Your Expence Amount
67 |
68 |
69 |
70 |
71 |
72 | 32% from last month
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | Income - Expense
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | Income - Expense
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | Last Records
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | Lending & Renting
114 |
115 |
116 | 3 hours ago
117 |
118 |
119 |
120 |
121 |
122 | -
123 |
124 |
125 | 1,000₮
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | Lending & Renting
138 |
139 |
140 | 3 hours ago
141 |
142 |
143 |
144 |
145 |
146 | -
147 |
148 |
149 | 1,000₮
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | );
159 | };
160 |
--------------------------------------------------------------------------------
/frontend/src/app/dashboard/page.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | ArrowCircleDown,
5 | ArrowCircleUp,
6 | BlueDot,
7 | GreenDot,
8 | HomeIcon,
9 | } from "@/components/svg";
10 | import { Header } from "@/components/components/Header";
11 | import { useRouter } from "next/navigation";
12 | import { useEffect } from "react";
13 | import { toast } from "react-toastify";
14 |
15 | const DashboardPage = () => {
16 | const router = useRouter();
17 |
18 | useEffect(() => {
19 | // Check if user is logged in
20 | const isLoggedIn = localStorage.getItem("isLoggedIn");
21 | if (!isLoggedIn) {
22 | toast.warning("Login please!");
23 | router.push("/");
24 | }
25 | }, [router]);
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Your Income
39 |
40 |
41 |
42 |
43 |
44 |
45 | 1,200,000
46 |
47 |
48 | ₮
49 |
50 |
51 |
52 | Your Income Amount
53 |
54 |
55 |
56 |
57 |
58 | 32% from last month
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Total Expenses
69 |
70 |
71 |
72 |
73 |
74 |
75 | -1,200,000
76 |
77 |
78 | ₮
79 |
80 |
81 |
82 | Your Expence Amount
83 |
84 |
85 |
86 |
87 |
88 | 32% from last month
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Income - Expense
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | Income - Expense
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | Last Records
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | Lending & Renting
130 |
131 |
132 | 3 hours ago
133 |
134 |
135 |
136 |
137 |
138 | -
139 |
140 |
141 | 1,000₮
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | Lending & Renting
154 |
155 |
156 | 3 hours ago
157 |
158 |
159 |
160 |
161 |
162 | -
163 |
164 |
165 | 1,000₮
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | );
175 | };
176 |
177 | export default DashboardPage;
178 |
--------------------------------------------------------------------------------
/frontend/src/components/pages/Records.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect, useState } from "react";
4 | import { Header } from "../components/Header";
5 | import { DownArrow, LeftArrow, RightArrow } from "../svg";
6 | import { AddRecord } from "../ui/AddRecord";
7 | import { Category } from "../ui/Category";
8 | import { Record } from "../ui/Record";
9 | import { AddCategory } from "../ui/AddCategory";
10 | import { RecordLoader } from "../ui/RecordLoader";
11 |
12 | export const RecordsPage = () => {
13 | const [dataRecord, setDataRecord] = useState([]);
14 | const [dataCategory, setDataCategory] = useState([]);
15 | const [error, setError] = useState(null);
16 | const [isLoading, setIsLoading] = useState();
17 | const [filteredRecords, setFilteredRecords] = useState([]);
18 |
19 | const fetchCategoryData = async () => {
20 | try {
21 | setIsLoading(true);
22 | const response = await fetch("http://localhost:5000/category");
23 |
24 | if (!response.ok) {
25 | throw new Error(`HTTP error! Status: ${response.status}`);
26 | }
27 |
28 | const category = await response.json();
29 | setDataCategory(category.data);
30 | } catch (error) {
31 | console.error(error);
32 | } finally {
33 | setIsLoading(false);
34 | }
35 | };
36 |
37 | const fetchRecordsData = async () => {
38 | try {
39 | setIsLoading(true);
40 | const response = await fetch("http://localhost:5000/records");
41 |
42 | if (!response.ok) {
43 | throw new Error(`HTTP error! Status: ${response.status}`);
44 | }
45 |
46 | const record = await response.json();
47 | setDataRecord(record.data);
48 | setFilteredRecords(record.data);
49 | } catch (error) {
50 | console.error(error);
51 | setError("Error occurred while fetching records.");
52 | } finally {
53 | setIsLoading(false);
54 | }
55 | };
56 |
57 | // Record filter
58 | const filterRecords = (tranType) => {
59 | if (tranType === "ALL") {
60 | setFilteredRecords(dataRecord);
61 | } else {
62 | const updateRecord = dataRecord.filter(
63 | (curType) => curType.transaction_type === tranType
64 | );
65 | setFilteredRecords(updateRecord);
66 | }
67 | };
68 |
69 | // Real-time update
70 | const handleAddButton = () => {
71 | fetchCategoryData();
72 | fetchRecordsData();
73 | };
74 |
75 | useEffect(() => {
76 | fetchRecordsData();
77 | fetchCategoryData();
78 | }, []);
79 |
80 | return (
81 |
82 |
83 |
84 |
85 |
86 |
87 | Records
88 |
89 |
90 |
91 |
96 |
97 |
98 | Types
99 |
100 |
101 |
102 |
filterRecords("ALL")}
108 | />
109 |
110 | All
111 |
112 |
113 |
114 |
filterRecords("INC")}
119 | />
120 |
121 | Income
122 |
123 |
124 |
125 |
filterRecords("EXP")}
130 | />
131 |
132 | Expense
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | Category
141 |
142 |
145 |
146 |
147 | {isLoading ? (
148 |
149 | ) : (
150 | dataCategory?.map((category) => (
151 |
152 |
153 |
154 | ))
155 | )}
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
166 |
167 | Last 30 Days
168 |
169 |
172 |
173 |
174 |
175 | Newest first
176 |
177 |
180 |
181 |
182 |
183 |
184 |
185 | Today
186 |
187 |
188 | {isLoading ? (
189 |
190 | ) : (
191 | filteredRecords?.map((record) => (
192 |
193 |
194 |
195 | ))
196 | )}
197 |
198 |
199 |
200 |
201 | Yesterday
202 |
203 |
204 | {/* {dataRecord?.map((record, recordIndex) => {
205 | return (
206 |
207 |
208 |
209 | );
210 | })} */}
211 |
212 |
213 |
214 |
215 |
216 |
217 | );
218 | };
219 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/HeaderAddRecord.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import { CloseIcon, PlusIcon } from "../svg";
5 | import { ChooseCategory } from "./ChooseCategory";
6 | import { useFormik } from "formik";
7 |
8 | export const HeaderAddRecord = ({ onAddRecord }) => {
9 | const [transactionType, setTransactionType] = useState("EXP");
10 | const [errorMessage, setErrorMessage] = useState("");
11 |
12 | const toggleTransactionType = (type) => {
13 | setTransactionType(type);
14 | };
15 |
16 | const formik = useFormik({
17 | initialValues: {
18 | name: "",
19 | amount: "",
20 | transaction_type: transactionType,
21 | category_id: "",
22 | description: "",
23 | createdat: "",
24 | },
25 |
26 | onSubmit: async (values) => {
27 | const requestData = {
28 | ...values,
29 | transaction_type: transactionType,
30 | };
31 | try {
32 | const response = await fetch("http://localhost:5000/record", {
33 | method: "POST",
34 | headers: {
35 | "Content-Type": "application/json",
36 | },
37 | body: JSON.stringify(requestData),
38 | });
39 | const data = await response.json();
40 |
41 | onAddRecord();
42 | if (!response.ok)
43 | throw new Error(`HTTP error! status: ${response.status}`);
44 | } catch (error) {
45 | setErrorMessage("Network error");
46 | }
47 | },
48 | });
49 |
50 | const handleCategoryChange = (category) => {
51 | formik.setFieldValue("category_id", category.id);
52 | };
53 | return (
54 | <>
55 |
62 |
63 |
202 | >
203 | );
204 | };
205 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/AddRecord.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import { CloseIcon, PlusIcon } from "../svg";
5 | import { ChooseCategory } from "./ChooseCategory";
6 | import { useFormik } from "formik";
7 |
8 | export const AddRecord = ({ onAddRecord }) => {
9 | const [transactionType, setTransactionType] = useState("EXP");
10 | const [errorMessage, setErrorMessage] = useState("");
11 | const [date, setDate] = useState("");
12 | const [time, setTime] = useState("");
13 |
14 | const toggleTransactionType = (type) => {
15 | setTransactionType(type);
16 | };
17 |
18 | const formik = useFormik({
19 | initialValues: {
20 | name: "",
21 | amount: "",
22 | transaction_type: transactionType,
23 | category_id: "",
24 | description: "",
25 | createdat: "", // Final timestamp in ISO format
26 | },
27 |
28 | onSubmit: async (values) => {
29 | // Prepare request data
30 | const requestData = {
31 | ...values,
32 | transaction_type: transactionType,
33 | };
34 |
35 | try {
36 | const response = await fetch("http://localhost:5000/record", {
37 | method: "POST",
38 | headers: {
39 | "Content-Type": "application/json",
40 | },
41 | body: JSON.stringify(requestData),
42 | });
43 |
44 | const data = await response.json();
45 | if (!response.ok)
46 | throw new Error(`HTTP error! status: ${response.status}`);
47 |
48 | onAddRecord();
49 | } catch (error) {
50 | setErrorMessage("Network error");
51 | }
52 | },
53 | });
54 |
55 | const handleDateChange = (e) => {
56 | setDate(e.target.value);
57 | updateCreatedAt(e.target.value, time);
58 | };
59 |
60 | const handleTimeChange = (e) => {
61 | setTime(e.target.value);
62 | updateCreatedAt(date, e.target.value);
63 | };
64 |
65 | const updateCreatedAt = (date, time) => {
66 | if (date && time) {
67 | const combinedDateTime = `${date}T${time}`;
68 | formik.setFieldValue("createdat", combinedDateTime);
69 | }
70 | };
71 |
72 | const handleCategoryChange = (category) => {
73 | formik.setFieldValue("category_id", category.id);
74 | };
75 |
76 | return (
77 | <>
78 |
87 |
88 |
226 | >
227 | );
228 | };
229 |
230 | ////
231 | ////
232 | ///
233 | //
234 | //
235 | //
236 |
--------------------------------------------------------------------------------