diff --git a/web/AASTU-web-group-2/a2sv-banking-system/.eslintrc.json b/web/AASTU-web-group-2/a2sv-banking-system/.eslintrc.json
new file mode 100644
index 000000000..bffb357a7
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/.gitignore b/web/AASTU-web-group-2/a2sv-banking-system/.gitignore
new file mode 100644
index 000000000..fd3dbb571
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/README.md b/web/AASTU-web-group-2/a2sv-banking-system/README.md
new file mode 100644
index 000000000..c4033664f
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/README.md
@@ -0,0 +1,36 @@
+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).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+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.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/BarChartForAccounts.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/BarChartForAccounts.tsx
new file mode 100644
index 000000000..76f2940fc
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/BarChartForAccounts.tsx
@@ -0,0 +1,83 @@
+"use client";
+import { Bar, BarChart, CartesianGrid, Legend, XAxis } from "recharts";
+
+import { Card, CardContent } from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+const chartData = [
+ { day: "Monday", debit: 186, credit: 80 },
+ { day: "Tuesday", debit: 305, credit: 200 },
+ { day: "Wednesday", debit: 237, credit: 120 },
+ { day: "Thursday", debit: 73, credit: 190 },
+ { day: "Friday", debit: 209, credit: 130 },
+ { day: "Saturday", debit: 214, credit: 140 },
+ { day: "Sunday", debit: 214, credit: 140 },
+];
+
+const chartConfig = {
+ debit: {
+ label: "Debit",
+ color: "#1814F3", // Debit color
+ },
+ credit: {
+ label: "Credit",
+ color: "#FC7900", // Credit color
+ },
+} satisfies ChartConfig;
+
+const CustomLegend = (props: any) => {
+ const { payload } = props;
+
+ return (
+
+ {payload.map((entry: any, index: number) => (
+
+
+
+ {entry.value.charAt(0).toUpperCase() + entry.value.slice(1)}
+
+
+ ))}
+
+ );
+};
+
+export default function BarChartForAccounts() {
+ return (
+
+
+
+
+ }
+ />
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/ListCard.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/ListCard.tsx
new file mode 100644
index 000000000..eb0b1c33c
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/ListCard.tsx
@@ -0,0 +1,58 @@
+import React from "react";
+import { IconType } from "react-icons";
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+type Column = {
+ icon: IconType;
+ iconStyle: string;
+ data: DataItem[]; // Updated to an array of DataItem objects
+};
+
+interface Props {
+ column: Column;
+ width: string;
+ darkMode: string
+}
+
+const ListCard = ({ column, width, darkMode }: Props) => {
+ return (
+
+
+
+
+
+ {column.data.map((item, index) => (
+
+
{item.heading}
+
{item.text}
+
+ ))}
+
+
+ );
+};
+
+export const ListCardLoading = () => {
+ return (
+
+ );
+};
+
+export default ListCard;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/Loading.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/Loading.tsx
new file mode 100644
index 000000000..603aafb87
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/components/Loading.tsx
@@ -0,0 +1,83 @@
+import React from "react";
+import { ListCardLoading } from "./ListCard";
+const Loading = () => {
+ return (
+ <>
+
+ >
+ );
+};
+
+export default Loading;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/page.tsx
new file mode 100644
index 000000000..50506d861
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/accounts/page.tsx
@@ -0,0 +1,410 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import {
+ MdHome,
+ MdSettings,
+ MdAttachMoney,
+ MdAccountBalance,
+} from "react-icons/md";
+import ListCard from "./components/ListCard";
+import { IconType } from "react-icons";
+import BarChartForAccounts from "./components/BarChartForAccounts";
+import Card from "../components/Page2/Card";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import { getCards } from "@/lib/api/cardController";
+import { Card as CardType } from "@/types/cardController.Interface";
+import { getCurrentUser } from "@/lib/api/userControl";
+import { UserInfo } from "@/types/userInterface";
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import { ListCardLoading } from "./components/ListCard";
+import {
+ getTransactionIncomes,
+ getTransactions,
+ getTransactionsExpenses,
+} from "@/lib/api/transactionController";
+import Loading from "./components/Loading";
+import { TransactionData, TransactionResponse } from "@/types/transactionController.interface";
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+type Column = {
+ icon: IconType;
+ iconStyle: string;
+ data: DataItem[];
+};
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+const Page = () => {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setLoading] = useState(true);
+ const [getCard, setGetCards] = useState();
+ const [currentUser, setCurrentUser] = useState();
+ const [balance, setBalance] = useState("Loading...");
+ const [income, setIncome] = useState("Loading...");
+ const [expense, setExpense] = useState("Loading...");
+ const [transaction, setTransaction] = useState([])
+
+ // Getting the session from the server and Access Token From Refresh
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ // Combined fetching data to reduce multiple useEffect hooks
+ useEffect(() => {
+ const fetchData = async () => {
+ if (!access_token) return;
+
+ try {
+ // Fetch Cards
+ const cardData = await getCards(access_token);
+ setGetCards(cardData.content);
+
+ // Fetch Balance
+ const currentUser = await getCurrentUser(access_token);
+ setCurrentUser(currentUser);
+ setBalance(currentUser.accountBalance);
+
+ // Fetch Income
+ const incomeData = await getTransactionIncomes(0, 1, access_token);
+ const totalIncome = incomeData.data.content.reduce(
+ (sum: number, item: any) => sum + item.amount,
+ 0
+ );
+ setIncome(String("0"));
+ setIncome(String(totalIncome));
+
+ // Fetch Expense
+ const expenseData = await getTransactionsExpenses(0, 1, access_token);
+ const totalExpense = expenseData.data.content.reduce(
+ (sum: number, item: any) => sum + item.amount,
+ 0
+ );
+ setExpense("0");
+ setExpense(String(totalExpense));
+
+ // Fetch Transactions
+ const transactionData:TransactionResponse = await getTransactions(0, 3, access_token)
+ setTransaction(transactionData.data.content)
+
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ }
+ };
+
+ fetchData();
+ }, [access_token]);
+
+ // Example data for the first ListCard
+ const ReusableCard: Column = {
+ icon: MdHome,
+ iconStyle: "text-[#FFBB38] bg-[#FFF5D9]",
+ data: [
+ {
+ heading: "My Balance",
+ text: String(balance),
+ headingStyle: "text-sm font-bold text-nowrap text-[#718EBF]",
+ dataStyle: "text-xs text-nowrap",
+ },
+ ],
+ };
+
+ // Example data for the second ListCard
+ const card1: Column = {
+ icon: MdAttachMoney, // Updating the icon
+ iconStyle: "text-[#396AFF] bg-[#E7EDFF]", // Updating the iconStyle
+ data: ReusableCard.data.map((item) => ({
+ ...item,
+ text: String(income),
+ heading: "Income", // Updating the heading
+ })),
+ };
+
+ // Example data for the third ListCard
+ const card2: Column = {
+ icon: MdSettings, // Updating the icon
+ iconStyle: "text-[#FF82AC] bg-[#FFE0EB]", // Updating the iconStyle
+ data: ReusableCard.data.map((item) => ({
+ ...item,
+ text: String(expense),
+ heading: "Expense", // Updating the heading
+ })),
+ };
+
+ // Example data for the fourth ListCard
+ const card3: Column = {
+ icon: MdAccountBalance, // Updating the icon
+ iconStyle: "text-[#16DBCC] bg-[#DCFAF8]", // Updating the iconStyle
+ data: ReusableCard.data.map((item) => ({
+ ...item,
+ heading: "Total Savings", // Updating the heading
+ })),
+ };
+
+
+ // First column with multiple data items
+ const ReusableLastTransaction: Column = {
+ icon: MdHome,
+ iconStyle: "text-[#FFBB38] bg-[#FFF5D9]",
+ data: [
+ {
+ heading: "Spotify Subscription",
+ text: "25 Jan 2021",
+ headingStyle: "text-sm font-bold text-nowrap",
+ dataStyle: "text-xs text-nowrap text-[#718EBF]",
+ },
+ {
+ heading: "-$150",
+ text: "",
+ headingStyle: "text-xs font-bold text-[#FE5C73]",
+ dataStyle: "text-xs text-nowrap",
+ },
+ ],
+ };
+
+ // First transaction example
+ const transaction1: Column = {
+ icon: MdAccountBalance, // Different icon
+ iconStyle: "text-[#16DBCC] bg-[#DCFAF8]", // Different iconStyle
+ data: ReusableLastTransaction.data.map((item, index) => ({
+ ...item,
+ heading: index === 0 ? "Mobile Services" : item.heading, // Custom heading for the first item
+ })),
+ };
+
+ const transaction2: Column = {
+ icon: MdAttachMoney, // Updating the icon
+ iconStyle: "text-[#16DBCC] bg-[#DCFAF8]", // Updating the iconStyle
+ data: ReusableLastTransaction.data.map((item, index) => ({
+ ...item,
+ heading: index === 0 ? "Emilly Wilson " : "+$780",
+ headingStyle:
+ index === 0 ? item.headingStyle : "text-xs font-bold text-[#16DBAA]",
+ })),
+ };
+
+
+
+
+ // Map transaction data to ListCard columns
+ const createTransactionColumn = (transaction: TransactionData): Column => {
+ return {
+ icon: MdAccountBalance, // Default icon, you can customize based on type
+ iconStyle: "text-[#16DBCC] bg-[#DCFAF8]", // Default iconStyle, you can customize based on type
+ data: [
+ {
+ heading: transaction.description,
+ text: formatDate(transaction.date),
+ headingStyle: "text-sm font-bold text-nowrap",
+ dataStyle: "text-xs text-nowrap text-[#718EBF]",
+ },
+ {
+ heading: transaction.amount < 0 ? `-${Math.abs(transaction.amount)}` : `+${transaction.amount}`,
+ text: transaction.receiverUserName || "unknown source",
+ headingStyle: `text-xs font-bold ${transaction.amount < 0 ? "text-[#FE5C73]" : "text-[#16DBAA]"}`,
+ dataStyle: "text-xs text-nowrap",
+ },
+ ],
+ };
+ };
+
+
+ if (loading) {
+ return ;
+ }
+
+ // Don't render anything while loading
+ return (
+ <>
+
+
+
+ {balance || income == "0" ? (
+
+ ) : (
+
+ )}
+
+ {income || income == "0" ? (
+
+ ) : (
+
+ )}
+ {expense || expense == "0" ? (
+
+ ) : (
+
+ )}
+ {balance || balance == "0" ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ Last Transaction
+
+
+ {transaction.length > 0 ? (
+ transaction.slice(0, 3).map((txn, index) => (
+
+ ))
+ ) : (
+
+ )}
+
+
+
+
+
+
+ My Card
+
+
+ See All
+
+
+ {getCard ? (
+ getCard.map((items) => (
+
+ ))
+ ) : (
+
+ )}
+
+
+
+
+
+
+ Debit & Credit Overview
+
+
+
+
+
+ Invoice Sent
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+const formatDate = (dateString: string): string => {
+ const date = new Date(dateString);
+
+ const options: Intl.DateTimeFormatOptions = {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ };
+
+ return date.toLocaleDateString("en-US", options);
+};
+
+export default Page;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/options.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/options.ts
new file mode 100644
index 000000000..06fa242c6
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/options.ts
@@ -0,0 +1,59 @@
+import CredentialsProvider from "next-auth/providers/credentials";
+import { NextAuthOptions } from 'next-auth';
+import { login } from "@/lib/api/authenticationController";
+export const options: NextAuthOptions = {
+ session: {
+ strategy: "jwt", // Use JWT for session strategy
+ },
+
+
+ providers: [
+ CredentialsProvider({
+ name: "credentials",
+ credentials: {
+ userName: { label: "Username", type: "string" },
+ password: { label: "Password", type: "string" }
+ },
+
+ async authorize(credentials, req) {
+ try {
+ const response = await login(credentials as { userName: string; password: string });
+ if (response.success) {
+ return response.data; // Return the user data object
+ } else {
+ return null;
+ }
+ } catch (error) {
+ console.error('Authorization error:', error);
+ return null;
+ }
+ },
+ })
+
+ ],
+
+ pages:{
+ signIn: '/api/auth/signin'
+ // signUp: '/api/auth/signup',
+ },
+ callbacks: {
+ // Store the user information in the JWT token
+
+ async jwt({ token, user }: any) {
+ // Decode the access token to check expiry
+ if (user) {
+ token.access_token = user.access_token;
+ token.data = user.data; // Assuming the user object has a 'name' field
+ token.refresh_token = user.refresh_token;
+ }
+ return token
+ },
+ // Make custom user data available in the session
+ async session({ session, token }: any) {
+ session.user.access_token = token.access_token;
+ session.user.data = token.data;
+ session.user.refresh_token = token.refresh_token;
+ return session;
+ },
+ },
+};
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/route.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 000000000..e0317d663
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,5 @@
+import NextAuth from "next-auth";
+import { options } from "./options";
+
+const handler = NextAuth(options);
+export { handler as GET, handler as POST };
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/token/RefreshToken.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/token/RefreshToken.ts
new file mode 100644
index 000000000..c05a893bd
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/[...nextauth]/token/RefreshToken.ts
@@ -0,0 +1,28 @@
+import {jwtDecode, JwtPayload } from "jwt-decode";
+import { getSession } from "next-auth/react";
+import { refreshToken } from "@/lib/api/authenticationController";
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+export default async function Refresh(): Promise {
+ const session = (await getSession()) as SessionDataType | null
+ if (!session?.user?.access_token || !session?.user?.refresh_token) {
+ return ""
+ }
+ const accessToken = session.user.access_token;
+ const refreshTokenValue = session.user.refresh_token;
+
+ const decodedToken = jwtDecode(accessToken);
+ const currentTime = Date.now() / 1000;
+ const expiry = decodedToken.exp;
+ console.log(accessToken, "access token");
+
+ return accessToken
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signin/components/Forms.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signin/components/Forms.tsx
new file mode 100644
index 000000000..0c0c2ba2a
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signin/components/Forms.tsx
@@ -0,0 +1,115 @@
+"use client";
+import { useForm } from "react-hook-form";
+import { DevTool } from "@hookform/devtools";
+import { BsExclamationCircle } from "react-icons/bs";
+import { FaGoogle, FaGithub } from "react-icons/fa";
+import { signIn } from "next-auth/react";
+import { useRouter } from "next/navigation";
+type FormValues = {
+ username: string;
+ password: string;
+};
+
+const Contact = () => {
+ const form = useForm();
+ const { register, control, handleSubmit, formState } = form;
+ const { errors } = formState;
+ const router = useRouter()
+
+ const onSubmit = async (data: FormValues) => {
+ await signIn("credentials", {
+ redirect: true,
+ // callbackUrl: "/dashboard",
+ userName: data.username,
+ password: data.password,
+ });
+ };
+
+
+ const route = () => {
+ router.push("/api/auth/signup")
+ }
+
+ return (
+
+
+
+
+ Welcome Back,
+
+
+
+
+
+
+
+
+
+
+ Don’t have an account?
+
+ Sign Up
+
+
+
+
+
+
+ );
+};
+
+export default Contact;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signin/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signin/page.tsx
new file mode 100644
index 000000000..f0afea115
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signin/page.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+import { getServerSession } from 'next-auth'
+import { options } from '../[...nextauth]/options'
+import { redirect } from 'next/navigation'
+import Forms from './components/Forms'
+const page = async () => {
+ const session = await getServerSession(options)
+ if(session){
+ redirect("/")
+ }
+ return (
+
+ )
+}
+
+export default page
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageOne.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageOne.tsx
new file mode 100644
index 000000000..3fbaf8013
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageOne.tsx
@@ -0,0 +1,238 @@
+import React from "react";
+import DatePicker from "react-datepicker";
+import { useFormContext, Controller } from "react-hook-form";
+import { BsExclamationCircle } from "react-icons/bs";
+import "react-datepicker/dist/react-datepicker.css";
+
+const PageOne = () => {
+ const { control } = useFormContext();
+
+ return (
+
+
+ {/* Welcome Text */}
+
+ Basic Information
+
+
+ {/* Form Container */}
+
+
+
+ );
+};
+
+export default PageOne;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageThree.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageThree.tsx
new file mode 100644
index 000000000..24ac86964
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageThree.tsx
@@ -0,0 +1,193 @@
+import React, { useState } from "react";
+import { useFormContext, Controller } from "react-hook-form";
+import Image from "next/image";
+import { FaPencilAlt } from "react-icons/fa";
+import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
+import { v4 } from "uuid";
+import { storage } from "@/app/firebase";
+import { BsExclamationCircle } from "react-icons/bs";
+
+type ToggleType = {
+ name: string;
+ label: string;
+ control: any;
+};
+
+// Reusable Toggle Switch Component
+const ToggleSwitch = ({ name, label, control }: ToggleType) => (
+
+
+ {label}
+
+
(
+ onChange(!value)}
+ >
+
+
+ )}
+ />
+
+);
+
+// Reusable error message component
+const ErrorMessage = ({ message }: any) => (
+
+
+ {message}
+
+);
+
+const PageThree = () => {
+ const { control, setValue, formState: { errors } } = useFormContext();
+ const [user, setUser] = useState({
+ profilePicture: "",
+ });
+
+ const [loading, setLoading] = useState(false); // Loading state for image upload
+ const [error, setError] = useState(""); // Error state for image upload
+
+ return (
+
+
+ Account Settings
+
+
+ {/* Profile Picture Upload */}
+
+ {loading && (
+
+ )}
+
+
+ {/* Hidden file input for selecting a new profile picture */}
+
) => {
+ const file = e.target.files?.[0]; // Check if files exist
+ if (file) {
+ setValue("profilePicture", file);
+ setLoading(true);
+
+ // Validate file type and size before upload
+ if (!file.type.startsWith("image/")) {
+ setError("Please select a valid image file.");
+ setLoading(false);
+ return;
+ }
+ if (file.size > 5 * 1024 * 1024) {
+ setError("File size should be less than 5MB.");
+ setLoading(false);
+ return;
+ }
+
+ // Immediately upload the image to Firebase
+ try {
+ const imageRef = ref(storage, `images/${file.name}-${v4()}`);
+ await uploadBytes(imageRef, file);
+
+ // Get the download URL after the image is uploaded
+ const downloadUrl = await getDownloadURL(imageRef);
+
+ // Update the profile picture in the form state and in the UI
+ setValue("profilePicture", downloadUrl);
+ setUser((prev) => ({ ...prev, profilePicture: downloadUrl }));
+ setError(""); // Clear any previous errors
+ } catch (error) {
+ setError("Error uploading image. Please try again.");
+ console.error("Error uploading image:", error);
+ } finally {
+ setLoading(false);
+ }
+ }
+ }}
+ style={{ display: "none" }} // Hide the input
+ id="profilePictureInput"
+ />
+
+ {/* Label for the file input, styled as an edit icon */}
+
+
+
+
+
+
+
+ {/* Error Message for Image Upload */}
+ {error &&
}
+
+ {/* Currency Field */}
+
+
+ Currency
+
+ (
+ <>
+
+ {error && }
+ >
+ )}
+ />
+
+
+ {/* Toggles */}
+
+
+
+
+
+ );
+};
+
+export default PageThree;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageTwo.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageTwo.tsx
new file mode 100644
index 000000000..40e3cd651
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/PageTwo.tsx
@@ -0,0 +1,73 @@
+import React from "react";
+import { useFormContext, Controller } from "react-hook-form";
+import { BsExclamationCircle } from "react-icons/bs";
+
+// Reusable error message component
+const ErrorMessage = ({ message }: any) => (
+
+
+ {message}
+
+);
+
+const PageTwo = () => {
+ const { control } = useFormContext();
+
+ return (
+
+
+
+ Your Address
+
+
+ {/* Reusable Field Component */}
+ {[
+ { name: "city", label: "City", placeholder: "Enter your city" },
+ {
+ name: "presentAddress",
+ label: "Present Address",
+ placeholder: "Enter your present address",
+ },
+ { name: "country", label: "Country", placeholder: "Enter your country" },
+ {
+ name: "permanentAddress",
+ label: "Permanent Address",
+ placeholder: "Enter your permanent address",
+ },
+ {
+ name: "postalCode",
+ label: "Postal Code",
+ placeholder: "Enter your postal code",
+ },
+ { name: "timeZone", label: "Time Zone", placeholder: "Enter your time zone" },
+ ].map(({ name, label, placeholder }) => (
+
+
+ {label}
+
+ (
+ <>
+
+ {error && }
+ >
+ )}
+ />
+
+ ))}
+
+
+ );
+};
+
+export default PageTwo;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/SignUpForm.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/SignUpForm.tsx
new file mode 100644
index 000000000..65daa22d9
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/components/SignUpForm.tsx
@@ -0,0 +1,113 @@
+"use client";
+import React, { useState } from "react";
+import { useForm, FormProvider, SubmitHandler } from "react-hook-form";
+import PageOne from "./PageOne";
+import PageTwo from "./PageTwo";
+import PageThree from "./PageThree";
+import { register } from "@/lib/api/authenticationController";
+import { RegisterRequest, RegisterResponse } from "@/types/authenticationController.interface";
+import { useRouter } from "next/navigation";
+
+type SignUpFormData = {
+ name: string;
+ email: string;
+ password: string;
+ city: string;
+ presentAddress: string;
+ country: string;
+ permanentAddress: string;
+ postalCode: string;
+ timeZone: string;
+ currency: string;
+ sentOrReceiveDigitalCurrency: boolean;
+ receiveMerchantOrder: boolean;
+ accountRecommendations: boolean;
+ twoFactorAuthentication: boolean;
+ profilePicture?: File | string;
+ dateOfBirth: string;
+ username: string;
+};
+
+const SignUpForm = () => {
+ const methods = useForm();
+ const [page, setPage] = useState(1);
+ const router = useRouter();
+
+ const onSubmit: SubmitHandler = async (data) => {
+ const registerRequest: RegisterRequest = {
+ name: data.name,
+ email: data.email,
+ dateOfBirth: data.dateOfBirth,
+ permanentAddress: data.permanentAddress,
+ postalCode: data.postalCode,
+ username: data.username,
+ password: data.password,
+ presentAddress: data.presentAddress,
+ city: data.city,
+ country: data.country,
+ profilePicture:
+ typeof data.profilePicture === "string" ? data.profilePicture : "",
+ preference: {
+ currency: data.currency,
+ sentOrReceiveDigitalCurrency: data.sentOrReceiveDigitalCurrency,
+ receiveMerchantOrder: data.receiveMerchantOrder,
+ accountRecommendations: data.accountRecommendations,
+ timeZone: data.timeZone,
+ twoFactorAuthentication: data.twoFactorAuthentication,
+ },
+ };
+
+ console.log("Final Data:", registerRequest);
+ const d: RegisterResponse = await register(registerRequest);
+ console.log(d)
+ alert("Registered Successfully");
+ router.push("/api/auth/signin");
+ };
+
+ const nextPage = () => setPage((prev) => prev + 1);
+ const prevPage = () => setPage((prev) => prev - 1);
+
+ return (
+
+ );
+};
+
+export default SignUpForm;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/page.tsx
new file mode 100644
index 000000000..cf5751475
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/api/auth/signup/page.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import SignUpForm from './components/SignUpForm'
+const page = () => {
+ return (
+
+ )
+}
+
+export default page
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/components/BankServiceList.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/components/BankServiceList.tsx
new file mode 100644
index 000000000..e6295b745
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/components/BankServiceList.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+
+interface Service {
+ name: string;
+ description: string;
+}
+
+interface BankServiceListProps {
+ logoBgColor?: string;
+ logoSvg?: React.ReactNode;
+ serviceName?: string;
+ serviceDescription?: string;
+ additionalServices?: Service[];
+ viewDetailsLink?: string;
+}
+
+const BankServiceList: React.FC = ({
+ logoBgColor = "bg-pink-100",
+ logoSvg = (
+
+ {/* Default SVG content */}
+
+ ),
+ serviceName = "Business loans",
+ serviceDescription = "It is a long established",
+ additionalServices = [],
+ viewDetailsLink = "#",
+}) => {
+ return (
+
+ {/* Mobile Layout */}
+
+
+
+ roller
+ {logoSvg}
+
+
+
{serviceName}
+
{serviceDescription}
+
+
+
View Details
+
+
+ {/* Web Layout */}
+
+
+
+ {logoSvg}
+
+
+
{serviceName}
+
{serviceDescription}
+
+
+
+ {additionalServices.map((service, index) => (
+
+
{service.name}
+
{service.description}
+
+ ))}
+
+
View Details
+
+
+ );
+};
+
+export default BankServiceList;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/components/InformationCard.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/components/InformationCard.tsx
new file mode 100644
index 000000000..fa0a698eb
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/components/InformationCard.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+interface InformationCardProps {
+ logoBgColor?: string;
+ logo: React.ReactNode;
+ title: string;
+ description: string;
+ cardBgColor?: string;
+}
+
+const InformationCard: React.FC = ({
+ logoBgColor = '#E7EDFF',
+ logo,
+ title,
+ description,
+ cardBgColor = 'bg-green-200',
+}) => {
+ return (
+
+ {/* Mobile Layout */}
+
+
+ {logo}
+
+
+
{title}
+ {description}
+
+
+
+ {/* Web Layout */}
+
+
+ {logo}
+
+
+
{title}
+ {description}
+
+
+
+ );
+};
+
+export default InformationCard;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/page.tsx
new file mode 100644
index 000000000..f9abcdaf7
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingServices/page.tsx
@@ -0,0 +1,199 @@
+'use client'
+import React, { useEffect, useState } from 'react'
+import { useRouter } from "next/navigation";
+import InformationCard from './components/InformationCard'
+import BankServiceList from './components/BankServiceList'
+import { getSession } from 'next-auth/react';
+import { IconType } from 'react-icons';
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import { getBankServices } from '@/lib/api/bankServiceControl';
+
+const shimmerClass = 'bg-gray-200 animate-pulse';
+
+type infoType = {
+ id: string;
+ name: string;
+ details: string;
+ numberOfUsers: number;
+ status: string;
+ type: string;
+ icon: string;
+}
+
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+type Column = {
+ icon: IconType;
+ iconStyle: string;
+ data: DataItem[];
+};
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+const Page = () => {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setLoading] = useState(true);
+ const [bankInfo, setBankServices] = useState([]);
+
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(`./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`);
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+ console.log("bayba", access_token)
+ useEffect(() => {
+ const addingData = async () => {
+ if (!access_token) return;
+ if (access_token) {
+ const bankServices = await getBankServices(access_token, 0, 100);
+ console.log("Fetching Completed", bankServices.data.content);
+ setBankServices(bankServices.data.content); // Set the content array
+ }
+ };
+ addingData();
+ setLoading(false);
+ }, [access_token]);
+
+ if (loading) {
+ // Render shimmer effect while loading
+ return (
+
+ );
+ }
+
+
+
+
+
+ // if (!session) {
+ // router.push(`./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`);
+ // return null;
+ // }
+
+ const getBackgroundColor = (index: number) => {
+ const colors = ['bg-[#FFE0EB]', 'bg-[#E0F7FA]', 'bg-[#FFF9C4]'];
+ return colors[index % colors.length];
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ title="Life Insurance"
+ description="Unlimited protection"
+ cardBgColor="bg-[#ffffff]"
+ />
+
+
+
+
+ }
+ title="Shopping"
+ description="Buy. Think. Grow"
+ cardBgColor="bg-[#ffffff]"
+ />
+
+
+
+
+
+
+ }
+ title="Safety"
+ description="We are your allies"
+ cardBgColor="bg-[#ffffff]"
+ />
+
+
Bank Services List
+
+ {bankInfo.map((item, index) => (
+
+
+
+ )}
+ serviceName={item.name}
+ serviceDescription={item.details}
+ additionalServices={[
+ { name: `Users: ${item.numberOfUsers}`, description: "" },
+ { name: `Type: ${item.type}`, description: "" },
+ { name: `Status: ${item.status}`, description: ""},
+ ]}
+ viewDetailsLink={`https://example.com/details/${index}`}
+ />
+ ))}
+
+
+ );
+}
+
+export default Page;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/bankingSettings/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingSettings/page.tsx
new file mode 100644
index 000000000..1f424e49a
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/bankingSettings/page.tsx
@@ -0,0 +1,221 @@
+"use client";
+import React, { useState, useEffect } from 'react';
+import { useRouter } from 'next/navigation';
+import { getSession } from "next-auth/react";
+import Tabs from '../components/Tabs';
+import NotificationToggle from '../components/NotificationToggle';
+import EditProfile from '../components/EditProfile';
+import SecuritySetting from '../components/SecuritySetting';
+import { getCurrentUser, userUpdatePreference } from '../../lib/api/userControl';
+import User, { Preference } from '../../types/userInterface';
+import Refresh from '@/app/api/auth/[...nextauth]/token/RefreshToken';
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+const SettingsPage: React.FC = () => {
+ const router = useRouter();
+ const [loading, setLoading] = useState(true);
+ const [session, setSession] = useState(null);
+ const [activeTab, setActiveTab] = useState('Edit Profile');
+ const [user, setUser] = useState(null);
+ const [notifications, setNotifications] = useState({
+ currency: "",
+ sentOrReceiveDigitalCurrency: true,
+ receiveMerchantOrder: false,
+ accountRecommendations: true,
+ timeZone: "",
+ twoFactorAuthentication: false,
+ });
+ const [message, setMessage] = useState(null);
+
+ useEffect(() => {
+ const fetchSessionAndUser = async () => {
+ setLoading(true);
+
+ const sessionData = (await getSession()) as SessionDataType | null;
+
+ if (sessionData && sessionData.user) {
+ setSession(sessionData);
+ try {
+ const userData = await getCurrentUser(sessionData.user.access_token);
+ setUser(userData);
+ setNotifications(userData.preference);
+ } catch (error) {
+ console.error("Error fetching user data:", error);
+ }
+ } else {
+ router.push(`./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`);
+ }
+ setLoading(false);
+ };
+
+ fetchSessionAndUser();
+ }, [router]);
+
+ const handleTabChange = (tab: string) => setActiveTab(tab);
+
+ const handleNotificationChange = (key: keyof Preference, value: boolean | string) => {
+ setNotifications(prev => ({ ...prev, [key]: value }));
+ };
+
+ const handleTextInputChange = (key: keyof Preference, value: string) => {
+ setNotifications(prev => ({ ...prev, [key]: value }));
+ };
+
+ const handlePreferencesUpdate = async (event: React.FormEvent) => {
+ event.preventDefault();
+ setMessage(null);
+ try {
+ if (session?.user?.access_token) {
+ const accessToken = await Refresh();
+ await userUpdatePreference(notifications, accessToken);
+ setMessage('Preferences updated successfully!');
+ }
+ } catch (error) {
+ console.error('Error updating preferences:', error);
+ setMessage('Failed to update preferences. Please try again.');
+ }
+ };
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ {activeTab === 'Preferences' && (
+
+ )}
+
+ {activeTab === 'Edit Profile' &&
}
+ {activeTab === 'Security' &&
}
+
+
+
+
+ );
+};
+
+export default SettingsPage;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Context/DarkModeContext.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Context/DarkModeContext.tsx
new file mode 100644
index 000000000..010f323bb
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Context/DarkModeContext.tsx
@@ -0,0 +1,37 @@
+// DarkModeContext.tsx
+"use client"
+// DarkModeContext.tsx
+import React, { createContext, useState, useContext, ReactNode, useEffect } from 'react';
+
+interface DarkModeContextType {
+ darkMode: boolean;
+ toggleDarkMode: () => void;
+}
+
+const DarkModeContext = createContext(undefined);
+
+export const DarkModeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
+ const [darkMode, setDarkMode] = useState(false);
+
+ useEffect(() => {
+ document.body.classList.toggle('dark', darkMode);
+ }, [darkMode]);
+
+ const toggleDarkMode = () => {
+ setDarkMode(prevMode => !prevMode);
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useDarkMode = () => {
+ const context = useContext(DarkModeContext);
+ if (context === undefined) {
+ throw new Error('useDarkMode must be used within a DarkModeProvider');
+ }
+ return context;
+};
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/EditProfile.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/EditProfile.tsx
new file mode 100644
index 000000000..4d31438c2
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/EditProfile.tsx
@@ -0,0 +1,411 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import Image from "next/image";
+import { FaPencilAlt, FaCaretDown } from "react-icons/fa";
+import DatePicker from "react-datepicker";
+import "react-datepicker/dist/react-datepicker.css";
+import { useForm, Controller } from "react-hook-form";
+import User, { UserInfo } from "@/types/userInterface";
+import {
+ getCurrentUser,
+ getUserByUsername,
+ userUpdate,
+} from "@/lib/api/userControl";
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import { storage } from "../firebase";
+import {
+ ref,
+ uploadBytes,
+ getDownloadURL,
+ deleteObject,
+} from "firebase/storage";
+import { v4 } from "uuid";
+interface FormData {
+ name: string;
+ username: string;
+ email: string;
+ city: string;
+ dateOfBirth: Date | null;
+ presentAddress: string;
+ permanentAddress: string;
+ country: string;
+ postalCode: string;
+ profilePicture: string | File;
+}
+
+const EditProfile = () => {
+ const { control, handleSubmit, setValue } = useForm();
+ const [user, setUser] = useState(null);
+ const [accessToken, setAccessToken] = useState("");
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const accessToken = await Refresh();
+ setAccessToken(accessToken);
+ } catch (error) {
+ console.error("Error fetching token:", error);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ if (accessToken) {
+ const currentUser = await getCurrentUser(accessToken);
+ const userData = await getUserByUsername(
+ currentUser.username,
+ accessToken
+ );
+ setUser(userData);
+
+ // Populate form fields
+ setValue("name", userData.name || "");
+ setValue("username", userData.username || "");
+ setValue("email", userData.email || "");
+ setValue("city", userData.city || "");
+ setValue(
+ "dateOfBirth",
+ userData.dateOfBirth ? new Date(userData.dateOfBirth) : null
+ );
+ setValue("presentAddress", userData.presentAddress || "");
+ setValue("permanentAddress", userData.permanentAddress || "");
+ setValue("country", userData.country || "");
+ setValue("postalCode", userData.postalCode || "");
+ }
+ } catch (error) {
+ console.error("Error fetching user data:", error);
+ }
+ };
+
+ fetchData();
+ }, [accessToken, setValue]);
+
+ const onSubmit = async (data: FormData) => {
+ try {
+ if (data.profilePicture != null) {
+ // Update the data with the download URL for profilePicture
+ const updatedData = {
+ ...data,
+ profilePicture: String(data.profilePicture),
+ dateOfBirth: data.dateOfBirth
+ ? data.dateOfBirth.toISOString().split("T")[0]
+ : null,
+ };
+
+ // Send the updated data to userUpdate
+ await userUpdate(updatedData, accessToken);
+ }
+
+ console.log("Profile updated successfully");
+ alert("Profile Edited Successfully");
+ } catch (error) {
+ console.error("Error updating profile:", error);
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+ {/* Hidden file input for selecting a new profile picture */}
+ ) => {
+ const file = e.target.files?.[0]; // Check if files exist
+ if (file) {
+ setValue("profilePicture", file);
+
+ // Immediately upload the image to Firebase
+ try {
+ const imageRef = ref(storage, `images/${file.name}-${v4()}`);
+ await uploadBytes(imageRef, file);
+
+ // Get the download URL after the image is uploaded
+ const downloadUrl = await getDownloadURL(imageRef);
+ console.log("dowloaded the url ", downloadUrl);
+
+ // Update the profile picture in the form state and in the UI
+ setValue("profilePicture", downloadUrl);
+ setUser(
+ (prev) => prev && { ...prev, profilePicture: downloadUrl }
+ );
+ } catch (error) {
+ console.error("Error uploading image:", error);
+ }
+ }
+ }}
+ style={{ display: "none" }} // Hide the input
+ id="profilePictureInput"
+ />
+
+ {/* Label for the file input, styled as an edit icon */}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default EditProfile;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Navbar.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Navbar.tsx
new file mode 100644
index 000000000..eda00652e
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Navbar.tsx
@@ -0,0 +1,172 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import { MdOutlineSearch } from "react-icons/md";
+import { GiHamburgerMenu } from "react-icons/gi";
+import {
+ IoLogOutOutline,
+ IoMoonOutline,
+ IoSettingsOutline,
+} from "react-icons/io5";
+import { IoMdNotificationsOutline } from "react-icons/io";
+import { useRouter } from "next/navigation";
+import { signOut } from "next-auth/react";
+interface Props {
+ handleClick: () => void;
+ toggleDarkMode: () => void;
+}
+import Image from "next/image";
+import { UserInfo } from "@/types/userInterface";
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import { getCurrentUser, getUserByUsername } from "@/lib/api/userControl";
+const Navbar = ({ handleClick, toggleDarkMode }: Props) => {
+ const route = useRouter();
+ const [user, setUser] = useState(null);
+ const [accessToken, setAccessToken] = useState("");
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const accessToken = await Refresh();
+ setAccessToken(accessToken);
+ } catch (error) {
+ console.error("Error fetching token:", error);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ if (accessToken) {
+ const currentUser = await getCurrentUser(accessToken);
+ const userData = await getUserByUsername(
+ currentUser.username,
+ accessToken
+ );
+ setUser(userData);
+ }
+ } catch (error) {
+ console.error("Error fetching user data:", error);
+ }
+ };
+
+ fetchData();
+ }, [accessToken]);
+ console.log("Profile Picture", user?.profilePicture);
+ return (
+
+
+
+
+
+
+
+
+ Overview
+
+
+
+
+
+
+
+
+
+
route.push("./bankingSettings")}
+ >
+
+
+
+
+
+
+
+
+
{
+ signOut();
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const NavBarLoading = () => {
+ return (
+
+ );
+};
+
+export default Navbar;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Navigation.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Navigation.tsx
new file mode 100644
index 000000000..572fd4e55
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Navigation.tsx
@@ -0,0 +1,99 @@
+"use client";
+import React, { useState, useEffect } from "react";
+import Navbar, { NavBarLoading } from "./Navbar";
+import Sidebar, { SidebarLoading } from "./Sidebar";
+import { getSession } from "next-auth/react";
+import Loading from "../accounts/components/Loading";
+import { useDarkMode } from "./Context/DarkModeContext";
+
+interface Props {
+ children: React.ReactNode;
+}
+
+const Navigation: React.FC = ({ children }) => {
+ const [toggle, setToggle] = useState(false);
+ const [session, setSession] = useState(false);
+ const [loading, setLoading] = useState(true);
+ const { darkMode, toggleDarkMode } = useDarkMode(); // Use dark mode context
+
+ useEffect(() => {
+ const fetchSession = async () => {
+ const sessionData = await getSession();
+ if (sessionData?.user) {
+ setSession(true);
+ }
+ setLoading(false);
+ };
+
+ fetchSession();
+ }, []);
+
+ return (
+ <>
+ {loading ? (
+
+ ) : (
+
+ {session && (
+
+ {
+ setToggle(!toggle);
+ }}
+ />
+
+ )}
+ {toggle && (
+
+ )}
+
+
+ {session && (
+
+ {
+ setToggle(!toggle);
+ }}
+ toggleDarkMode={toggleDarkMode}
+ />
+
+ )}
+ {children}
+
+
+ )}
+ >
+ );
+};
+
+export default Navigation;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/NotificationToggle.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/NotificationToggle.tsx
new file mode 100644
index 000000000..489c0bbec
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/NotificationToggle.tsx
@@ -0,0 +1,49 @@
+"use client";
+import React, { useState } from "react";
+
+interface NotificationToggleProps {
+ id: string;
+ label: string;
+ checked: boolean;
+ onChange: (checked: boolean) => void;
+}
+
+const NotificationToggle: React.FC = ({ id, label, checked, onChange }) => {
+ const [enabled, setEnabled] = useState(checked);
+
+ const handleToggle = () => {
+ const newChecked = !enabled;
+ setEnabled(newChecked);
+ onChange(newChecked);
+ };
+
+ return (
+
+
+
+
+
+
+
+ {label}
+
+
+ );
+};
+
+export default NotificationToggle;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/BarChart.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/BarChart.tsx
new file mode 100644
index 000000000..749048d6b
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/BarChart.tsx
@@ -0,0 +1,125 @@
+import React, { useState, useEffect } from 'react';
+import { Bar } from 'react-chartjs-2';
+import { Chart as ChartJS, BarElement, CategoryScale, LinearScale, Tooltip, Legend, TooltipItem } from 'chart.js';
+import { getBalanceHistory } from '@/lib/api/transactionController'; // Import the function
+
+interface BalanceHistoryData {
+ time: string;
+ value: number;
+}
+
+ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend);
+
+// Utility function to get the last six months' labels
+const getLastSixMonthsLabels = (): string[] => {
+ const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+ const currentMonth = new Date().getMonth(); // Get the current month (0-11)
+ const labels = [];
+
+ for (let i = 5; i >= 0; i--) {
+ const monthIndex = (currentMonth - i + 5) % 12;
+ labels.push(monthNames[monthIndex]);
+ }
+
+ return labels;
+};
+
+// Shimmer component with vertical bars
+const Shimmer = () => {
+ return (
+
+ {[...Array(6)].map((_, index) => (
+
+ ))}
+
+ );
+};
+
+const BarChart: React.FC<{ token: string }> = ({ token }) => {
+ const [chartData, setChartData] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [labels, setLabels] = useState(getLastSixMonthsLabels());
+
+ useEffect(() => {
+ const fetchBalanceHistory = async () => {
+ try {
+ const response = await getBalanceHistory(token); // Fetching data for the last 6 months
+ const data = response.data;
+
+
+ const sortedData = data
+ .sort((a: BalanceHistoryData, b: BalanceHistoryData) => new Date(a.time).getMonth() - new Date(b.time).getMonth())
+ .map((entry: BalanceHistoryData) => entry.value + 1000);
+
+ setChartData(sortedData.length > 0 ? sortedData : [0, 0, 0, 0, 0, 0]);
+ } catch (error) {
+ console.error('Error fetching balance history:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchBalanceHistory();
+ }, [token]);
+
+ const maxValueIndex = chartData.indexOf(Math.max(...chartData));
+
+ const data = {
+ labels: labels,
+ datasets: [
+ {
+ label: 'Balance History',
+ data: chartData,
+ backgroundColor: chartData.map((_, index) =>
+ index === maxValueIndex ? 'rgba(0, 204, 204, 0.8)' : 'rgba(0, 0, 0, 0.05)'
+ ),
+ hoverBackgroundColor: chartData.map((_, index) =>
+ index === maxValueIndex ? 'rgba(0, 204, 204, 1)' : 'rgba(0, 204, 204, 0.5)'
+ ),
+ borderRadius: 10,
+ borderSkipped: false,
+ barPercentage: 0.6,
+ },
+ ],
+ };
+
+ const options = {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false,
+ },
+ tooltip: {
+ callbacks: {
+ label: function (tooltipItem: TooltipItem<'bar'>) {
+ const value = tooltipItem.raw as number;
+ return `$${value.toLocaleString()}`;
+ },
+ },
+ },
+ },
+ scales: {
+ x: {
+ grid: {
+ display: false,
+ },
+ ticks: {
+ color: 'rgba(0, 0, 0, 0.5)',
+ },
+ },
+ y: {
+ beginAtZero: true,
+ display: false,
+ },
+ },
+ };
+
+ return (
+
+ {loading ? : }
+
+ );
+};
+
+export default BarChart;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/Card.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/Card.tsx
new file mode 100644
index 000000000..c78e58e61
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/Card.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+
+interface CardProps {
+ balance?: string;
+ cardHolder?: string;
+ validThru?: string;
+ cardNumber?: string;
+ filterClass?: string;
+ bgColor?: string;
+ textColor?: string;
+ iconBgColor?: string;
+ showIcon?: boolean;
+ loading?: boolean;
+}
+
+const CreditCard: React.FC = ({
+ balance,
+ cardHolder,
+ validThru,
+ cardNumber,
+ filterClass = "filter-white",
+ bgColor = '',
+ textColor = 'text-white',
+ iconBgColor = 'bg-opacity-10',
+ showIcon = true,
+ loading = false,
+}) => {
+
+ const isBlueGradient = bgColor.includes('#4C49ED') || bgColor.includes('#0A06F4');
+ const ellipseImageSrc = isBlueGradient ? '/group17.svg' : '/group18.svg';
+ const iconSrc = isBlueGradient ? '/sim.svg' : '/blackSim.svg';
+
+ const cardHolderTextColor = isBlueGradient ? 'text-[rgba(255,255,255,0.7)]' : 'text-[#718EBF]';
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ return (
+
+ {/* Set position relative here to contain the absolute positioned element */}
+ {/*
*/}
+
+
+
+ {showIcon && (
+
+
+
+ )}
+
+
+
+
+
CARD HOLDER
+
{cardHolder}
+
+
+
VALID THRU
+
{validThru}
+
+
+
+
+ {/* The absolute element stays within the bounds of the relative parent */}
+
+
+ {cardNumber}
+
+
+
+
+
+
+
+ );
+};
+
+export default CreditCard;
+
+
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/Table.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/Table.tsx
new file mode 100644
index 000000000..af1f94847
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/Table.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+
+interface Column
{
+ Header: string;
+ accessor: keyof T;
+ Cell?: (props: { [key: string]: any }) => React.ReactNode;
+}
+
+interface TableProps {
+ columns: Column[];
+ data: T[];
+}
+
+const Table = ({ columns, data }: TableProps) => {
+ return (
+
+
+
+ {/* Table Header */}
+
+
+ {columns.map((column, index) => (
+
+ {column.Header}
+
+ ))}
+
+
+
+ {/* Table Body */}
+
+ {(data || []).map((row, rowIndex) => (
+
+ {columns.map((column, colIndex) => (
+
+ {column.Cell
+ ? column.Cell(row)
+ : (row[column.accessor] as unknown as React.ReactNode)}
+
+ ))}
+
+ ))}
+
+
+
+
+ );
+};
+
+export default Table;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/TransactionsList.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/TransactionsList.tsx
new file mode 100644
index 000000000..1bc02df80
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/TransactionsList.tsx
@@ -0,0 +1,295 @@
+import React, { useState, useEffect } from 'react';
+
+interface Transaction {
+ description: string;
+ transactionId: string;
+ type: string;
+ receiverUserName?: string;
+ senderUserName?: string;
+ date: string;
+ amount?: string;
+ receipt?: string;
+}
+
+interface TransactionsListProps {
+ transactions: Transaction[];
+ loading: boolean;
+ activeTab: string;
+}
+
+const TransactionsList: React.FC = ({ transactions, loading, activeTab }) => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const itemsPerPage = 5;
+ const totalPages = Math.ceil(transactions.length / itemsPerPage);
+
+ // Adjust amount based on activeTab
+ const adjustedTransactions = transactions.map(transaction => {
+ let amount = parseFloat(transaction.amount || '0');
+
+ if (activeTab === 'Income') {
+ amount = Math.abs(amount); // Ensure amount is positive for income
+ } else if (activeTab === 'Expense') {
+ amount = -Math.abs(amount); // Ensure amount is negative for expense
+ }
+
+ return {
+ ...transaction,
+ amount: amount.toFixed(2) // Format to 2 decimal places for consistency
+ };
+ });
+
+ const paginatedTransactions = adjustedTransactions.slice(
+ (currentPage - 1) * itemsPerPage,
+ currentPage * itemsPerPage
+ );
+
+ const handlePageClick = (pageNumber: number) => {
+ setCurrentPage(pageNumber);
+ };
+
+ const handleNextPage = () => {
+ if (currentPage < totalPages) {
+ setCurrentPage(currentPage + 1);
+ }
+ };
+
+ const handlePreviousPage = () => {
+ if (currentPage > 1) {
+ setCurrentPage(currentPage - 1);
+ }
+ };
+
+ const renderPagination = () => {
+ const pageNumbers: (number | string)[] = [];
+ const maxPageNumbersToShow = 3;
+
+ if (totalPages <= maxPageNumbersToShow) {
+ for (let i = 1; i <= totalPages; i++) {
+ pageNumbers.push(i);
+ }
+ } else {
+ let startPage = Math.max(1, currentPage - 2);
+ let endPage = Math.min(totalPages, currentPage + 2);
+
+ if (currentPage <= 3) {
+ startPage = 1;
+ endPage = maxPageNumbersToShow;
+ } else if (currentPage >= totalPages - 2) {
+ startPage = totalPages - maxPageNumbersToShow + 1;
+ endPage = totalPages;
+ }
+
+ for (let i = startPage; i <= endPage; i++) {
+ pageNumbers.push(i);
+ }
+
+ if (startPage > 1) {
+ pageNumbers.unshift(1);
+ if (startPage > 2) {
+ pageNumbers.splice(1, 0, '...');
+ }
+ }
+
+ if (endPage < totalPages) {
+ pageNumbers.push(totalPages);
+ if (endPage < totalPages - 1) {
+ pageNumbers.splice(pageNumbers.length - 1, 0, '...');
+ }
+ }
+ }
+
+ return pageNumbers.map((pageNumber, index) =>
+ typeof pageNumber === 'number' ? (
+ handlePageClick(pageNumber)}
+ className={`px-3 py-1 rounded ${pageNumber === currentPage ? 'bg-[#1814F3] text-white' : 'text-blue-600 dark:text-[#9faaeb]'}`}
+ >
+ {pageNumber}
+
+ ) : (
+
+ {pageNumber}
+
+ )
+ );
+ };
+
+ const columns = ['Description', 'Transaction ID', 'Type', 'Card', 'Date', 'Amount', 'Receipt'];
+
+ return (
+
+ {loading ? (
+
+ {/* Shimmer effect for loading state */}
+
+
+
+
+
+ {columns.map((column, index) => (
+
+ {column}
+
+ ))}
+
+
+
+ {[...Array(5)].map((_, rowIndex) => (
+
+ {columns.map((_, colIndex) => (
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+
+ {/* Mobile View */}
+
+ {[...Array(5)].map((_, index) => (
+
+ ))}
+
+
+ ) : (
+
+ {/* Table rendering */}
+ {paginatedTransactions.length > 0 && (
+ <>
+
+
+
+
+
+ {columns.map((column, index) => (
+
+ {column}
+
+ ))}
+
+
+
+ {paginatedTransactions.map((row, rowIndex) => {
+ const amount = row.amount || '0';
+ const isPositive = parseFloat(amount) > 0;
+
+ return (
+
+
+
+
+
+
+
{row.description}
+
+
+ {row.transactionId}
+ {row.type}
+ {row.receiverUserName || 'Unknown'}
+ {row.date}
+
+
+ {isPositive ? `+${amount}` : `${amount}`}
+
+
+
+ Download
+
+
+ );
+ })}
+
+
+
+
+
+ {/* Mobile View */}
+
+ {paginatedTransactions.map((transaction, index) => {
+ const amount = transaction.amount || '0';
+ const isPositive = parseFloat(amount) > 0;
+
+ return (
+
+
+
+
+
+
+
+
{transaction.description}
+
{transaction.date}
+
+
+
+ {isPositive ? `+${amount}` : `${amount}`}
+
+
+
+ );
+ })}
+
+ >
+ )}
+
+ {/* Pagination */}
+
+
+
+
+
+ Previous
+
+ {renderPagination()}
+
+ Next
+
+
+
+
+
+
+ )}
+
+ );
+};
+
+export default TransactionsList;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/WhiteCard.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/WhiteCard.tsx
new file mode 100644
index 000000000..efa731dca
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Page2/WhiteCard.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import Card from './Card';
+
+const WhiteCard = () => {
+ return (
+
+ );
+};
+
+export default WhiteCard;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/SecuritySetting.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/SecuritySetting.tsx
new file mode 100644
index 000000000..524899037
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/SecuritySetting.tsx
@@ -0,0 +1,208 @@
+"use client";
+import React, { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import {
+ userUpdatePreference,
+ getCurrentUser,
+} from "../../lib/api/userControl";
+import { getSession } from "next-auth/react";
+import { changePassword } from "../../lib/api/authenticationController";
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+const HeadingLabel = ({ label }: { label: string }) => {
+ return (
+
+ {label}
+
+ );
+};
+
+const InputLabel = ({ label, htmlFor }: { label: string; htmlFor: string }) => {
+ return (
+
+ {label}
+
+ );
+};
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+
+const ToggleSwitch = ({
+ handleToogle,
+ enabled,
+}: {
+ enabled: boolean;
+ handleToogle: Function;
+}) => {
+ const toggleSwitch = () => {
+ handleToogle(!enabled);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+interface SecurityFormInputs {
+ old_password: string;
+ new_password: string;
+}
+
+const SecuritySetting = () => {
+ const [session, setSession] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ reset,
+ } = useForm();
+ const router = useRouter();
+
+ const [enabled, setEnabled] = useState(false);
+
+ useEffect(() => {
+ const fetchSession = async () => {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ setLoading(false);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ const handleToogle = (value: boolean) => {
+ setEnabled(value);
+ };
+
+ const onSubmit = async (data: SecurityFormInputs) => {
+ const user = await getCurrentUser(session?.access_token!);
+ user.preference.twoFactorAuthentication = enabled;
+ await userUpdatePreference(user.preference, session?.access_token!);
+ await changePassword(
+ { password: data.old_password, newPassword: data.new_password },
+ session?.access_token!
+ );
+ reset();
+ };
+
+ if (loading) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export default SecuritySetting;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Sidebar.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Sidebar.tsx
new file mode 100644
index 000000000..1a566f231
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Sidebar.tsx
@@ -0,0 +1,160 @@
+import React, { useState } from "react";
+import Image from "next/image";
+import {
+ MdHome,
+ MdAttachMoney,
+ MdAccountBalance,
+ MdAssessment,
+ MdCreditCard,
+ MdPayment,
+ MdStar,
+ MdSettings,
+} from "react-icons/md";
+import { FaTimes } from "react-icons/fa";
+import { RiHandCoinLine } from "react-icons/ri";
+import { usePathname, useRouter } from "next/navigation";
+import SidebarElements from "./SidebarElements";
+
+interface SidebarProps {
+ toggle: boolean;
+ handleClose: () => void;
+}
+const Sidebar = ({ toggle, handleClose }: SidebarProps) => {
+
+const map = new Map();
+map.set("/", "Dashboard");
+map.set("/dashboard", "Dashboard");
+map.set("/transactions", "Transactions");
+map.set("/accounts", "Accounts");
+map.set("/investments", "Investments");
+map.set("/creditCards", "Credit Cards");
+map.set("/loans", "Loans");
+map.set("/bankingServices", "Services");
+map.set("/bankingSettings", "Settings");
+
+const pathName = usePathname();
+const router = useRouter();
+const [active, setActive] = useState(map.get(pathName));
+
+ const elements = [
+ {
+ id: 1,
+ text: "Dashboard",
+ destination: "./dashboard",
+ icon: MdHome,
+ },
+ {
+ id: 2,
+ text: "Transactions",
+ destination: "./transactions",
+ icon: MdAttachMoney,
+ },
+ {
+ id: 3,
+ text: "Accounts",
+ destination: "./accounts",
+ icon: MdAccountBalance,
+ },
+ {
+ id: 4,
+ text: "Investments",
+ destination: "./investments",
+ icon: MdAssessment,
+ },
+ {
+ id: 5,
+ text: "Credit Cards",
+ destination: "./creditCards",
+ icon: MdCreditCard,
+ },
+ {
+ id: 6,
+ text: "Loans",
+ destination: "./loans",
+ icon: RiHandCoinLine,
+ },
+ {
+ id: 7,
+ text: "Services",
+ destination: "./bankingServices",
+ icon: MdPayment,
+ },
+ {
+ id: 8,
+ text: "Settings",
+ destination: "./bankingSettings",
+ icon: MdSettings,
+ },
+ ];
+
+ const handleNav = async (destination: string) => {
+ router.push(destination);
+ };
+ const handleActive = (element: string) => {
+ setActive(element);
+ handleClose();
+ };
+
+ return (
+ <>
+
+
+ {toggle && (
+
+ )}
+ >
+ );
+};
+
+export const SidebarLoading = () => {
+ return (
+
+ );
+};
+
+export default Sidebar;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/SidebarElements.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/SidebarElements.tsx
new file mode 100644
index 000000000..d555869c1
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/SidebarElements.tsx
@@ -0,0 +1,90 @@
+import { useRouter } from "next/navigation";
+import React from "react";
+import { IconType } from "react-icons";
+import { IoLogOutOutline, IoMoonOutline, IoSettingsOutline } from "react-icons/io5";
+import { useDarkMode } from "./Context/DarkModeContext";
+import { signOut } from "next-auth/react";
+
+type ElementType = {
+ id: number;
+ text: string;
+ destination: string;
+ icon: IconType;
+};
+
+interface Props {
+ handleNav: (s: string) => void;
+ handleActive: (s: string) => void;
+ elements: ElementType[];
+ active: string;
+}
+
+const SidebarElements = ({
+ handleActive,
+ handleNav,
+ elements,
+ active,
+}: Props) => {
+ const route = useRouter();
+ const { darkMode, toggleDarkMode } = useDarkMode(); // Use dark mode context
+
+ return (
+
+ {elements.map((el) => (
+
+
{
+ handleActive(el.text);
+ handleNav(el.destination);
+ }}
+ className={`flex items-center w-full`}
+ >
+
+
+
+
+
+ {el.text.charAt(0).toUpperCase() + el.text.slice(1)}
+
+
+
+ ))}
+
+
route.push("./bankingSettings")}
+ >
+
+
+ {/*
+
+
*/}
+
+
+
+
{
+ signOut();
+ }}
+ >
+
+
+
+
+ );
+};
+
+export default SidebarElements;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/Tabs.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Tabs.tsx
new file mode 100644
index 000000000..a23959666
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/Tabs.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+interface TabsProps {
+ tabs: string[];
+ activeTab: string;
+ onTabChange: (tab: string) => void;
+}
+
+const Tabs: React.FC = ({ tabs, activeTab, onTabChange }) => {
+ return (
+
+ {tabs.map((tab) => (
+ onTabChange(tab)}
+ >
+ {tab}
+
+ ))}
+
+ );
+};
+
+export default Tabs;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/TextInput.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/TextInput.tsx
new file mode 100644
index 000000000..421e9316c
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/TextInput.tsx
@@ -0,0 +1,31 @@
+// components/TextInput.tsx
+import React from 'react';
+
+interface TextInputProps {
+ id: string;
+ label: string;
+ value: string;
+ placeholder?: string;
+ readOnly?: boolean;
+ onChange?: (event: React.ChangeEvent) => void;
+}
+
+const TextInput: React.FC = ({ id, label, value, placeholder, readOnly = false, onChange }) => {
+ return (
+
+ {label}
+
+
+ );
+};
+
+export default TextInput;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/components/loadingprovider/ProgressBar.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/components/loadingprovider/ProgressBar.tsx
new file mode 100644
index 000000000..458f834d6
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/components/loadingprovider/ProgressBar.tsx
@@ -0,0 +1,23 @@
+"use client";
+import React, { useEffect } from "react";
+import { Next13ProgressBar } from "next13-progressbar";
+
+const ProgressBar = ({ children }: { children: React.ReactNode }) => {
+ useEffect(() => {
+ console.log("ProgressBar rendered");
+ }, []);
+
+ return (
+ <>
+ {children}
+
+ >
+ );
+};
+
+export default ProgressBar;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/AddCardForm.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/AddCardForm.tsx
new file mode 100644
index 000000000..4f0d1ed76
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/AddCardForm.tsx
@@ -0,0 +1,162 @@
+import React from "react";
+import { useForm } from "react-hook-form";
+import { PostCardRequest } from "../../types/cardController.Interface";
+import { postCard } from "../../lib/api/cardController";
+
+export const InputLabel = ({ label }: { label: string }) => {
+ return (
+
+ {label}
+
+ );
+};
+
+const AddCardForm = ({
+ access_token,
+ handleAddition,
+}: {
+ access_token: string;
+ handleAddition: Function;
+}) => {
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ reset,
+ } = useForm();
+
+ const onSubmit_ = handleSubmit(async (data) => {
+ console.log(data.passcode);
+ data.passcode = "192930";
+ const response = await postCard(data, access_token);
+ handleAddition(response);
+ reset();
+ });
+
+ return (
+
+
+ Credit Card generally means a plastic card issued by Scheduled
+ Commercial Banks assigned to a Cardholder, with a credit limit, that can
+ be used to purchase goods and services on credit or obtain cash
+ advances.
+
+
+
+
+ );
+};
+
+export default AddCardForm;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CardSetting.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CardSetting.tsx
new file mode 100644
index 000000000..d666bb8c1
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CardSetting.tsx
@@ -0,0 +1,28 @@
+import Link from "next/link";
+import React, { ReactNode } from "react";
+
+interface Props {
+ icon: ReactNode;
+ data: Array<[string, string]>;
+}
+const CardSetting = ({ icon, data }: Props) => {
+ return (
+
+
{icon}
+
+ {data.map((data, index) => {
+ return (
+
0 && "hidden"} lg:block`} key={index}>
+
+ {data[0]}
+
+
{data[1]}
+
+ );
+ })}
+
+
+ );
+};
+
+export default CardSetting;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CardSettingList.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CardSettingList.tsx
new file mode 100644
index 000000000..c0aec4ffc
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CardSettingList.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import CardSetting from "./CardSetting";
+
+const CardList = () => {
+ return (
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+
+ );
+};
+
+export default CardList;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CreditCard.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CreditCard.tsx
new file mode 100644
index 000000000..512572d1e
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/CreditCard.tsx
@@ -0,0 +1,144 @@
+import Link from "next/link";
+import React, { ReactNode } from "react";
+
+interface Props {
+ icon: ReactNode;
+ data: Array<[string, string]>;
+ handleDetail: Function;
+ card: Card;
+ initialValue: boolean;
+}
+
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Card } from "@/types/cardController.Interface";
+
+const DetailCard = ({
+ handleClick,
+ initialValue,
+ card,
+}: {
+ handleClick: Function;
+ initialValue: boolean;
+ card: Card;
+}) => {
+ return (
+ {
+ handleClick(false);
+ }}
+ >
+
+
+
+ Card Details
+
+
+
+
+ Card Holder
+
+
+ {card.cardHolder}
+
+
+
+
+ Balance
+
+
+ ${card.balance.toFixed(2)}
+
+
+
+
+
+
+ Card Number
+
+
+ {card.expiryDate}
+
+
+
+
+ Card Type
+
+
+ **** **** **** {card.semiCardNumber}
+
+
+
+
handleClick(false)}
+ className="mt-6 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition"
+ >
+ Close
+
+
+
+
+ );
+};
+
+const CreditCard = ({
+ icon,
+ data,
+ handleDetail,
+ card,
+ initialValue,
+}: Props) => {
+ return (
+ <>
+
+
+ {icon}
+
+ {data.map((data, index) => {
+ return (
+
1 && "hidden"} lg:block`} key={index}>
+
+ {data[0]}
+
+
{data[1]}
+
+ );
+ })}
+
+
{
+ handleDetail(true);
+ }}
+ >
+ View Detail
+
+
+ >
+ );
+};
+
+export default CreditCard;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/MainCreditCard.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/MainCreditCard.tsx
new file mode 100644
index 000000000..f4e0b813f
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/MainCreditCard.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+
+interface Props {
+ color1: string;
+ color2: string;
+ balance: number;
+ validThru: string;
+ cardNumber: string;
+ cardHolder: string;
+}
+
+const MainCreditCard = ({
+ color1,
+ color2,
+ balance,
+ validThru,
+ cardNumber,
+ cardHolder,
+}: Props) => {
+ return (
+
+
+
+
Balance
+
+ {"$"}
+ {balance}
+
+
+
+
+
CARD HOLDER
+
{cardHolder}
+
+
+
VALID THRU
+
{validThru}
+
+
+
+
{cardNumber}
+
+
+
+
+
+ );
+};
+
+export default MainCreditCard;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/PieChart.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/PieChart.tsx
new file mode 100644
index 000000000..19ee6ec4e
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/PieChart.tsx
@@ -0,0 +1,189 @@
+"use client";
+
+import * as React from "react";
+import { Label, Pie, PieChart, Sector } from "recharts";
+import { PieSectorDataItem } from "recharts/types/polar/Pie";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartStyle,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+const desktopData = [
+ { month: "ABM Bank", desktop: 186, fill: "#16DBCC" },
+ { month: "DBL Bank", desktop: 305, fill: "#4C78FF" },
+ { month: "MCP Bank", desktop: 237, fill: "#FFBB38" },
+ { month: "BRC Bank", desktop: 173, fill: "#FF82AC" },
+];
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ desktop: {
+ label: "Desktop",
+ },
+ mobile: {
+ label: "Mobile",
+ },
+ january: {
+ label: "January",
+ color: "hsl(var(--chart-1))",
+ },
+ february: {
+ label: "February",
+ color: "hsl(var(--chart-2))",
+ },
+ march: {
+ label: "March",
+ color: "hsl(var(--chart-3))",
+ },
+ april: {
+ label: "April",
+ color: "hsl(var(--chart-4))",
+ },
+ may: {
+ label: "May",
+ color: "hsl(var(--chart-5))",
+ },
+} satisfies ChartConfig;
+
+export function PieChartPage() {
+ const id = "pie-interactive";
+ const [activeMonth, setActiveMonth] = React.useState(desktopData[0].month);
+
+ const activeIndex = React.useMemo(
+ () => desktopData.findIndex((item) => item.month === activeMonth),
+ [activeMonth]
+ );
+ const months = React.useMemo(() => desktopData.map((item) => item.month), []);
+
+ return (
+
+
+
+
+
+
+
+ }
+ />
+ (
+
+
+
+
+ )}
+ >
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {desktopData[activeIndex].desktop.toLocaleString()}
+
+
+ Visitors
+
+
+ );
+ }
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export const ChartLabel = ({
+ colors,
+ labels,
+}: {
+ colors: string[];
+ labels: string[];
+}) => {
+ return (
+
+ {colors.map((color, index) => (
+
+
+
+ {labels[index]}
+
+
+ ))}
+
+ );
+};
+
+export default PieChartPage;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/Shimmer.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/Shimmer.tsx
new file mode 100644
index 000000000..d04058ea0
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/Shimmer.tsx
@@ -0,0 +1,69 @@
+export const ShimmerCreditCard = () => {
+ return (
+
+ );
+};
+
+export const ShimmerMainCreditCard = () => {
+ return (
+
+ );
+};
+
+export const ShimmerPieChartPage = () => {
+ return (
+
+ );
+};
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/page.tsx
new file mode 100644
index 000000000..2f51e571d
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/creditCards/page.tsx
@@ -0,0 +1,203 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import PieChart from "./PieChart";
+import CreditCard from "./CreditCard";
+import CardSettingList from "./CardSettingList";
+import AddCardForm from "./AddCardForm";
+import MainCreditCard from "./MainCreditCard";
+import Card from "../components/Page2/Card";
+import { Card as Card1 } from "../../types/cardController.Interface";
+import { getCards } from "@/lib/api/cardController";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import {
+ ShimmerCreditCard,
+ ShimmerMainCreditCard,
+ ShimmerPieChartPage,
+} from "./Shimmer";
+
+const HeadingTitle = ({ title }: { title: string }) => {
+ return (
+
+ {title}
+
+ );
+};
+
+const CreditCards = () => {
+ const [accessToken, setAccessToken] = useState("");
+ const [cards, setCards] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const router = useRouter();
+ const [searchTerm, setSearchTerm] = useState("");
+ const [dialogOpen, setDialogOpen] = useState(false);
+
+ const filteredCards = cards.filter(
+ (card) =>
+ card.cardType.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ card.semiCardNumber.toString().includes(searchTerm)
+ );
+ const handleDialogToogle = (value: boolean) => {
+ setDialogOpen(value);
+ };
+ const convertToDate = (date: string) => {
+ const year = date.slice(2, 4);
+ const month = date.slice(5, 7);
+
+ return month + "/" + year;
+ };
+ useEffect(() => {
+ const fetchSession = async () => {
+ const access_token = await Refresh();
+ if (access_token) {
+ setAccessToken(access_token);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/creditCards")}`
+ );
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ useEffect(() => {
+ if (accessToken == "") {
+ return;
+ }
+ async function fetch() {
+ const data = await getCards(accessToken, 0, 700);
+ data.content.reverse();
+ setCards(data.content);
+ setLoading(false);
+ }
+ fetch();
+ }, [accessToken]);
+
+ const decideColor = (index: number) => {
+ const remainder = index % 3;
+ if (remainder == 0) {
+ return ["from-[#0A06F4] to-[#0A06F4]", "text-white"];
+ } else if (remainder == 1) {
+ return ["from-[#4C49ED] to-[#4C49ED]", "text-white"];
+ } else {
+ return ["from-[#FFF] to-[#FFF]", "text-black"];
+ }
+ };
+ const handleAddition = (card: Card1) => {
+ const newCards = [card, ...cards];
+ setCards(newCards);
+ };
+
+ if (accessToken == "" && loading == false) {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/creditCards")}`
+ );
+ }
+ return (
+
+
+
+
+
+ {loading ? (
+
+ {Array(3)
+ .fill(0)
+ .map((_, index) => (
+
+ ))}
+
+ ) : (
+ cards.map((card, index) => {
+ const [bgColor, textColor] = decideColor(index);
+
+ if (index <= 2) {
+ return (
+
+ );
+ }
+ })
+ )}
+
+
+
+
+
+
+
+ setSearchTerm(e.target.value)}
+ className="p-2 border border-gray-300 rounded-lg basis-4/12 dark:bg-[#313245] dark:border dark:border-[#333B69]"
+ />
+
+
+ {loading ? (
+
+ {Array(4)
+ .fill(0)
+ .map((_, index) => (
+
+ ))}
+
+ ) : filteredCards.length == 0 ? (
+
No Available Cards!
+ ) : (
+ filteredCards.map((card, index) => (
+
}
+ handleDetail={handleDialogToogle}
+ data={[
+ ["Card Type", card.cardType],
+ ["Balance", card.balance.toString()],
+ ["Card Number", card.semiCardNumber],
+ ["Card Expiry", convertToDate(card.expiryDate)],
+ ]}
+ card={card}
+ initialValue={dialogOpen}
+ key={index}
+ />
+ ))
+ )}
+
+
+
+
+
+
+ );
+};
+
+export default CreditCards;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/BalanceHistory.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/BalanceHistory.tsx
new file mode 100644
index 000000000..3534bd48e
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/BalanceHistory.tsx
@@ -0,0 +1,150 @@
+"use client";
+import { TrendingUp } from "lucide-react";
+import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+import Refresh from "@/app/api/auth/[...nextauth]/token/RefreshToken";
+import { getBalanceHistory } from "@/lib/api/transactionController";
+
+
+
+const chartConfig = {
+ Balance: {
+ label: "Balance",
+ color: "hsl(var(--chart-1))",
+ },
+} satisfies ChartConfig;
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+// eslint-disable-next-line react-hooks/rules-of-hooks
+
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+type SessionDataType = {
+ user: Data;
+};
+export function BalanceHistory() {
+ const [chartData, setChartData] = useState<{ month: string; balance: number }[]>([]);
+
+ const [loading, setLoading] = useState(true);
+ const [session, setSession] = useState(null);
+ const router = useRouter();
+ const [access_token, setAccess_token] = useState("");
+
+
+ useEffect(() => {
+ const fetchSession = async () => {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ setLoading(false);
+ };
+
+ fetchSession();
+ }, [router]);
+ useEffect(() => {
+ const fetchBalanceHistory = async () => {
+ if (!access_token) return;
+ try {
+ const balanceHistory = await getBalanceHistory(access_token);
+
+ if (balanceHistory.success) {
+ const formattedData = balanceHistory.data.map((item) => ({
+ month: item.time, // Adjust the time to match your needs (e.g., month)
+ balance: item.value,
+ }));
+ setChartData(formattedData);
+ }
+ } catch (error) {
+ console.error("Error fetching balance history:", error);
+ }
+ };
+
+ fetchBalanceHistory();
+
+ }, [access_token]);
+ return (
+
+
+
+ Balance History
+
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+
+ }
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/CreditCard.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/CreditCard.tsx
new file mode 100644
index 000000000..8cc03aee0
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/CreditCard.tsx
@@ -0,0 +1,73 @@
+import React from 'react';
+
+interface CardProps {
+ balance: string;
+ cardHolder: string;
+ validThru: string;
+ cardNumber: string;
+ filterClass?: string;
+ bgColor?: string;
+ textColor?: string;
+ iconBgColor?: string;
+ showIcon?: boolean;
+}
+
+const CreditCard: React.FC = ({
+ balance,
+ cardHolder,
+ validThru,
+ cardNumber,
+ filterClass = "filter-white",
+ bgColor = '',
+ textColor = 'text-white',
+ iconBgColor = 'bg-opacity-10',
+ showIcon = true
+}) => {
+
+ const isBlueGradient = bgColor.includes('#4C49ED') || bgColor.includes('#0A06F4');
+ const ellipseImageSrc = isBlueGradient ? '/group17.svg' : '/group18.svg';
+ const iconSrc = isBlueGradient ? '/sim.svg' : '/blackSim.svg';
+
+ const cardHolderTextColor = isBlueGradient ? 'text-[rgba(255,255,255,0.7)]' : 'text-[#718EBF]';
+
+ return (
+
+
+
+
+ {showIcon && (
+
+
+
+ )}
+
+
+
+
+
CARD HOLDER
+
{cardHolder}
+
+
+
VALID THRU
+
{validThru}
+
+
+
+
+
+
+ {cardNumber}
+
+
+
+
+
+
+
+ );
+};
+
+export default CreditCard;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/ExpenseStatistics.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/ExpenseStatistics.tsx
new file mode 100644
index 000000000..1e888bf9a
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/ExpenseStatistics.tsx
@@ -0,0 +1,193 @@
+"use client";
+
+import { TrendingUp } from "lucide-react";
+import { LabelList, Pie, PieChart } from "recharts";
+import { useState, useEffect } from "react";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import {
+ getTransactionIncomes,
+ getTransactionsExpenses,
+} from "@/lib/api/transactionController";
+import Refresh from "@/app/api/auth/[...nextauth]/token/RefreshToken";
+import { IconType } from "react-icons";
+
+const initialChartData = [
+ { browser: "shopping", amount: 0, fill: "var(--color-shopping)" },
+ { browser: "transfer", amount: 0, fill: "var(--color-transfer)" },
+ { browser: "deposit", amount: 0, fill: "var(--color-deposit)" },
+ {browser: "service", amount: 0, fill: "var(--color-service)"}
+];
+
+const chartConfig = {
+ Amount: {
+ label: "Amount",
+ },
+ shopping: {
+ label: "Shopping",
+ color: "hsl(var(--chart-1))",
+ },
+ transfer: {
+ label: "Transfer",
+ color: "hsl(var(--chart-2))",
+ },
+ deposit: {
+ label: "Deposit",
+ color: "hsl(var(--chart-3))",
+ },
+ service: {
+ label: "Service",
+ color: "hsl(var(--chart-4))",
+ },
+} satisfies ChartConfig;
+
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+type Column = {
+ icon: IconType;
+ iconStyle: string;
+ data: DataItem[];
+};
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+const ShimmerEffect = () => (
+
+);
+export function ExpenseStatistics() {
+ const [chartData, setChartData] = useState(initialChartData);
+ const [loading, setLoading] = useState(true);
+ const [session, setSession] = useState(null);
+ const router = useRouter();
+ const [access_token, setAccess_token] = useState("");
+
+
+ useEffect(() => {
+ const fetchSession = async () => {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ setLoading(false);
+ };
+
+ fetchSession();
+ }, [router]);
+
+
+
+ useEffect(() => {
+ const fetchAndProcessExpenses = async () => {
+ try {
+ if (access_token) {
+ const { data } = await getTransactionsExpenses(0, 1000, access_token);
+ console.log("worked", data)
+ const typeAmounts: { [key: string]: number } = {
+ shopping: 0,
+ transfer: 0,
+ deposit: 0,
+ service: 0
+ };
+
+ data.content.forEach((transaction: any) => {
+ if (typeAmounts.hasOwnProperty(transaction.type)) {
+ typeAmounts[transaction.type] += transaction.amount;
+ } else {
+ typeAmounts.other += transaction.amount; // If type is not predefined, add to "other"
+ }
+ });
+
+ setChartData([
+ { browser: "shopping", amount: typeAmounts.shopping, fill: "var(--color-shopping)" },
+ { browser: "transfer", amount: typeAmounts.transfer, fill: "var(--color-transfer)" },
+ { browser: "deposit", amount: typeAmounts.deposit, fill: "var(--color-deposit)" },
+ { browser: "service", amount: typeAmounts.service, fill: "var(--color-service)" },
+ ]);
+
+ setLoading(false);
+ }
+ } catch (error) {
+ console.error("Error fetching expenses:", error);
+ setLoading(false);
+ }
+ };
+
+ fetchAndProcessExpenses();
+ });
+
+ return (
+
+
+
+ Expense Statistics
+
+
+
+
+ {loading ? (
+
+ ) : (
+
+
+ }
+ />
+
+
+ chartConfig[value]?.label
+ }
+ />
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/ImageComponent.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/ImageComponent.tsx
new file mode 100644
index 000000000..ce2ad15c4
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/ImageComponent.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import Image from 'next/image';
+
+interface ImageComponentProps {
+ src: string; // URL of the image
+ alt: string; // Alt text for the image
+ width: number; // Width of the image
+ height: number; // Height of the image
+ name: string; // Name to display
+ role: string; // Role to display
+}
+
+const ImageComponent: React.FC = ({ src, alt, width, height, name, role }) => {
+ return (
+
+ );
+};
+
+export default ImageComponent;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/QuickTransfer.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/QuickTransfer.tsx
new file mode 100644
index 000000000..20e70e989
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/QuickTransfer.tsx
@@ -0,0 +1,233 @@
+/* eslint-disable react/no-children-prop */
+import Image from "next/image";
+import ImageComponent from "../components/ImageComponent"
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import { getSession } from 'next-auth/react';
+import Refresh from "../../api/auth/[...nextauth]/token/RefreshToken";
+import { getQuickTransfers } from "@/lib/api/transactionController";
+import { PropsWithChildren } from 'react';
+import { PostTransactionRequest } from "@/types/transactionController.interface";
+import { postTransaction } from "@/lib/api/transactionController";
+
+
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/components/ui/carousel"
+
+type infoType = {
+ id: string;
+ name: string;
+ username: string;
+ city: string;
+ country: string;
+ profilePicture: string;
+}
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+export default function Home() {
+ const ShimmerEffect = () => (
+
+
+
+ {/* Carousel Shimmer */}
+
+
+
+ {[...Array(3)].map((_, index) => (
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setLoading] = useState(true);
+ const [transfer, setQuickTransfer] = useState([]);
+ const [activeIndex, setActiveIndex] = useState(-1);
+ const [amount, setAmount] = useState("");
+ const [showDialog, setShowDialog] = useState(false);
+
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(`./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`);
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ useEffect(() => {
+ const addingData = async () => {
+ if (!access_token) return;
+ if (access_token) {
+ const transfers = await getQuickTransfers(100, access_token);
+ console.log("Fetching Completeddddd", transfers);
+
+ setQuickTransfer(transfers.data); // Set the content array
+ }
+ };
+ addingData();
+ }), [];
+
+ const handleSubmit = async (event: React.FormEvent) => {
+ event.preventDefault();
+
+ if (activeIndex < 0 || !transfer[activeIndex]) {
+ alert("You have to choose a user and Enter data.")
+ console.error('Invalid index or no transfer data available');
+ return;
+ }
+ else{
+ console.log(amount)
+ }
+
+ const transactionDetails: PostTransactionRequest = {
+ type: 'transfer', // Or dynamically set based on your application logic
+ description: `Transfer to ${transfer[activeIndex].username}`,
+ amount: parseInt(amount),
+ receiverUserName: transfer[activeIndex].username,
+ };
+
+ try {
+ console.log(transactionDetails);
+ const response = await postTransaction(transactionDetails, access_token);
+ if (response.success === true){
+ // setShowDialog(true); // Show the dialog on success
+ console.log()
+ alert("success")
+ console.log('Transaction successful:', response);
+ }
+ else{
+ alert('NO'); // Display success message
+ console.log(response.message)
+ }
+ } catch (error) {
+ console.error('Error posting transaction:', error);
+ }
+ };
+
+ const newLocal = "M1 1L7.5 7.5L1 14";
+ return (
+
+ {
+ loading ? (
+
+ ): (
+
+
Quick Transfers
+
+ {/* Image Component */}
+
+
+
+ {transfer.map((item,index) => (
+ {setActiveIndex(index)}}>
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+
+
+
+ );
+
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/RecentTransaction.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/RecentTransaction.tsx
new file mode 100644
index 000000000..b0d3b1731
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/RecentTransaction.tsx
@@ -0,0 +1,196 @@
+import React, { useEffect, useState } from 'react'
+import {
+ getTransactionIncomes,
+ getTransactions,
+ getTransactionsExpenses,
+} from "@/lib/api/transactionController";
+import { getSession } from "next-auth/react";
+import Refresh from "../../api/auth/[...nextauth]/token/RefreshToken";
+
+import { TransactionData, TransactionResponse } from "@/types/transactionController.interface";
+import { IconType } from 'react-icons';
+import { useRouter } from "next/navigation";
+
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+type Column = {
+ icon: IconType;
+ iconStyle: string;
+ data: DataItem[];
+};
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+const RecentTransaction = () => {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setLoading] = useState(true);
+ const [balance, setBalance] = useState("Loading...");
+ const [income, setIncome] = useState("Loading...");
+ const [expense, setExpense] = useState("Loading...");
+ const [transaction, setTransaction] = useState([])
+ const ShimmerEffect = () => {
+ return (
+
+ {[...Array(3)].map((_, index) => (
+
+ ))}
+
+ );
+ };
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+ useEffect(() => {
+ // Simulate a data fetch with a delay
+ setTimeout(() => {
+ setTransaction([]); // Simulate empty transaction data
+ }, 2000);
+ }, []);
+ // Combined fetching data to reduce multiple useEffect hooks
+ useEffect(() => {
+ const fetchData = async () => {
+ if (!access_token) return;
+
+ try {
+ // Fetch Cards
+
+ // Fetch Transactions
+ const transactionData:TransactionResponse = await getTransactions(0, 3, access_token)
+ setTransaction(transactionData.data.content)
+
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ }
+ };
+
+ fetchData();
+ }, [access_token]);
+ return (
+
+
+
+
+
Recent Transaction
+
+
+ {
+ transaction.length > 0 ? (
+ transaction.slice(0,3).map((txn, index) => (
+
+
+
+
+ {txn.type === "shopping" &&
+
+
+
+
+
+ }
+ {txn.type === "transfer" &&
+
+
+
+
+
+ }
+ {txn.type === "deposit" &&
+
+
+
+
+
+
+ }
+ {txn.type === "withdrawal" &&
+
+
+
+
+
+
+
+ }
+
+
+
+
+
+
+
+ {txn.description}
+
+
+ {formatDate(txn.date)}
+
+
+
+
+
+ {txn.amount < 0 ? `- ${Math.abs(txn.amount)}` : `+ ${txn.amount}`}
+
+
+
+
+ ))
+ ):(
+
+ )
+ }
+
+
+
+
+
+
+ )
+}
+
+export default RecentTransaction
+const formatDate = (dateString: string): string => {
+ const date = new Date(dateString);
+
+ const options: Intl.DateTimeFormatOptions = {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ };
+
+ return date.toLocaleDateString("en-US", options);
+};
\ No newline at end of file
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/WeeklyActivity.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/WeeklyActivity.tsx
new file mode 100644
index 000000000..b2f49cadc
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/components/WeeklyActivity.tsx
@@ -0,0 +1,94 @@
+"use client";
+
+import { TrendingUp } from "lucide-react";
+import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
+import {
+ Card,
+ CardContent,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+
+const chartData = [
+ { month: "Saturday", desktop: 186, mobile: 80 },
+ { month: "Sunday", desktop: 305, mobile: 200 },
+ { month: "Monday", desktop: 237, mobile: 120 },
+ { month: "Tuesday", desktop: 73, mobile: 190 },
+ { month: "Wednesday", desktop: 209, mobile: 130 },
+ { month: "Thursday", desktop: 214, mobile: 140 },
+ { month: "Friday", desktop: 214, mobile: 140 },
+];
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "#1814f3", // Color for desktop bars
+ },
+ mobile: {
+ label: "Mobile",
+ color: "#16dbcc", // Color for mobile bars
+ },
+} satisfies ChartConfig;
+
+export function WeeklyActivity() {
+ return (
+
+
+
+ Weekly Activity
+
+
+
+
+
+
+ value}
+ domain={[0, 500]}
+ interval={0}
+ tickLine={false}
+ axisLine={false}
+ tickMargin={10}
+ />
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/page.tsx
new file mode 100644
index 000000000..7059d2357
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/dashboard/page.tsx
@@ -0,0 +1,270 @@
+'use client'
+import Image from "next/image";
+import { IconType } from "react-icons";
+import { useEffect, useState } from "react";
+import ImageComponent from "./components/ImageComponent";
+import Reviving from "./components/QuickTransfer";
+import { BalanceHistory } from "./components/BalanceHistory";
+import { WeeklyActivity } from "./components/WeeklyActivity";
+import { ExpenseStatistics } from "./components/ExpenseStatistics";
+import RecentTransaction from "./components/RecentTransaction";
+import CreditCard from "./components/CreditCard";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import { getCards } from "@/lib/api/cardController";
+import { GetCardsResponse, Card as CardType } from "@/types/cardController.Interface";
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import { TransactionData, TransactionResponse } from "@/types/transactionController.interface";
+
+import {
+ getTransactionIncomes,
+ getTransactions,
+ getTransactionsExpenses,
+} from "@/lib/api/transactionController";
+// import {RecentTransaction} from "@/components/RecentTransaction"
+import {
+MdHome,
+MdSettings,
+MdAttachMoney,
+MdAccountBalance,
+} from "react-icons/md";
+
+type DataItem = {
+ heading: string;
+ text: string;
+ headingStyle: string;
+ dataStyle: string;
+};
+
+type Column = {
+ icon: IconType;
+ iconStyle: string;
+ data: DataItem[];
+};
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+type AllowedProperties = {
+ ListCardLoading: () => JSX.Element;
+ // Add other allowed properties here
+};
+type SessionDataType = {
+ user: Data;
+};
+// export const ListCardLoading = () => {
+// return (
+//
+// );
+// };
+export default function Home() {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setLoading] = useState(true);
+ const [getCard, setGetCards] = useState();
+ const [transaction, setTransaction] = useState([])
+
+ const createTransactionColumn = (transaction: TransactionData): Column => {
+ return {
+ icon: MdAccountBalance, // Default icon, you can customize based on type
+ iconStyle: "text-[#16DBCC] bg-[#DCFAF8]", // Default iconStyle, you can customize based on type
+ data: [
+ {
+ heading: transaction.description,
+ text: formatDate(transaction.date),
+ headingStyle: "text-sm font-bold text-nowrap",
+ dataStyle: "text-xs text-nowrap text-[#718EBF]",
+ },
+ {
+ heading: transaction.amount < 0 ? `-${Math.abs(transaction.amount)}` : `+${transaction.amount}`,
+ text: transaction.receiverUserName || "unknown source",
+ headingStyle: `text-xs font-bold ${transaction.amount < 0 ? "text-[#FE5C73]" : "text-[#16DBAA]"}`,
+ dataStyle: "text-xs text-nowrap",
+ },
+ ],
+ };
+ };
+ // getting the session ends here
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ // Fetching cards
+ useEffect(() => {
+ const addingData = async () => {
+ if (!access_token) return;
+ if (access_token) {
+ const cardData = await getCards(access_token, 0, 3);
+ console.log("Fetching Complete", cardData.content)
+ setGetCards(cardData.content);
+
+ // Fetch Transactions
+ const transactionData:TransactionResponse = await getTransactions(0, 3, access_token)
+ setTransaction(transactionData.data.content)
+ }
+ };
+ addingData();
+ });
+
+ if (loading) return null; // Don't render anything while loading
+
+
+ if (!session) {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ return null;
+ }
+ return (
+
+
+
+ {/* Mobile Version */}
+
+
+
My Cards
+ See All
+
+
+
+
+
+
+ fad
+
+ {getCard && getCard.length > 0 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ {/* Web Version */}
+
+ {/*
+
My Cards
+ See All
+ */}
+
+
+ {/* My Cards Section */}
+
+
My Cards
+ See All
+
+
+
+ {loading &&
Loading ...
}
+
+ {!loading && getCard &&
+ getCard.map((items, index) => (
+
+ ))}
+
+
+
+
+
Recent Transaction
+
+
+
+
+
+
+
Weekly Activity
+
+
+
+
Expense Statistics
+
+
+
+
+
+
Quick Transfers
+
+
+
+
Balance History
+
+
+
+
+
+
+
+
+ );
+
+}
+const formatDate = (dateString: string): string => {
+ const date = new Date(dateString);
+
+ const options: Intl.DateTimeFormatOptions = {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ };
+
+ return date.toLocaleDateString("en-US", options);
+};
\ No newline at end of file
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/firebase.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/firebase.ts
new file mode 100644
index 000000000..b453add9a
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/firebase.ts
@@ -0,0 +1,16 @@
+// Import the functions you need from the SDKs you need
+import { initializeApp } from "firebase/app";
+import { getStorage } from "firebase/storage";
+const firebaseConfig = {
+ apiKey: "AIzaSyCv_JSIXqdk05YebMCWuDm79VUlWhvuOgA",
+ authDomain: "a2sv-wallet.firebaseapp.com",
+ projectId: "a2sv-wallet",
+ storageBucket: "a2sv-wallet.appspot.com",
+ messagingSenderId: "136604332771",
+ appId: "1:136604332771:web:8264100cc84c484fbc4bd6",
+ measurementId: "G-44PK3KTJJT"
+};
+
+// Initialize Firebase
+const app = initializeApp(firebaseConfig);
+export const storage = getStorage(app)
\ No newline at end of file
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/globals.css b/web/AASTU-web-group-2/a2sv-banking-system/app/globals.css
new file mode 100644
index 000000000..fa5f09580
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/globals.css
@@ -0,0 +1,78 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+ --radius: 0.5rem;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ }
+
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ }
+
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+.react-datepicker-wrapper {
+ @apply inline-block p-0 border-0 w-full;
+}
+@layer base {
+ input[type="number"]::-webkit-inner-spin-button,
+ input[type="number"]::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/investments/back/Invest.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/back/Invest.ts
new file mode 100644
index 000000000..1057adbc2
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/back/Invest.ts
@@ -0,0 +1,23 @@
+import axios from "axios";
+const baseUrl = "https://a2svwallets.onrender.com";
+export async function getRandomInvestementData(
+ months: number,
+ years: number,
+ accessToken: string
+) {
+ try {
+ const response = await axios.get(
+ baseUrl + `/user/random-investment-data?months=${months}&years=${years}`,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ return response.data.data;
+ } catch (error) {
+ console.error("Error fetching random investment data:", error);
+ throw error;
+ }
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/Card1.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/Card1.tsx
new file mode 100644
index 000000000..2f8055650
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/Card1.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import Image from "next/image";
+interface Card1Props {
+ text: string;
+ num: number | string;
+ img: string;
+}
+const Card1 = ({ text, num, img }: Card1Props) => {
+ return (
+
+
+
+
+
+
+
+
+ {text}
+
+
+ {num}
+
+
+
+
+
+ );
+};
+
+export default Card1;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/LineChart.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/LineChart.tsx
new file mode 100644
index 000000000..d4b2712c0
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/LineChart.tsx
@@ -0,0 +1,179 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import Refresh from "../../api/auth/[...nextauth]/token/RefreshToken";
+import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
+import { getRandomInvestementData } from "../back/Invest";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/app/loans/components/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/app/loans/components/chart";
+interface arr {
+ time: string;
+ value: number;
+}
+const token =
+ "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJsc2FqZGxzanNuIiwiaWF0IjoxNzI0MTU1NzkzLCJleHAiOjE3MjQyNDIxOTN9.wi7oRgF81zMp1v8tPzRPmAj4GOLaYy4bV_TMVvtWmzg2mjrTThiruT_Fswcyu1eq";
+
+interface info {
+ totalInvestment: number;
+ rateOfReturn: number;
+ yearlyTotalInvestment: arr[];
+ monthlyRevenue: arr[];
+}
+const chartConfig = {
+ value: {
+ label: "value",
+ color: "hsl(var(--chart-1))",
+ },
+} satisfies ChartConfig;
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+export default function Linechart() {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setloading] = useState(true);
+ const [Loading, setLoading] = useState(true);
+ const [data, setdata] = useState({
+ totalInvestment: 1,
+ rateOfReturn: 1,
+ yearlyTotalInvestment: [],
+ monthlyRevenue: [],
+ });
+
+ // Getting the session from the server and Access Token From Refresh
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setloading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ // Combined fetching data to reduce multiple useEffect hooks
+ useEffect(() => {
+ const fetchData = async () => {
+ if (!access_token) return;
+
+ try {
+ // Fetch data
+ const d: info = await getRandomInvestementData(11, 20, access_token);
+ setdata(d);
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [access_token]);
+
+ if (loading || Loading)
+ return (
+
+ );
+ const { yearlyTotalInvestment } = data;
+ return (
+
+
+
+
+
+ value.slice(0, 4)}
+ />
+
+ }
+ />
+
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/Monthly.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/Monthly.tsx
new file mode 100644
index 000000000..0a2f3fa1a
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/components/Monthly.tsx
@@ -0,0 +1,187 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import Refresh from "../../api/auth/[...nextauth]/token/RefreshToken";
+import Card1 from "../components/Card1";
+import { getServerSession } from "next-auth";
+import { options } from "../../api/auth/[...nextauth]/options";
+import { TrendingUp } from "lucide-react";
+import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
+import { getRandomInvestementData } from "../back/Invest";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/app/loans/components/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/app/loans/components/chart";
+import { cp } from "fs";
+const chartConfig = {
+ value: {
+ label: "value",
+ color: "hsl(var(--chart-1))",
+ },
+} satisfies ChartConfig;
+interface arr {
+ time: string;
+ value: number;
+}
+const token =
+ "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJsc2FqZGxzanNuIiwiaWF0IjoxNzI0MTU1NzkzLCJleHAiOjE3MjQyNDIxOTN9.wi7oRgF81zMp1v8tPzRPmAj4GOLaYy4bV_TMVvtWmzg2mjrTThiruT_Fswcyu1eq";
+
+interface info {
+ totalInvestment: number;
+ rateOfReturn: number;
+ yearlyTotalInvestment: arr[];
+ monthlyRevenue: arr[];
+}
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+export default function Monthly() {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setloading] = useState(true);
+ const [Loading, setLoading] = useState(true);
+ const [data, setdata] = useState({
+ totalInvestment: 1,
+ rateOfReturn: 1,
+ yearlyTotalInvestment: [],
+ monthlyRevenue: [],
+ });
+
+ // Getting the session from the server and Access Token From Refresh
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setloading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ // Combined fetching data to reduce multiple useEffect hooks
+ useEffect(() => {
+ const fetchData = async () => {
+ if (!access_token) return;
+
+ try {
+ // Fetch data
+ const d: info = await getRandomInvestementData(11, 2021, access_token);
+ setdata(d);
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [access_token]);
+
+ if (loading || Loading)
+ return (
+
+ );
+ // console.log(data);
+ const { monthlyRevenue } = data;
+ return (
+
+
+
+
+
+ value.slice(0, 7)}
+ />
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/investments/page.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/page.tsx
new file mode 100644
index 000000000..bb64d5c0a
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/investments/page.tsx
@@ -0,0 +1,400 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import { getSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import Refresh from "../api/auth/[...nextauth]/token/RefreshToken";
+import Card1 from "./components/Card1";
+import Linechart from "./components/LineChart";
+import Monthly from "./components/Monthly";
+import { getServerSession } from "next-auth";
+import { options } from "../api/auth/[...nextauth]/options";
+import { getRandomInvestementData } from "./back/Invest";
+import {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableFooter,
+ TableRow,
+} from "@/app/loans/components/table";
+
+const invoices = [
+ {
+ slno: "01.",
+ name: "Trivago",
+ price: "$520",
+ return: "+5%",
+ },
+ {
+ slno: "02.",
+ name: "Canon",
+ price: "$480",
+ return: "+10%",
+ },
+ {
+ slno: "03.",
+ name: "Uber Food",
+ price: "$350",
+ return: "-3%",
+ },
+ {
+ slno: "04.",
+ name: "Nokia",
+ price: "$40,500",
+ return: "+2%",
+ },
+ {
+ slno: "05.",
+ name: "Tiktok",
+ price: "$670",
+ return: "-12%",
+ },
+];
+interface arr {
+ time: string;
+ value: number;
+}
+const loanid = "66c3054e80b7cf4a6c2f7709";
+
+type ISODateString = string;
+
+interface DefaultSession {
+ user?: {
+ name?: string | null;
+ email?: string | null;
+ image?: string | null;
+ access_token?: string | any;
+ };
+ expires: ISODateString;
+}
+interface datatype {
+ totalInvestment: any;
+ rateOfReturn: any;
+ yearlyTotalInvestment: [{ time: string; value: number }];
+ monthlyRevenue: arr[];
+}
+
+type Data = {
+ access_token: string;
+ data: string;
+ refresh_token: string;
+};
+
+type SessionDataType = {
+ user: Data;
+};
+
+export default function Home() {
+ const [session, setSession] = useState(null);
+ const [access_token, setAccess_token] = useState("");
+ const router = useRouter();
+ const [loading, setloading] = useState(true);
+ const [Loading, setLoading] = useState(true);
+ const [data, setdata] = useState();
+
+ // Getting the session from the server and Access Token From Refresh
+ useEffect(() => {
+ const fetchSession = async () => {
+ try {
+ const sessionData = (await getSession()) as SessionDataType | null;
+ setAccess_token(await Refresh());
+ if (sessionData && sessionData.user) {
+ setSession(sessionData.user);
+ } else {
+ router.push(
+ `./api/auth/signin?callbackUrl=${encodeURIComponent("/accounts")}`
+ );
+ }
+ } catch (error) {
+ console.error("Error fetching session:", error);
+ } finally {
+ setloading(false);
+ }
+ };
+
+ fetchSession();
+ }, [router]);
+
+ // Combined fetching data to reduce multiple useEffect hooks
+ useEffect(() => {
+ const fetchData = async () => {
+ if (!access_token) return;
+
+ try {
+ // Fetch data
+ const d: datatype = await getRandomInvestementData(
+ 11,
+ 2021,
+ access_token
+ );
+ setdata(d);
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [access_token]);
+
+ if (loading || Loading)
+ return (
+
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+ Yearly Total Investment
+
+
+
+
+
+ Monthly Revenue
+
+
+
+
+
+
+
+
+
+ My Investment
+
+
+
+
+
+
+ Apple Store
+
+
+ E-commerce, Marketplace
+
+
+
+
+ $54,000
+
+
+ Envestment Value
+
+
+
+
+ +16%
+
+
+ Return Value
+
+
+
+
+
+
+
+ Samsung Mobile
+
+
+ E-commerce, Marketplace
+
+
+
+
+ $25,300
+
+
+ Envestment Value
+
+
+
+
+ -4%
+
+
+ Return Value
+
+
+
+
+
+
+
+ Tesla Motors
+
+
+ E-commerce, Marketplace
+
+
+
+
+ $8,200
+
+
+ Envestment Value
+
+
+
+
+ +25%
+
+
+ Return Value
+
+
+
+
+
+
+
+ Trending Stock
+
+
+
+
+
+ SL No
+
+
+ Name
+
+
+ Price
+
+
+ Return
+
+
+
+
+ {invoices.map((invoice) => (
+
+
+ {invoice.slno}
+
+
+ {invoice.name}
+
+
+ {invoice.price}
+
+
+ {invoice.return}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/layout.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/layout.tsx
new file mode 100644
index 000000000..c875a4695
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/layout.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from "next";
+import { Inter, Lato } from "next/font/google";
+import "./globals.css";
+import Navigation from "./components/Navigation";
+import { DarkModeProvider } from "./components/Context/DarkModeContext";
+import ProgressBar from "./components/loadingprovider/ProgressBar";
+const inter = Inter({ subsets: ["latin"] });
+const lato = Lato({
+ subsets: ["latin"],
+ weight: ["400", "700"],
+});
+
+export const metadata: Metadata = {
+ title: "A2SV Wallet",
+ description: "Built for a2sv",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/loans/back/ActiveLoanController.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/back/ActiveLoanController.ts
new file mode 100644
index 000000000..f9c065e0d
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/back/ActiveLoanController.ts
@@ -0,0 +1,108 @@
+import axios from "axios";
+const baseUrl = "https://a2svwallets.onrender.com";
+const activeloansall = async (token: string, size: number, page: number) => {
+ const response = await axios.get(
+ baseUrl + `/active-loans/all?page=${page}&size=${size}`,
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ return response.data.data;
+};
+
+const activeloansdetaildata = async (token: string) => {
+ const response = await axios.get(baseUrl + "/active-loans/detail-data", {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ });
+ return response.data.data;
+};
+
+const activeloansmyloans = async (token: string) => {
+ const response = await axios.get(baseUrl + "/active-loans/my-loans", {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ });
+ return response.data.data;
+};
+
+const activeloansid = async (loanid: string, token: string) => {
+ const response = await axios.get(baseUrl + `/active-loans/${loanid}`, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ });
+ return response.data.data;
+};
+
+const activeloansidapprove = async (loanid: string, token: string) => {
+ const respons7 = await axios.post(
+ baseUrl + `/active-loans/${loanid}/approve`,
+ {},
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+};
+
+const activeloansidreject = async (loanid: string, token: string) => {
+ const response = await axios.post(
+ baseUrl + `/active-loans/${loanid}/reject`,
+ {},
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+ return response.data;
+};
+
+interface activeloanbody {
+ loanAmount: number;
+ duration: number;
+ interestRate: number;
+ type: string;
+}
+
+type Form = {
+ loanAmount: number;
+ duration: number;
+ interestRate: number;
+ type: string;
+};
+
+const activeloans = async (token: string, data: Form) => {
+ const response = await axios.post(
+ baseUrl + "/active-loans",
+ JSON.stringify(data),
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ return response.data.data;
+};
+
+export {
+ activeloansall,
+ activeloansdetaildata,
+ activeloansmyloans,
+ activeloansid,
+ activeloansidapprove,
+ activeloansidreject,
+ activeloans,
+};
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/loans/back/Invest.ts b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/back/Invest.ts
new file mode 100644
index 000000000..1057adbc2
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/back/Invest.ts
@@ -0,0 +1,23 @@
+import axios from "axios";
+const baseUrl = "https://a2svwallets.onrender.com";
+export async function getRandomInvestementData(
+ months: number,
+ years: number,
+ accessToken: string
+) {
+ try {
+ const response = await axios.get(
+ baseUrl + `/user/random-investment-data?months=${months}&years=${years}`,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ return response.data.data;
+ } catch (error) {
+ console.error("Error fetching random investment data:", error);
+ throw error;
+ }
+}
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/loans/components/Card1.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/components/Card1.tsx
new file mode 100644
index 000000000..395b1449d
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/components/Card1.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import Image from "next/image";
+interface Card1Props {
+
+ text: string;
+ num: number | string;
+ img: string;
+}
+const Card1 = ({text, num, img}: Card1Props) => {
+ return (
+
+
+
+
+
+
+
+
+ {text}
+
+
+ {num}
+
+
+
+
+
+ );
+};
+
+export default Card1;
diff --git a/web/AASTU-web-group-2/a2sv-banking-system/app/loans/components/Createloan.tsx b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/components/Createloan.tsx
new file mode 100644
index 000000000..601c739c8
--- /dev/null
+++ b/web/AASTU-web-group-2/a2sv-banking-system/app/loans/components/Createloan.tsx
@@ -0,0 +1,132 @@
+import { useForm } from "react-hook-form";
+import {
+ Sheet,
+ SheetClose,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/components/ui/sheet";
+import Card1 from "./Card1";
+type Form = {
+ loanAmount: number;
+ duration: number;
+ interestRate: number;
+ type: string
+}
+
+export function SheetDemo({ handleform }: { handleform : (data:Form)=>void}) {
+ const forms = useForm