Wip : Admin module in progress
This commit is contained in:
4
TODO.md
4
TODO.md
@@ -17,6 +17,10 @@
|
|||||||
- In Every OTP page "Resend button" & 5 min timing of expiry.
|
- In Every OTP page "Resend button" & 5 min timing of expiry.
|
||||||
- OTP binding with actual mobile number.
|
- OTP binding with actual mobile number.
|
||||||
- IN settings page NOTE position Fixing.
|
- IN settings page NOTE position Fixing.
|
||||||
|
- Admin page
|
||||||
|
- give rights
|
||||||
|
- view rights (Pending)
|
||||||
|
- Forget Password
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -81,6 +81,12 @@ scp -P 9022 Smsservice/smsserviceapplication.jar <username>@localhost:/home/<use
|
|||||||
|
|
||||||
# ssh nabanita@localhost -p 9022
|
# ssh nabanita@localhost -p 9022
|
||||||
```
|
```
|
||||||
|
## About Backend
|
||||||
|
|
||||||
|
- If user "is_first_login" = true means Users did not login in IB.
|
||||||
|
- **ib_access_level** or **mb_access_level**
|
||||||
|
- **0** → Disabled
|
||||||
|
- **1** → Transaction
|
||||||
|
- **2** → Read Only
|
||||||
|
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ export const generateCSV = (
|
|||||||
// CSV Header
|
// CSV Header
|
||||||
let csv = `Bank Statement\n`;
|
let csv = `Bank Statement\n`;
|
||||||
csv += `Account No:,${accountNo}\n`;
|
csv += `Account No:,${accountNo}\n`;
|
||||||
csv += `Available Balance: ${balance}\n`;
|
csv += `Available Balance:,${balance}\n`;
|
||||||
csv += `Generated:,${new Date().toLocaleString()}\n\n`;
|
csv += `Generated:,${new Date().toLocaleString()}\n\n`;
|
||||||
|
|
||||||
// Column headers
|
// Column headers
|
||||||
|
@@ -41,7 +41,7 @@ export const generatePDF = (
|
|||||||
const content = `
|
const content = `
|
||||||
<div style="font-family:Arial, sans-serif;">
|
<div style="font-family:Arial, sans-serif;">
|
||||||
${headerHTML}
|
${headerHTML}
|
||||||
<h3>Account Statement</h3>
|
<h3 style ="text-align:center;">Account Statement</h3>
|
||||||
<p><strong>Account No:</strong> ${accountNo}</p>
|
<p><strong>Account No:</strong> ${accountNo}</p>
|
||||||
<p><strong>Available Balance:</strong> ₹ ${parseFloat(
|
<p><strong>Available Balance:</strong> ₹ ${parseFloat(
|
||||||
balance
|
balance
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { TextInput, Button, Title, Stack, Radio, Group, Text, Divider, LoadingOverlay, Box, Modal, Card, Checkbox } from '@mantine/core';
|
import { TextInput, Button, Title, Stack, Group, Text, Divider, LoadingOverlay, Box, Modal, Checkbox } from '@mantine/core';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
export default function UserConfiguration() {
|
export default function UserConfiguration() {
|
||||||
|
const router = useRouter();
|
||||||
const [CIF, setCIF] = useState('');
|
const [CIF, setCIF] = useState('');
|
||||||
const [userDetails, setUserDetails] = useState<any>(null);
|
const [userDetails, setUserDetails] = useState<any>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -12,9 +14,9 @@ export default function UserConfiguration() {
|
|||||||
const [showPreviewModal, setShowPreviewModal] = useState(false);
|
const [showPreviewModal, setShowPreviewModal] = useState(false);
|
||||||
const [confirmedPreview, setConfirmedPreview] = useState(false);
|
const [confirmedPreview, setConfirmedPreview] = useState(false);
|
||||||
const [ibEnabled, setIbEnabled] = useState(false);
|
const [ibEnabled, setIbEnabled] = useState(false);
|
||||||
const [ibAccess, setIbAccess] = useState<"read" | "transaction" | "">("");
|
const [ibAccess, setIbAccess] = useState<"2" | "1" | "">("");
|
||||||
const [mbEnabled, setMbEnabled] = useState(false);
|
const [mbEnabled, setMbEnabled] = useState(false);
|
||||||
const [mbAccess, setMbAccess] = useState<"read" | "transaction" | "">("");
|
const [mbAccess, setMbAccess] = useState<"2" | "1" | "">("");
|
||||||
const isValidated = !!userDetails;
|
const isValidated = !!userDetails;
|
||||||
const canSubmit = isValidated && !!savingsAccount && confirmedPreview;
|
const canSubmit = isValidated && !!savingsAccount && confirmedPreview;
|
||||||
|
|
||||||
@@ -60,6 +62,10 @@ export default function UserConfiguration() {
|
|||||||
address: user.custaddress,
|
address: user.custaddress,
|
||||||
activeAccount: user.activeAccounts,
|
activeAccount: user.activeAccounts,
|
||||||
});
|
});
|
||||||
|
if (!user.mobileno) {
|
||||||
|
localStorage.setItem("user_mob_no", user.mobileno);
|
||||||
|
setAccountError('User not have any registered Mobile Number');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('User not found or data format incorrect');
|
throw new Error('User not found or data format incorrect');
|
||||||
}
|
}
|
||||||
@@ -74,7 +80,31 @@ export default function UserConfiguration() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handlePreview = () => {
|
||||||
|
const hasRight = ibAccess || mbAccess;
|
||||||
|
const hasSavings = savingsAccount;
|
||||||
|
|
||||||
|
if (!hasRight) {
|
||||||
|
notifications.show({
|
||||||
|
title: "Rights Required",
|
||||||
|
message: "Please select at least one rights (Internet Banking or Mobile Banking).",
|
||||||
|
color: "red",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSavings) {
|
||||||
|
notifications.show({
|
||||||
|
title: "Savings Account Required",
|
||||||
|
message: "User must have at least one Savings account.",
|
||||||
|
color: "red",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setShowPreviewModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
if (!canSubmit) {
|
if (!canSubmit) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: 'red',
|
color: 'red',
|
||||||
@@ -84,26 +114,113 @@ export default function UserConfiguration() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const rightsData = {
|
try {
|
||||||
CIF,
|
const token = localStorage.getItem("admin_access_token");
|
||||||
};
|
const response = await fetch('/api/auth/admin/user/rights', {
|
||||||
console.log('Submitting rights:', rightsData);
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
CIF: CIF,
|
||||||
|
ib_access_level: ibAccess,
|
||||||
|
mb_access_level: mbAccess
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
if (data?.otp) {
|
||||||
|
const otp = data.otp;
|
||||||
|
try {
|
||||||
|
const otp_response = await fetch('/api/otp/send', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
mobileNumber: localStorage.getItem("user_mob_no"),
|
||||||
|
type: "REGISTRATION",
|
||||||
|
userOtp: otp
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
// const otp_result = await otp_response.json();
|
||||||
|
if (otp_response.ok) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
title: 'Submitted',
|
title: 'Submitted',
|
||||||
message: `User -${CIF} rights submitted successfully.`,
|
message: `User ${CIF} rights submitted successfully. The password has been sent to the user as an OTP.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (err: any) {
|
||||||
|
notifications.show({
|
||||||
|
color: 'red',
|
||||||
|
title: 'Failed to Send',
|
||||||
|
message: `Rights for User ${CIF} saved successfully. Message delivery failed.`,
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(data?.message){
|
||||||
|
try {
|
||||||
|
const otp_response = await fetch('/api/otp/send', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
mobileNumber: localStorage.getItem("user_mob_no"),
|
||||||
|
type: "RIGHT_UPDATE",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (otp_response.ok) {
|
||||||
|
notifications.show({
|
||||||
|
color: 'blue',
|
||||||
|
title: 'Submitted',
|
||||||
|
message: `User ${CIF} rights updated successfully. The confirmation has been sent to the user.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err: any) {
|
||||||
|
notifications.show({
|
||||||
|
color: 'red',
|
||||||
|
title: 'Failed to Send',
|
||||||
|
message: `Rights for User ${CIF} saved successfully. Message delivery failed.`,
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (response.status === 401 || data.message === 'invalid or expired token') {
|
||||||
|
localStorage.removeItem("admin_access_token");
|
||||||
|
localStorage.clear();
|
||||||
|
sessionStorage.clear();
|
||||||
|
router.push('/administrator/login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err: any) {
|
||||||
|
notifications.show({
|
||||||
|
color: 'red',
|
||||||
|
title: 'Failed -Right',
|
||||||
|
message: `Can not Processed user -${CIF} rights.`,
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
setSavingsAccount(null);
|
setSavingsAccount(null);
|
||||||
setUserDetails(null);
|
setUserDetails(null);
|
||||||
setCIF('');
|
setCIF('');
|
||||||
|
setConfirmedPreview(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
const handleMainAction = () => {
|
const handleMainAction = () => {
|
||||||
if (!isValidated) {
|
if (!isValidated) {
|
||||||
handleValidate();
|
handleValidate();
|
||||||
} else if (!confirmedPreview) {
|
} else if (!confirmedPreview) {
|
||||||
setShowPreviewModal(true);
|
handlePreview();
|
||||||
} else if (isValidated && confirmedPreview) {
|
} else if (isValidated && confirmedPreview) {
|
||||||
handleSubmit();
|
handleSubmit();
|
||||||
}
|
}
|
||||||
@@ -176,6 +293,7 @@ export default function UserConfiguration() {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box w={100}>
|
<Box w={100}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
disabled={isValidated && confirmedPreview}
|
||||||
checked={ibEnabled}
|
checked={ibEnabled}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setIbEnabled(e.currentTarget.checked);
|
setIbEnabled(e.currentTarget.checked);
|
||||||
@@ -185,16 +303,16 @@ export default function UserConfiguration() {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box w={120}>
|
<Box w={120}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!ibEnabled}
|
disabled={!ibEnabled || isValidated && confirmedPreview}
|
||||||
checked={ibAccess === "transaction"}
|
checked={ibAccess === "1"}
|
||||||
onChange={() => setIbAccess("transaction")}
|
onChange={() => setIbAccess("1")}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box w={100}>
|
<Box w={100}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!ibEnabled}
|
disabled={!ibEnabled || isValidated && confirmedPreview}
|
||||||
checked={ibAccess === "read"}
|
checked={ibAccess === "2"}
|
||||||
onChange={() => setIbAccess("read")}
|
onChange={() => setIbAccess("2")}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -206,6 +324,7 @@ export default function UserConfiguration() {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box w={100}>
|
<Box w={100}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
disabled={isValidated && confirmedPreview}
|
||||||
checked={mbEnabled}
|
checked={mbEnabled}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setMbEnabled(e.currentTarget.checked);
|
setMbEnabled(e.currentTarget.checked);
|
||||||
@@ -215,16 +334,16 @@ export default function UserConfiguration() {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box w={120}>
|
<Box w={120}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!mbEnabled}
|
disabled={!mbEnabled || isValidated && confirmedPreview}
|
||||||
checked={mbAccess === "transaction"}
|
checked={mbAccess === "1"}
|
||||||
onChange={() => setMbAccess("transaction")}
|
onChange={() => setMbAccess("1")}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box w={100}>
|
<Box w={100}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!mbEnabled}
|
disabled={!mbEnabled || isValidated && confirmedPreview}
|
||||||
checked={mbAccess === "read"}
|
checked={mbAccess === "2"}
|
||||||
onChange={() => setMbAccess("read")}
|
onChange={() => setMbAccess("2")}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -247,7 +366,34 @@ export default function UserConfiguration() {
|
|||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput label="CIF" value={CIF} readOnly disabled />
|
<Text><strong>CIF:</strong> {CIF}</Text>
|
||||||
|
<Text><strong>Savings Account:</strong> {savingsAccount}</Text>
|
||||||
|
<Text><strong>Mobile Number:</strong> {userDetails?.mobile ?? "N/A"}</Text>
|
||||||
|
<Divider label='Rights' size="xs" />
|
||||||
|
<TextInput
|
||||||
|
label="Internet Banking"
|
||||||
|
value={
|
||||||
|
ibAccess === "1"
|
||||||
|
? "Transaction"
|
||||||
|
: ibAccess === "2"
|
||||||
|
? "Read"
|
||||||
|
: "No Rights"
|
||||||
|
}
|
||||||
|
readOnly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Mobile Banking"
|
||||||
|
value={
|
||||||
|
mbAccess === "1"
|
||||||
|
? "Transaction"
|
||||||
|
: mbAccess === "2"
|
||||||
|
? "Read"
|
||||||
|
: "No Rights"
|
||||||
|
}
|
||||||
|
readOnly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Group p="right" mt="md">
|
<Group p="right" mt="md">
|
||||||
<Button variant="default" onClick={() => setShowPreviewModal(false)}>Cancel</Button>
|
<Button variant="default" onClick={() => setShowPreviewModal(false)}>Cancel</Button>
|
||||||
|
@@ -6,7 +6,7 @@ import { Providers } from "@/app/providers";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import NextImage from "next/image";
|
import NextImage from "next/image";
|
||||||
import logo from '@/app/image/logo1.jpg';
|
import logo from '@/app/image/logo1.jpg';
|
||||||
import { IconLogout, IconPhoneFilled, IconUsers, IconUserScreen } from "@tabler/icons-react";
|
import { IconEye, IconLogout, IconPhoneFilled, IconUsers, IconUserScreen } from "@tabler/icons-react";
|
||||||
import UserConfiguration from "./UserConfiguration";
|
import UserConfiguration from "./UserConfiguration";
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
@@ -19,6 +19,8 @@ export default function Login() {
|
|||||||
async function handleLogout(e: React.FormEvent) {
|
async function handleLogout(e: React.FormEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
localStorage.removeItem("admin_access_token");
|
localStorage.removeItem("admin_access_token");
|
||||||
|
localStorage.clear();
|
||||||
|
sessionStorage.clear();
|
||||||
router.push("/administrator/login");
|
router.push("/administrator/login");
|
||||||
}
|
}
|
||||||
async function handleFetchUserDetails(e: React.FormEvent) {
|
async function handleFetchUserDetails(e: React.FormEvent) {
|
||||||
@@ -82,7 +84,7 @@ export default function Login() {
|
|||||||
if (authorized) {
|
if (authorized) {
|
||||||
return (
|
return (
|
||||||
<Providers>
|
<Providers>
|
||||||
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "100%",}}>
|
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "100%", }}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
@@ -127,11 +129,11 @@ export default function Login() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{/* layout and */}
|
{/* layout and */}
|
||||||
<Box style={{ display: 'flex', height: '88vh' }}>
|
<Box style={{ display: 'flex', height: '90vh' }}>
|
||||||
{/* Sidebar manually placed under header */}
|
{/* Sidebar manually placed under header */}
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: 220,
|
width: 250,
|
||||||
background: "#ebf5f5ff",
|
background: "#ebf5f5ff",
|
||||||
// background: "linear-gradient(90deg,rgba(42, 123, 155, 1) 0%, rgba(87, 199, 133, 1) 30%)",
|
// background: "linear-gradient(90deg,rgba(42, 123, 155, 1) 0%, rgba(87, 199, 133, 1) 30%)",
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
@@ -142,19 +144,19 @@ export default function Login() {
|
|||||||
<Divider my="xs" label="Menu" labelPosition="center" />
|
<Divider my="xs" label="Menu" labelPosition="center" />
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text td="underline" variant="light" style={{ cursor: 'pointer' }} onClick={() => handleClick('UserConf')}>
|
<Text td="underline" variant="light" style={{ cursor: 'pointer' }} onClick={() => handleClick('UserConf')}>
|
||||||
<IconUsers /> User Configuration
|
<IconUsers size ="15"/> User Configuration
|
||||||
</Text>
|
</Text>
|
||||||
<Text td="underline" variant="light" style={{ cursor: 'pointer' }} onClick={() => handleClick('ViewUser')}>
|
<Text td="underline" variant="light" style={{ cursor: 'pointer' }} onClick={() => handleClick('ViewUser')}>
|
||||||
<IconUserScreen /> View Users
|
<IconEye size ="15"/> View User Configuration
|
||||||
</Text>
|
</Text>
|
||||||
<Text td="underline" variant="light" style={{ cursor: 'pointer' }} onClick={handleLogout}>
|
<Text td="underline" variant="light" style={{ cursor: 'pointer' }} onClick={handleLogout}>
|
||||||
<IconLogout /> Logout
|
<IconLogout size ="15"/> Logout
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<Stack style={{ flex: 1, padding: '1rem' }}>
|
<Stack style={{ flex: 1, padding: '1rem'}}>
|
||||||
<Box>
|
<Box>
|
||||||
<Text c="Blue" size="lg" fs='italic'>Welcome ,{name} </Text>
|
<Text c="Blue" size="lg" fs='italic'>Welcome ,{name} </Text>
|
||||||
<Text size="xs" c="gray" style={{ fontFamily: "inter", fontSize: '13px' }}>
|
<Text size="xs" c="gray" style={{ fontFamily: "inter", fontSize: '13px' }}>
|
||||||
@@ -163,13 +165,14 @@ export default function Login() {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
{view === 'userConf' && <UserConfiguration />}
|
{view === 'userConf' && <UserConfiguration />}
|
||||||
{view === 'view' && <Text size="xl">view</Text>}
|
{view === 'view' && <Text size="xl">Feature will be available soon ....</Text>}
|
||||||
{!view &&
|
{!view &&
|
||||||
<Text size="xl">Welcome To The Admin Portal</Text>
|
<Text size="xl">Welcome To The Admin Portal</Text>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Text size="sm" style={{textAlign:"center"}}>© 2025 The Kangra Central Co-Operative Bank</Text>
|
||||||
</div>
|
</div>
|
||||||
</Providers>
|
</Providers>
|
||||||
);
|
);
|
||||||
|
@@ -290,12 +290,12 @@ export default function Login() {
|
|||||||
mt="sm"
|
mt="sm"
|
||||||
/>
|
/>
|
||||||
<Box style={{ textAlign: "right" }}>
|
<Box style={{ textAlign: "right" }}>
|
||||||
<Anchor
|
{/* <Anchor
|
||||||
style={{ fontSize: "14px", color: "#1c7ed6", cursor: "pointer" }}
|
style={{ fontSize: "14px", color: "#1c7ed6", cursor: "pointer" }}
|
||||||
onClick={() => router.push("/ValidateUser")}
|
onClick={() => router.push("/ValidateUser")}
|
||||||
>
|
>
|
||||||
Forgot Password?
|
Forgot Password?
|
||||||
</Anchor>
|
</Anchor> */}
|
||||||
</Box>
|
</Box>
|
||||||
<Group mt="sm" align="center">
|
<Group mt="sm" align="center">
|
||||||
<Box style={{ backgroundColor: "#fff", fontSize: "18px", textDecoration: "line-through", padding: "4px 8px", fontFamily: "cursive" }}>{captcha}</Box>
|
<Box style={{ backgroundColor: "#fff", fontSize: "18px", textDecoration: "line-through", padding: "4px 8px", fontFamily: "cursive" }}>{captcha}</Box>
|
||||||
|
Reference in New Issue
Block a user