├── 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 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /frontend/src/components/svg/GreendDot.jsx: -------------------------------------------------------------------------------- 1 | export const GreenDot = () => { 2 | return ( 3 | 10 | 11 | 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 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/LogoBig.jsx: -------------------------------------------------------------------------------- 1 | export const LogoBig = () => { 2 | return ( 3 | 10 | 14 | 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 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/RightArrow.jsx: -------------------------------------------------------------------------------- 1 | export const RightArrow = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/DownArrow.jsx: -------------------------------------------------------------------------------- 1 | export const DownArrow = () => { 2 | return ( 3 | 10 | 14 | 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 |
8 | 15 | 16 | 17 |
18 |

19 | {category?.name} 20 |

21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Vignette.jsx: -------------------------------------------------------------------------------- 1 | export const Vignette = () => { 2 | return ( 3 | 10 | 14 | 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 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/OpenEye.jsx: -------------------------------------------------------------------------------- 1 | export const OpenEye = () => { 2 | return ( 3 | 11 | 15 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /frontend/src/components/svg/BluePlusBig.jsx: -------------------------------------------------------------------------------- 1 | export const BluePlusBig = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/IntersectSquare.jsx: -------------------------------------------------------------------------------- 1 | export const IntersectSquare = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/BluePlusIcon.jsx: -------------------------------------------------------------------------------- 1 | export const BluePlusIcon = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/CloseIcon.jsx: -------------------------------------------------------------------------------- 1 | export const CloseIcon = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Home.jsx: -------------------------------------------------------------------------------- 1 | export const Home = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Pencil.jsx: -------------------------------------------------------------------------------- 1 | export const Pencil = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/HouseLine.jsx: -------------------------------------------------------------------------------- 1 | export const HouseLine = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/ImageSquare.jsx: -------------------------------------------------------------------------------- 1 | export const ImageSquare = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Globe.jsx: -------------------------------------------------------------------------------- 1 | export const Globe = () => { 2 | return ( 3 | 10 | 14 | 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 |
4 | 11 | 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Exclude.jsx: -------------------------------------------------------------------------------- 1 | export const Exclude = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Microphone.jsx: -------------------------------------------------------------------------------- 1 | export const Microphone = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/NumberSeven.jsx: -------------------------------------------------------------------------------- 1 | export const NumberSeven = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/DrinkIcon.jsx: -------------------------------------------------------------------------------- 1 | export const DrinkIcon = () => { 2 | return ( 3 |
4 | 11 | 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Anchor.jsx: -------------------------------------------------------------------------------- 1 | export const Anchor = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/CloseEye.jsx: -------------------------------------------------------------------------------- 1 | export const CloseEye = () => { 2 | return ( 3 | 11 | 17 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /frontend/src/components/icons/HourGlass.jsx: -------------------------------------------------------------------------------- 1 | export const HourGlass = () => { 2 | return ( 3 | 10 | 14 | 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 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Watch.jsx: -------------------------------------------------------------------------------- 1 | export const Watch = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/ArrowCircleUp.jsx: -------------------------------------------------------------------------------- 1 | export const ArrowCircleUp = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/ArrowCircleDown.jsx: -------------------------------------------------------------------------------- 1 | export const ArrowCircleDown = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Peace.jsx: -------------------------------------------------------------------------------- 1 | export const Peace = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Plus.jsx: -------------------------------------------------------------------------------- 1 | export const Plus = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/ShoppingIcon.jsx: -------------------------------------------------------------------------------- 1 | export const ShoppingIcon = () => { 2 | return ( 3 |
4 | 11 | 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /frontend/src/components/icons/NotePad.jsx: -------------------------------------------------------------------------------- 1 | export const NotePad = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Question.jsx: -------------------------------------------------------------------------------- 1 | export const Question = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/GeldBig.jsx: -------------------------------------------------------------------------------- 1 | export const GeldBig = () => { 2 | return ( 3 | 10 | 14 | 18 | 19 | 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /frontend/src/components/svg/Geld.jsx: -------------------------------------------------------------------------------- 1 | export const Geld = () => { 2 | return ( 3 | 10 | 14 | 18 | 22 | 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /frontend/src/components/icons/ListPlus.jsx: -------------------------------------------------------------------------------- 1 | export const ListPlus = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Paper.jsx: -------------------------------------------------------------------------------- 1 | export const Paper = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/NumberFive.jsx: -------------------------------------------------------------------------------- 1 | export const NumberFive = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Orange.jsx: -------------------------------------------------------------------------------- 1 | export const Orange = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/ExcellLogo.jsx: -------------------------------------------------------------------------------- 1 | export const ExcellLogo = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/svg/TaxiIcon.jsx: -------------------------------------------------------------------------------- 1 | export const TaxiIcon = () => { 2 | return ( 3 |
4 | 11 | 15 | 16 |
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 |
19 | 26 | 27 | 28 |
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 |
4 | 11 | 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Road.jsx: -------------------------------------------------------------------------------- 1 | export const Road = () => { 2 | return ( 3 | 10 | 14 | 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 | {/*
47 |
52 | 63 |
*/} 64 |
65 |
66 |
67 |
68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Exam.jsx: -------------------------------------------------------------------------------- 1 | export const Exam = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/IdentificationBadge.jsx: -------------------------------------------------------------------------------- 1 | export const IdentificationBadge = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/IdentificationCard.jsx: -------------------------------------------------------------------------------- 1 | export const IdentificationCard = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/Ladder.jsx: -------------------------------------------------------------------------------- 1 | export const Ladder = () => { 2 | return ( 3 | 10 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /frontend/src/components/icons/BezierCurve.jsx: -------------------------------------------------------------------------------- 1 | export const BezierCurve = () => { 2 | return ( 3 | 10 | 14 | 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 |
4 | 11 | 12 | 16 | 17 |
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 |
90 | 99 | 108 | 114 |
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 |
79 | 88 | 97 | 106 | 112 | 118 |
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 | 72 |
73 |
74 |

75 | Add Category 76 |

77 |
78 | 81 |
82 |
83 |
87 |
88 |
98 |
{selectedOption || }
99 | 100 |
101 | {open && ( 102 |
103 |
104 |
105 | {icons.map((icon, id, path) => { 106 | return ( 107 | 114 | ); 115 | })} 116 |
117 |
118 |
119 | {colors.map((color, id) => { 120 | return ( 121 | 128 | ); 129 | })} 130 |
131 |
132 |
133 | )} 134 | 135 | 144 |
145 | 146 | 153 |
154 |
155 |
156 | 157 | ); 158 | }; 159 | -------------------------------------------------------------------------------- /frontend/src/components/icons/BaseBall.jsx: -------------------------------------------------------------------------------- 1 | export const BaseBall = () => { 2 | return ( 3 | 10 | 14 | 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 | 64 |
65 |
66 |

67 | Add Record 68 |

69 |
70 | 73 |
74 |
75 |
76 |
77 |
78 | 89 | 100 | {/*
*/} 101 |
102 |
103 |
104 |
105 |

106 | Amount 107 |

108 | 117 |
118 | 119 |
120 |

121 | Category 122 |

123 | 129 |
130 |
131 |
132 |

133 | Date 134 |

135 | 143 |
144 |
145 |

146 | Time 147 |

148 | 156 |
157 |
158 |
159 | 168 |
169 |
170 |
171 |
172 |

173 | Payee 174 |

175 | 184 |
185 |
186 |

187 | Note 188 |

189 | 197 |
198 |
199 |
200 |
201 |
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 | 89 |
90 |
91 |

92 | Add Record 93 |

94 |
95 | 98 |
99 |
100 |
101 |
102 |
103 | 114 | 125 |
126 |
127 |
128 |
129 |

130 | Amount 131 |

132 | 141 |
142 | 143 |
144 |

145 | Category 146 |

147 | 153 |
154 |
155 |
156 |

157 | Date 158 |

159 | 167 |
168 |
169 |

170 | Time 171 |

172 | 180 |
181 |
182 |
183 | 192 |
193 |
194 |
195 |
196 |

197 | Payee 198 |

199 | 208 |
209 |
210 |

211 | Note 212 |

213 | 221 |
222 |
223 |
224 |
225 |
226 | 227 | ); 228 | }; 229 | 230 | //// 231 | //// 232 | /// 233 | // 234 | // 235 | // 236 | --------------------------------------------------------------------------------