- {/* CAPTCHA Image */}
-
- {/*
*/}
+
+
+
+
+ {captcha}
- {/* Refresh Button */}
- {/* TextInput */}
setCaptchaInput(e.currentTarget.value)}
- required
- style={{ height: 30, flexGrow: 1 }}
+ withAsterisk
+ style={{ flexGrow: 1 }}
+ readOnly={step !== "form"}
/>
-
-
- {captchaError || '\u00A0'}
-
+ {step !== "form" && (
+
setOtp(e.currentTarget.value)}
+ withAsterisk
+ mt="sm"
+ maxLength={6}
+ readOnly={otpValidated}
+ />
+ )}
+
- {/* Fixed Save Button */}
-
-
+ {/* Buttons */}
+
+
+
+
-
);
-};
-
-export default ChangePassword;
+}
diff --git a/src/app/(main)/settings/change_txn_password/page.tsx b/src/app/(main)/settings/change_txn_password/page.tsx
new file mode 100644
index 0000000..7b01af7
--- /dev/null
+++ b/src/app/(main)/settings/change_txn_password/page.tsx
@@ -0,0 +1,263 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { TextInput, PasswordInput, Button, Title, Paper, Group, Box } from "@mantine/core";
+import { notifications } from "@mantine/notifications";
+import { IconLock } from "@tabler/icons-react";
+import { generateCaptcha } from "@/app/captcha";
+
+export default function ChangePassword() {
+ const [oldPassword, setOldPassword] = useState("");
+ const [newPassword, setNewPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [captcha, setCaptcha] = useState("");
+ const [captchaInput, setCaptchaInput] = useState("");
+ const [otp, setOtp] = useState("");
+ const [otpValidated, setOtpValidated] = useState(false);
+
+ const [step, setStep] = useState<"form" | "otp" | "final">("form"); // ✅ steps control
+ const [passwordHistory] = useState(["Pass@1234", "OldPass@123", "MyPass#2023"]);
+ const icon =
;
+
+ useEffect(() => {
+ regenerateCaptcha();
+ }, []);
+
+ const regenerateCaptcha = async () => {
+ const newCaptcha = await generateCaptcha();
+ setCaptcha(newCaptcha);
+ setCaptchaInput("");
+ };
+
+ const validatePasswordPolicy = (password: string) => {
+ return /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/.test(password);
+ };
+
+ const handleSubmit = () => {
+ // Step 1 → validate form
+ if (step === "form") {
+ if (!oldPassword || !newPassword || !confirmPassword || !captchaInput) {
+ notifications.show({
+ title: "Missing Field",
+ message: "Please fill all mandatory fields.",
+ color: "red",
+ });
+ return;
+ }
+
+ const actualOldPassword = "Pass@123";
+ if (oldPassword !== actualOldPassword) {
+ notifications.show({
+ title: "Old Password Incorrect",
+ message: "Entered old password does not match.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (!validatePasswordPolicy(newPassword)) {
+ notifications.show({
+ title: "Invalid Password",
+ message:
+ "Password must be at least 8 characters and contain alphanumeric and special characters.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (passwordHistory.includes(newPassword)) {
+ notifications.show({
+ title: "Password Reused",
+ message: "New password must be different from the last 3 passwords.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (newPassword !== confirmPassword) {
+ notifications.show({
+ title: "Password Mismatch",
+ message: "Confirm password does not match new password.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (captchaInput !== captcha) {
+ notifications.show({
+ title: "Invalid Captcha",
+ message: "Please enter correct Captcha",
+ color: "red",
+ });
+ regenerateCaptcha();
+ return;
+ }
+
+ // ✅ Passed → move to OTP
+ setStep("otp");
+ notifications.show({
+ title: "OTP Sent",
+ message: "An OTP has been sent to your registered mobile.",
+ color: "blue",
+ });
+ return;
+ }
+
+ // Step 2 → validate OTP
+ if (step === "otp") {
+ if (otp !== "123456") {
+ notifications.show({
+ title: "Invalid OTP",
+ message: "Please enter the correct OTP.",
+ color: "red",
+ });
+ return;
+ }
+
+ setOtpValidated(true);
+ setStep("final");
+ notifications.show({
+ title: "OTP Verified",
+ message: "OTP has been successfully verified.",
+ color: "green",
+ });
+ return;
+ }
+
+ // Step 3 → Final Change Password
+ if (step === "final") {
+ notifications.show({
+ title: "Password Changed",
+ message: "Your password has been successfully updated.",
+ color: "green",
+ });
+ resetForm();
+ }
+ };
+
+ const resetForm = () => {
+ setOldPassword("");
+ setNewPassword("");
+ setConfirmPassword("");
+ setCaptchaInput("");
+ setOtp("");
+ setOtpValidated(false);
+ setStep("form");
+ regenerateCaptcha();
+ };
+
+ return (
+
+
+ Change Transaction Password
+
+
+ {/* Scrollable form area */}
+
+
setOldPassword(e.currentTarget.value)}
+ withAsterisk
+ mb="xs"
+ />
+
+ setNewPassword(e.currentTarget.value)}
+ withAsterisk
+ mb="xs"
+ />
+
+ setConfirmPassword(e.currentTarget.value)}
+ withAsterisk
+ rightSection={icon}
+ mb="sm"
+ readOnly={step !== "form"}
+ onCopy={(e) => e.preventDefault()}
+ onPaste={(e) => e.preventDefault()}
+ onCut={(e) => e.preventDefault()}
+ />
+
+
+
+ {/* CAPTCHA */}
+
+
+
+
+
+ {captcha}
+
+
+
+
+
setCaptchaInput(e.currentTarget.value)}
+ withAsterisk
+ style={{ flexGrow: 1 }}
+ readOnly={step !== "form"}
+ />
+
+
+ {step !== "form" && (
+ setOtp(e.currentTarget.value)}
+ withAsterisk
+ mt="sm"
+ maxLength={6}
+ readOnly={otpValidated}
+ />
+ )}
+
+
+
+ {/* Buttons */}
+
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/settings/layout.tsx b/src/app/(main)/settings/layout.tsx
index ba3d1f4..2cf0bfa 100644
--- a/src/app/(main)/settings/layout.tsx
+++ b/src/app/(main)/settings/layout.tsx
@@ -13,8 +13,8 @@ export default function Layout({ children }: { children: React.ReactNode }) {
const links = [
{ label: "View Profile", href: "/settings" },
{ label: "Change Login Password", href: "/settings/change_login_password" },
- { label: "Change transaction Password", href: "/settings/change_transaction_password" },
- { label: "Set transaction Password", href: "/settings/set_transaction_password" },
+ { label: "Change transaction Password", href: "/settings/change_txn_password" },
+ { label: "Set transaction Password", href: "/settings/set_txn_password" },
];
useEffect(() => {
const token = localStorage.getItem("access_token");
diff --git a/src/app/(main)/settings/page.tsx b/src/app/(main)/settings/page.tsx
index b79c0c1..d70d0d2 100644
--- a/src/app/(main)/settings/page.tsx
+++ b/src/app/(main)/settings/page.tsx
@@ -1,11 +1,145 @@
"use client";
-import { Divider, Stack, Text } from '@mantine/core';
-import React, { useEffect, useState } from 'react';
+
+import React, { useEffect, useState } from "react";
+import {
+ Paper,
+ Title,
+ Grid,
+ Text,
+ Anchor,
+ Divider,
+ Loader,
+ Center,
+} from "@mantine/core";
+import { notifications } from "@mantine/notifications";
import { useRouter } from "next/navigation";
-export default function settings() {
- return(
-
Hii
- )
+// Response structure from backend
+interface ProfileData {
+ custname: string;
+ cifNumber: string;
+ stBranchNo: string;
+ custdob: string;
+ mobileno: string;
+ id: string;
+ custaddress: string;
+ pincode: string;
-}
\ No newline at end of file
+}
+
+export default function ViewProfile() {
+ const router = useRouter();
+ const [profileData, setProfileData] = useState
(null);
+ const [loading, setLoading] = useState(true);
+
+ // Fetch API with same style as RootLayout
+ async function handleFetchProfile() {
+ try {
+ const token = localStorage.getItem("access_token");
+ const response = await fetch("/api/customer", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ if (!response.ok) {
+ notifications.show({
+ withBorder: true,
+ color: "red",
+ title: "Error",
+ message: "Unable to fetch profile details",
+ autoClose: 5000,
+ });
+ return;
+ }
+
+ const data = await response.json();
+
+ // If backend returns array → take first record
+ if (Array.isArray(data) && data.length > 0) {
+ setProfileData(data[0]);
+ }
+ } catch (err) {
+ notifications.show({
+ withBorder: true,
+ color: "red",
+ title: "Please try again later",
+ message: "Something went wrong while fetching profile",
+ autoClose: 5000,
+ });
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ handleFetchProfile();
+ }, []);
+
+ const Row = ({
+ label,
+ value,
+ link,
+ }: {
+ label: string;
+ value: string;
+ link?: string;
+ }) => (
+
+
+
+ {label}
+
+
+
+ {link ? (
+
+ {value}
+
+ ) : (
+ {value}
+ )}
+
+
+ );
+
+ return (
+
+
+ View Profile
+
+
+
+ {loading ? (
+
+
+
+ ) : profileData ? (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ ) : (
+
+ No profile data found.
+
+ )}
+
+ );
+}
diff --git a/src/app/(main)/settings/set_txn_password/page.tsx b/src/app/(main)/settings/set_txn_password/page.tsx
new file mode 100644
index 0000000..707ee62
--- /dev/null
+++ b/src/app/(main)/settings/set_txn_password/page.tsx
@@ -0,0 +1,255 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { TextInput, PasswordInput, Button, Title, Paper, Group, Box } from "@mantine/core";
+import { notifications } from "@mantine/notifications";
+import { IconLock } from "@tabler/icons-react";
+import { generateCaptcha } from "@/app/captcha";
+
+export default function ChangePassword() {
+ const [oldPassword, setOldPassword] = useState("");
+ const [newPassword, setNewPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [captcha, setCaptcha] = useState("");
+ const [captchaInput, setCaptchaInput] = useState("");
+ const [otp, setOtp] = useState("");
+ const [otpValidated, setOtpValidated] = useState(false);
+
+ const [step, setStep] = useState<"form" | "otp" | "final">("form"); // ✅ steps control
+ const [passwordHistory] = useState(["Pass@1234", "OldPass@123", "MyPass#2023"]);
+ const icon = ;
+
+ useEffect(() => {
+ regenerateCaptcha();
+ }, []);
+
+ const regenerateCaptcha = async () => {
+ const newCaptcha = await generateCaptcha();
+ setCaptcha(newCaptcha);
+ setCaptchaInput("");
+ };
+
+ const validatePasswordPolicy = (password: string) => {
+ return /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/.test(password);
+ };
+
+ const handleSubmit = () => {
+ // Step 1 → validate form
+ if (step === "form") {
+ if (!newPassword || !confirmPassword || !captchaInput) {
+ notifications.show({
+ title: "Missing Field",
+ message: "Please fill all mandatory fields.",
+ color: "red",
+ });
+ return;
+ }
+
+
+
+ if (!validatePasswordPolicy(newPassword)) {
+ notifications.show({
+ title: "Invalid Password",
+ message:
+ "Password must be at least 8 characters and contain alphanumeric and special characters.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (passwordHistory.includes(newPassword)) {
+ notifications.show({
+ title: "Password Reused",
+ message: "New password must be different from the last 3 passwords.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (newPassword !== confirmPassword) {
+ notifications.show({
+ title: "Password Mismatch",
+ message: "Confirm password does not match new password.",
+ color: "red",
+ });
+ return;
+ }
+
+ if (captchaInput !== captcha) {
+ notifications.show({
+ title: "Invalid Captcha",
+ message: "Please enter correct Captcha",
+ color: "red",
+ });
+ regenerateCaptcha();
+ return;
+ }
+
+ // ✅ Passed → move to OTP
+ setStep("otp");
+ notifications.show({
+ title: "OTP Sent",
+ message: "An OTP has been sent to your registered mobile.",
+ color: "blue",
+ });
+ return;
+ }
+
+ // Step 2 → validate OTP
+ if (step === "otp") {
+ if (otp !== "123456") {
+ notifications.show({
+ title: "Invalid OTP",
+ message: "Please enter the correct OTP.",
+ color: "red",
+ });
+ return;
+ }
+
+ setOtpValidated(true);
+ setStep("final");
+ notifications.show({
+ title: "OTP Verified",
+ message: "OTP has been successfully verified.",
+ color: "green",
+ });
+ return;
+ }
+
+ // Step 3 → Final Change Password
+ if (step === "final") {
+ notifications.show({
+ title: "Password Changed",
+ message: "Your password has been successfully updated.",
+ color: "green",
+ });
+ resetForm();
+ }
+ };
+
+ const resetForm = () => {
+ setOldPassword("");
+ setNewPassword("");
+ setConfirmPassword("");
+ setCaptchaInput("");
+ setOtp("");
+ setOtpValidated(false);
+ setStep("form");
+ regenerateCaptcha();
+ };
+
+ return (
+
+
+ Set Transaction Password
+
+
+ {/* Scrollable form area */}
+
+ {/*
setOldPassword(e.currentTarget.value)}
+ withAsterisk
+ mb="xs"
+ /> */}
+
+ setNewPassword(e.currentTarget.value)}
+ withAsterisk
+ mb="xs"
+ />
+
+ setConfirmPassword(e.currentTarget.value)}
+ withAsterisk
+ rightSection={icon}
+ mb="sm"
+ readOnly={step !== "form"}
+ onCopy={(e) => e.preventDefault()}
+ onPaste={(e) => e.preventDefault()}
+ onCut={(e) => e.preventDefault()}
+ />
+
+
+
+ {/* CAPTCHA */}
+
+
+
+
+
+ {captcha}
+
+
+
+
+
setCaptchaInput(e.currentTarget.value)}
+ withAsterisk
+ style={{ flexGrow: 1 }}
+ readOnly={step !== "form"}
+ />
+
+
+ {step !== "form" && (
+ setOtp(e.currentTarget.value)}
+ withAsterisk
+ mt="sm"
+ maxLength={6}
+ readOnly={otpValidated}
+ />
+ )}
+
+
+
+ {/* Buttons */}
+
+
+
+
+
+ );
+}