Compare commits
No commits in common. "e83cbaafa8d8314e5ef535540cbf54ace7ab3d94" and "adce955f69daf49899d50b30a0fcd875c3c2061e" have entirely different histories.
e83cbaafa8
...
adce955f69
|
|
@ -2,22 +2,6 @@ import bcrypt from "bcrypt";
|
|||
import jwt from "jsonwebtoken";
|
||||
import { VAModel, LoggedModel, ClientModel, ProjectModel } from "../models/vaModel.js";
|
||||
|
||||
|
||||
|
||||
async function logVaTime(req, res) {
|
||||
try {
|
||||
const payload = req.body || {};
|
||||
const { vaId, vaEmail, projectId, type } = payload;
|
||||
|
||||
if (!vaId, !vaEmail, !projectId, !type) return res.status(400).send("Missing Props");
|
||||
await LoggedModel.insert({ loggedType: type, vaId, vaEmail, projectId })
|
||||
|
||||
return res.status(201).send({ success: true, message: `${type} Successfully` });
|
||||
} catch (error) {
|
||||
return res.status(500).send("Internal Server Error");
|
||||
}
|
||||
}
|
||||
|
||||
// Getting all active VA's and their details
|
||||
async function va_callback(req, res) {
|
||||
try {
|
||||
|
|
@ -34,7 +18,7 @@ async function va_callback(req, res) {
|
|||
return res.status(200).json({ success: true, count: 0, data: [] });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const vas = await VAModel.find({ _id: { $in: vaIds } }).lean().exec();
|
||||
|
||||
|
|
@ -57,18 +41,18 @@ async function va_register(req, res) {
|
|||
try {
|
||||
const payload = req.body ? JSON.parse(req.body) : {};
|
||||
const { userDetails } = payload;
|
||||
|
||||
|
||||
if (!userDetails || !userDetails.email || !userDetails.firstName || !userDetails.password) {
|
||||
return res.status(400).json({ success: false, message: "Missing required fields" });
|
||||
}
|
||||
|
||||
|
||||
if (userDetails.password !== userDetails.cPassword) {
|
||||
return res.status(400).json({ success: false, message: "password not matching" });
|
||||
}
|
||||
|
||||
const saltRounds = 10;
|
||||
const hashedPassword = await bcrypt.hash(userDetails.password, saltRounds);
|
||||
|
||||
|
||||
// sanitize/construct allowed fields
|
||||
const toCreate = {
|
||||
userDetails: {
|
||||
|
|
@ -105,7 +89,7 @@ async function va_login_callback(req, res) {
|
|||
// const payload = req.body ? JSON.parse(req.body) : {};
|
||||
const payload = req.body || {};
|
||||
const { userDetails } = payload;
|
||||
|
||||
|
||||
if (!userDetails || !userDetails.email || !userDetails.password) {
|
||||
return res.status(400).json({ success: false, message: "Please provide email and password" });
|
||||
}
|
||||
|
|
@ -129,7 +113,7 @@ async function va_login_callback(req, res) {
|
|||
return res.status(401).json({ success: false, message: "Invalid password" });
|
||||
}
|
||||
|
||||
// Generate JWT accessToken with 24h expiry
|
||||
// Generate JWT accessToken with 24h expiry
|
||||
const accessToken = jwt.sign(
|
||||
{
|
||||
id: user._id,
|
||||
|
|
@ -140,7 +124,7 @@ async function va_login_callback(req, res) {
|
|||
{ expiresIn: "24h" }
|
||||
);
|
||||
|
||||
// Generate JWT refreshToken with 24h expiry
|
||||
// Generate JWT refreshToken with 24h expiry
|
||||
const refreshToken = jwt.sign(
|
||||
{
|
||||
id: user._id,
|
||||
|
|
@ -151,14 +135,10 @@ async function va_login_callback(req, res) {
|
|||
{ expiresIn: "30d" }
|
||||
);
|
||||
|
||||
await VAModel.updateOne({ "userDetails.email": userDetails.email }, {
|
||||
$set: { accessToken, refreshToken }
|
||||
})
|
||||
|
||||
// Return accessToken and user info
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Login Successfull",
|
||||
message: "Login successful",
|
||||
accessToken,
|
||||
refreshToken,
|
||||
user: {
|
||||
|
|
@ -172,33 +152,6 @@ async function va_login_callback(req, res) {
|
|||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
async function va_logout_callback(req, res) {
|
||||
try {
|
||||
// const payload = req.body ? JSON.parse(req.body) : {};
|
||||
const vaId = req.body.vaId || {};
|
||||
const vaEmail = req.body.vaEmail || {};
|
||||
if (!vaId || !vaEmail) return res.status(400).json({ success: false, message: "Invalid Credentials" });
|
||||
|
||||
// Find user by email
|
||||
const user = await VAModel.findOne({ "userDetails.email": vaEmail });
|
||||
if (!user) {
|
||||
return res.status(404).json({ success: false, message: "Invalid credentials" });
|
||||
}
|
||||
|
||||
await VAModel.updateOne({ "userDetails.email": userDetails.email }, {
|
||||
$set: { accessToken: null, refreshToken: null }
|
||||
})
|
||||
|
||||
// Return accessToken and user info
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Log Out Successfull",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("va_register error:", error);
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh Token VA
|
||||
async function va_refresh_token_callback(req, res) {
|
||||
|
|
@ -211,7 +164,7 @@ async function va_refresh_token_callback(req, res) {
|
|||
message: "Refresh token missing",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 🔍 Verify refresh token
|
||||
jwt.verify(
|
||||
refreshToken,
|
||||
|
|
@ -405,110 +358,35 @@ async function va_clients_projects_callback(req, res) {
|
|||
}
|
||||
}
|
||||
|
||||
// Getting VA log between specific dates with optional screenshot
|
||||
async function va_log_callback(req, res) {
|
||||
try {
|
||||
const { projectId, from, to } = req.params;
|
||||
const { vaId } = req.body;
|
||||
|
||||
if (!projectId) return res.status(400).json({ success: false, message: "Missing Props" })
|
||||
|
||||
const project = await ProjectModel.findById(projectId);
|
||||
|
||||
if (!project) return res.status(400).json({ success: false, message: "Project not found" });
|
||||
|
||||
// Default range
|
||||
const fromDate = from && from !== "undefined" ? new Date(from) : new Date(project.startDate || "2000-01-01");
|
||||
const toDate = to && to !== "undefined" ? new Date(to) : new Date();
|
||||
|
||||
// If from and to are the same day
|
||||
if (from === to) {
|
||||
fromDate.setHours(0, 0, 0, 0);
|
||||
toDate.setHours(23, 59, 59, 999);
|
||||
} else {
|
||||
// Otherwise use them as a normal range
|
||||
toDate.setHours(23, 59, 59, 999);
|
||||
}
|
||||
|
||||
const query = {
|
||||
vaId: vaId,
|
||||
projectId,
|
||||
time: {
|
||||
$gte: fromDate,
|
||||
$lte: toDate
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const logs = await LoggedModel.find(query).sort({ time: 1 });
|
||||
|
||||
if (!logs.length) {
|
||||
return res.json({ success: true, message: "No logs found", totalWork: "0h 0m" });
|
||||
}
|
||||
|
||||
// Find first LOGIN and last LOGOUT
|
||||
const firstLogin = logs.find((l) => l.loggedType === "LOGIN");
|
||||
const lastLogout = [...logs].reverse().find((l) => l.loggedType === "LOGOUT");
|
||||
|
||||
if (!firstLogin || !lastLogout) {
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "Incomplete log data (missing LOGIN or LOGOUT)",
|
||||
totalWork: "0h 0m",
|
||||
firstLogin,
|
||||
lastLogout,
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate duration
|
||||
const diffMs = new Date(lastLogout.time) - new Date(firstLogin.time);
|
||||
const totalMinutes = Math.floor(diffMs / (1000 * 60));
|
||||
const hours = Math.floor(totalMinutes / 60);
|
||||
const minutes = totalMinutes % 60;
|
||||
|
||||
res.json({
|
||||
const { id, from, to, screentShot } = req.params;
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
vaId,
|
||||
projectId,
|
||||
firstLogin: firstLogin.time,
|
||||
lastLogout: lastLogout.time,
|
||||
logs: logs,
|
||||
totalTimeLogged: `${hours}h ${minutes}m`,
|
||||
hoursLogged: hours,
|
||||
minutesLogged: minutes,
|
||||
message: "VA Log Callback",
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
screentShot
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ success: false, message: err.message });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Getting logged hours in a specific project of VA
|
||||
async function va_project_log_callback(req, res) {
|
||||
try {
|
||||
const payload = req.body || {};
|
||||
const { vaId, vaEmail, projectId, type } = payload;
|
||||
|
||||
if (!vaId, !vaEmail, !projectId, !type) return res.status(400).send("Missing Props");
|
||||
await LoggedModel.create({ loggedType: type, vaId, vaEmail, projectId })
|
||||
|
||||
return res.status(201).send({ success: true, message: `${type} Successfully` });
|
||||
const { id, projectID } = req.params;
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "VA Project Log Callback",
|
||||
id,
|
||||
projectID
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(500).send("Internal Server Error");
|
||||
}
|
||||
}
|
||||
|
||||
async function va_time_logged_on_proj(req, res) {
|
||||
try {
|
||||
const payload = req.body || {};
|
||||
const { vaId, vaEmail, projectId, type } = payload;
|
||||
|
||||
if (!vaId, !vaEmail, !projectId, !type) return res.status(400).send("Missing Props");
|
||||
await LoggedModel.create({ loggedType: type, vaId, vaEmail, projectId })
|
||||
|
||||
return res.status(201).send({ success: true, message: `${type} Successfully` });
|
||||
} catch (error) {
|
||||
return res.status(500).send("Internal Server Error");
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -599,7 +477,6 @@ async function va_filter_search(req, res) {
|
|||
|
||||
|
||||
export {
|
||||
logVaTime,
|
||||
va_callback,
|
||||
va_username_callback,
|
||||
va_clients_callback,
|
||||
|
|
@ -610,6 +487,5 @@ export {
|
|||
va_register,
|
||||
va_projects_callback,
|
||||
va_login_callback,
|
||||
va_logout_callback,
|
||||
va_refresh_token_callback,
|
||||
va_refresh_token_callback
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,36 +1,76 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import { VAModel } from "../models/vaModel.js";
|
||||
import AuthRepository from "../repository/auth.repository.js";
|
||||
import AuthService from "../services/auth.service.js";
|
||||
|
||||
import TemplateRepository from "../repository/template.repository.js";
|
||||
import TemplateService from "../services/template.service.js";
|
||||
|
||||
import httpReqRefToken from "../httpReq/refreshToken.httpReq.js";
|
||||
import httpReqValidateAuth from "../httpReq/validateAuth.httpReq.js";
|
||||
import { decryptCryptr, encryptCryptr } from "../helper/cryptrEncDec.js";
|
||||
import logger from "../config/logger.js";
|
||||
|
||||
const authService = new AuthService(new AuthRepository());
|
||||
const templateService = new TemplateService(new TemplateRepository());
|
||||
|
||||
|
||||
async function authValidator(req, res, next) {
|
||||
try {
|
||||
const accessToken = req.headers.token;
|
||||
if (!accessToken) return res.status(401).json({ success: false, message: "Not Authorized" });
|
||||
logger.info("AuthValidator - middleware is running ...")
|
||||
logger.info("Process ID", process.pid)
|
||||
let templateId = null;
|
||||
let orgId;
|
||||
if ('template' in req.body && 'org_auth_id' in req.body.template && req.body.template['org_auth_id'] != null) {
|
||||
|
||||
const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN);
|
||||
|
||||
if (decoded && ("id" in decoded) && decoded.id !== null && ("email" in decoded)) {
|
||||
const vaEmail = decoded.email;
|
||||
|
||||
const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail })
|
||||
if (!vaUser) {
|
||||
return res.status(401).json({ success: false, message: "Not Authorized" });
|
||||
}
|
||||
if (!("accessToken" in vaUser) || !vaUser.accessToken) {
|
||||
return res.status(401).json({ success: false, message: "Not Authorized" });
|
||||
}
|
||||
|
||||
if (vaUser.accessToken === accessToken) {
|
||||
req.body.vaId = vaUser._id
|
||||
req.body.vaEmail = vaEmail
|
||||
next();
|
||||
} else {
|
||||
return res.status(401).json({ success: false, message: "Not Authorized" });
|
||||
orgId = req.body.template.org_auth_id;
|
||||
} else {
|
||||
if ('templateId' in req.body) {
|
||||
templateId = req.body.templateId;
|
||||
} else if ('id' in req.body.template) {
|
||||
templateId = req.body.template.id;
|
||||
}
|
||||
|
||||
const tempRes = await templateService.fetch({ _id: templateId });
|
||||
const isPublish = tempRes.json_template !== null;
|
||||
req.body.isPublish = isPublish;
|
||||
orgId = tempRes.org_auth_id;
|
||||
}
|
||||
|
||||
let authEntryExist = await authService.fetchByOrgId(orgId);
|
||||
|
||||
if (!authEntryExist) {
|
||||
logger.error("AuthValidator - Entry not exist templateId:", templateId);
|
||||
return res.status(403).send({"message": "Entry not exist"});
|
||||
}
|
||||
|
||||
let { instance_url, refresh_token, access_token, is_sandbox } = authEntryExist;
|
||||
|
||||
access_token = decryptCryptr(access_token)
|
||||
refresh_token = decryptCryptr(refresh_token)
|
||||
|
||||
let isValid = await httpReqValidateAuth(orgId, access_token, instance_url, refresh_token)
|
||||
|
||||
if (!isValid) {
|
||||
logger.info("AuthValidator - Access_Token expired - refreshing ...");
|
||||
const refTokenRes = await httpReqRefToken(refresh_token, is_sandbox);
|
||||
access_token = refTokenRes.access_token;
|
||||
await authService.update({ org_id: orgId }, { access_token: encryptCryptr(access_token) });
|
||||
}
|
||||
|
||||
isValid = await httpReqValidateAuth(orgId, access_token, instance_url, refresh_token)
|
||||
|
||||
if (!isValid) {
|
||||
logger.error("AuthValidator - Not Refreshing RefreshingToken Expired return 401. access_token");
|
||||
return res.send(401).status({"message": "Token expired (acc | ref)"})
|
||||
}
|
||||
|
||||
req.body.access_token = access_token;
|
||||
req.body.instance_url = instance_url;
|
||||
|
||||
logger.info("AuthValidator - middleware Work Done (VALIDATED SUCCESSFULLY) ...")
|
||||
next();
|
||||
} catch (error) {
|
||||
logger.error("AuthValidator - Middleware (CATCH Block)", error.message);
|
||||
return res.send(401).status({ "message": error.message })
|
||||
return res.send(401).status({"message": error.message})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ const projectSchema = new Schema({
|
|||
},
|
||||
totalLoggedHours: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
vaAssigned: [
|
||||
{
|
||||
|
|
@ -114,14 +113,6 @@ const loggedSchema = new Schema({
|
|||
vaId: {
|
||||
type: Types.ObjectId,
|
||||
ref: "VA"
|
||||
},
|
||||
vaEmail: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
projectId: {
|
||||
type: Types.ObjectId,
|
||||
ref: "Project"
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -136,12 +127,6 @@ const vaSchema = new Schema(
|
|||
ref: "Client",
|
||||
},
|
||||
],
|
||||
projectIds: [
|
||||
{
|
||||
type: Types.ObjectId,
|
||||
ref: "Project",
|
||||
},
|
||||
],
|
||||
schedule: {
|
||||
type: String,
|
||||
},
|
||||
|
|
@ -150,47 +135,14 @@ const vaSchema = new Schema(
|
|||
enum: ["PARTTIME", "FULLTIME", "INACTIVE"],
|
||||
default: "FULLTIME",
|
||||
},
|
||||
accessToken: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
refreshToken: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
// Need to add working hours and time zone tooooo in which VA is going to work, need to calculate total work hours of the day - Schedules
|
||||
// Need to add this Schedule feature on SF there we can change the schedulte of VA - Add in form tooo
|
||||
// Need to ask to client if we want to provide schedule changing functionality on Docker toooooo
|
||||
|
||||
|
||||
// Add IN OUT
|
||||
|
||||
|
||||
// const vaTimeSheetSchema = new Schema({
|
||||
// vaId: {
|
||||
// type: Types.ObjectId,
|
||||
// ref: "VA",
|
||||
// },
|
||||
// projectId: {
|
||||
// type: Types.ObjectId,
|
||||
// ref: "Project",
|
||||
// },
|
||||
// logged: {
|
||||
// enum: ["IN", "OUT"],
|
||||
// default: "IN",
|
||||
// }
|
||||
// },
|
||||
// { timestamps: true }
|
||||
// )
|
||||
|
||||
/* ---------------------- Model Exports ---------------------- */
|
||||
const VAModel = model("VA", vaSchema);
|
||||
const ClientModel = model("Client", clientSchema);
|
||||
const ProjectModel = model("Project", projectSchema);
|
||||
const LoggedModel = model("TrackLog", loggedSchema);
|
||||
const LoggedModel = model("Logged", loggedSchema);
|
||||
|
||||
export { VAModel, ClientModel, ProjectModel, LoggedModel };
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
import express from "express";
|
||||
const vaRoutes = express.Router();
|
||||
|
||||
import { va_callback, va_username_callback, va_clients_callback, va_clients_projects_callback, va_log_callback, va_project_log_callback, va_filter_search, va_projects_callback, va_register, va_login_callback, va_refresh_token_callback, va_logout_callback } from "../../controllers/va.controller.js";
|
||||
import { authValidator } from "../../middlewares/auth.middleware.js";
|
||||
import { va_callback, va_username_callback, va_clients_callback, va_clients_projects_callback, va_log_callback, va_project_log_callback, va_filter_search, va_projects_callback, va_register, va_login_callback, va_refresh_token_callback } from "../../controllers/va.controller.js";
|
||||
|
||||
vaRoutes.get("/", va_callback); // VA Callback for getting active VA
|
||||
|
||||
vaRoutes.post("/login", va_login_callback); // VA Callback for login VA
|
||||
|
||||
vaRoutes.get("/logout", authValidator, va_logout_callback); // VA Callback for login VA
|
||||
|
||||
vaRoutes.post("/refreshToken", va_refresh_token_callback); // VA Callback for refresh token
|
||||
|
||||
vaRoutes.post("/register", va_register); // Register a new VA
|
||||
|
|
@ -22,10 +19,9 @@ vaRoutes.get("/clients/:clientId/projects/:projectId", va_clients_projects_callb
|
|||
|
||||
vaRoutes.get("/project/:projectId", va_projects_callback); // Projects Callback for getting project details
|
||||
|
||||
// FROM || TO -
|
||||
vaRoutes.get("/log/:projectId/:from?/:to?", authValidator, va_log_callback); // VA Log Callback
|
||||
vaRoutes.get("/:id/log/:from/:to/:screentShot", va_log_callback); // VA Log Callback
|
||||
|
||||
vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged Hours in specific Project
|
||||
vaRoutes.get("/:id/log/project/:projectID", va_project_log_callback); // VA Logged Hours in specific Project
|
||||
|
||||
// VA filter search: supports query params `name`, `email`, `from`, `to`
|
||||
// Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31
|
||||
|
|
|
|||
|
|
@ -4,202 +4,132 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>VA Registration</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
.card-registration {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-registration img {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 3rem !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 600;
|
||||
color: #343a40;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-outline {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-control-lg {
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
border-radius: 8px;
|
||||
padding: 0.7rem 2rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: #ffc107;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
background-color: #e0a800;
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
border: 1px solid #ccc;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
border-bottom: 2px solid #ffc107;
|
||||
display: inline-block;
|
||||
padding-bottom: 0.25rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.card-body {
|
||||
padding: 2rem !important;
|
||||
}
|
||||
|
||||
.card-registration img {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="/public/css/va-register.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section class="h-100 py-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center">
|
||||
<div class="col-lg-10 col-xl-9">
|
||||
<div class="card card-registration shadow-lg">
|
||||
<div class="row g-0">
|
||||
|
||||
<!-- Left Image -->
|
||||
<div class="col-xl-5 d-none d-xl-block">
|
||||
<img
|
||||
src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-registration/img4.webp"
|
||||
alt="Registration"
|
||||
class="img-fluid"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Right Form -->
|
||||
<div class="col-xl-7 bg-white">
|
||||
<div class="card-body text-black">
|
||||
<h3 class="text-center mb-4">Virtual Assistant Registration</h3>
|
||||
<section class="h-100">
|
||||
<div class="container py-1">
|
||||
<div class="row d-flex justify-content-center align-items-center">
|
||||
<div class="col">
|
||||
<div class="card card-registration my-4 shadow">
|
||||
<div class="row g-0">
|
||||
<!-- Left Side Image -->
|
||||
<div class="col-xl-6 d-none d-xl-block">
|
||||
<img
|
||||
src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-registration/img4.webp"
|
||||
alt="Registration Image"
|
||||
class="img-fluid"
|
||||
style="border-top-left-radius: .25rem; border-bottom-left-radius: .25rem;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form id="vaForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<!-- Right Side Form -->
|
||||
<div class="col-xl-6">
|
||||
<div class="card-body p-md-5 text-black">
|
||||
<h3 class="mb-5 text-uppercase text-center">Virtual Assistant Registration</h3>
|
||||
|
||||
<form id="vaForm">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="form-outline">
|
||||
<input type="text" id="firstName" name="userDetails[firstName]" class="form-control form-control-lg" required />
|
||||
<label class="form-label" for="firstName">First Name *</label>
|
||||
<input type="text" id="firstName" name="userDetails[firstName]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="lastName">Last Name *</label>
|
||||
<input type="text" id="lastName" name="userDetails[lastName]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="email">Email *</label>
|
||||
<input type="email" id="email" name="userDetails[email]" class="form-control form-control-lg" required>
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="form-outline">
|
||||
<input type="text" id="lastName" name="userDetails[lastName]" class="form-control form-control-lg" required/>
|
||||
<label class="form-label" for="lastName">Last Name</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-outline mb-4">
|
||||
<input type="email" id="email" name="userDetails[email]" class="form-control form-control-lg" required />
|
||||
<label class="form-label" for="email">Email *</label>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="form-outline">
|
||||
<input type="text" id="age" name="userDetails[age]" class="form-control form-control-lg not-required" placeholder=""/>
|
||||
<label class="form-label" for="age">Age</label>
|
||||
<input type="text" id="age" name="userDetails[age]" class="form-control form-control-lg">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
</div>
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="form-outline">
|
||||
<input type="date" id="dob" name="userDetails[dob]" class="form-control form-control-lg" />
|
||||
<label class="form-label" for="dob">Date of Birth</label>
|
||||
<input type="date" id="dob" name="userDetails[dob]" class="form-control form-control-lg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="form-outline">
|
||||
<input type="password" id="password" name="userDetails[password]" class="form-control form-control-lg" required />
|
||||
<label class="form-label" for="password">Password *</label>
|
||||
<input type="password" id="password" name="userDetails[password]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
</div>
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="form-outline">
|
||||
<input type="password" id="cPassword" name="userDetails[cPassword]" class="form-control form-control-lg" required />
|
||||
<label class="form-label" for="cPassword">Confirm Password *</label>
|
||||
<input type="password" id="cPassword" name="userDetails[cPassword]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="address">Address</label>
|
||||
<input type="text" id="address" name="userDetails[address]" class="form-control form-control-lg">
|
||||
<div class="form-outline mb-4">
|
||||
<input type="text" id="address" name="userDetails[address]" class="form-control form-control-lg not-required" placeholder=""/>
|
||||
<label class="form-label" for="address">Address</label>
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4 mb-3">Bank Details</h5>
|
||||
<div class="form-outline mb-4">
|
||||
<input type="text" id="bankName" name="userDetails[bankName]" class="form-control form-control-lg" required/>
|
||||
<label class="form-label" for="bankName">Bank Name</label>
|
||||
</div>
|
||||
<div class="form-outline mb-4">
|
||||
<input type="text" id="accountHolderName" name="userDetails[accountHolderName]" class="form-control form-control-lg" required/>
|
||||
<label class="form-label" for="accountHolderName">Account Holder Name</label>
|
||||
</div>
|
||||
<div class="form-outline mb-4">
|
||||
<input type="text" id="accountNumber" name="userDetails[accountNumber]" class="form-control form-control-lg" required/>
|
||||
<label class="form-label" for="accountNumber">Account Number</label>
|
||||
</div>
|
||||
|
||||
<h5>Bank Details</h5>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="bankName">Bank Name</label>
|
||||
<input type="text" id="bankName" name="userDetails[bankName]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="accountHolderName">Account Holder Name</label>
|
||||
<input type="text" id="accountHolderName" name="userDetails[accountHolderName]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="accountNumber">Account Number</label>
|
||||
<input type="text" id="accountNumber" name="userDetails[accountNumber]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="routingNumber">Routing Number</label>
|
||||
<input type="text" id="routingNumber" name="userDetails[routingNumber]" class="form-control form-control-lg" required>
|
||||
</div>
|
||||
<div class="form-outline mb-4">
|
||||
<input type="text" id="routingNumber" name="userDetails[routingNumber]" class="form-control form-control-lg" required/>
|
||||
<label class="form-label" for="routingNumber">Routing Number</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="status">Status</label>
|
||||
<select id="status" name="status" class="form-select form-select-lg">
|
||||
<option value="FULLTIME">Full-Time</option>
|
||||
<option value="PARTTIME">Part-Time</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="status">Status</label>
|
||||
<select id="status" name="status" class="form-select form-select-lg">
|
||||
<option value="FULLTIME">Full-Time</option>
|
||||
<option value="PARTTIME">Part-Time</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end pt-3">
|
||||
<button type="reset" class="btn btn-light btn-lg">Reset all</button>
|
||||
<button type="submit" class="btn btn-warning btn-lg ms-2">Submit form</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2 pt-3">
|
||||
<button type="reset" class="btn btn-light btn-lg">Reset</button>
|
||||
<button type="submit" class="btn btn-warning btn-lg">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/public/js/va-register.js"></script>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Custom JS -->
|
||||
<script src="/public/js/va-register.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue