merge branch
This commit is contained in:
commit
2d61f25d5b
|
|
@ -55,26 +55,50 @@ async function render_reset_form(req, res) {
|
||||||
try {
|
try {
|
||||||
const { token, userType } = req.query;
|
const { token, userType } = req.query;
|
||||||
|
|
||||||
|
// ❌ Missing params
|
||||||
if (!token || !userType) {
|
if (!token || !userType) {
|
||||||
return res.render('auth/reset-password-error', { error: 'Reset token or user type is missing', userType: userType || 'va' });
|
return res.render('auth/reset-password-error', {
|
||||||
|
title: "Reset Password",
|
||||||
|
layout: 'layouts/auth',
|
||||||
|
error: 'Reset token or user type is missing',
|
||||||
|
userType: userType || 'va'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate token
|
// ✅ Validate token
|
||||||
const validation = await passwordResetService.validateResetToken(token, userType);
|
const validation = await passwordResetService.validateResetToken(token, userType);
|
||||||
|
|
||||||
if (!validation.valid) {
|
if (!validation.valid) {
|
||||||
return res.render('auth/reset-password-error', { error: validation.message, userType: userType || 'va' });
|
return res.render('auth/reset-password-error', {
|
||||||
|
title: "Reset Password",
|
||||||
|
layout: 'layouts/auth',
|
||||||
|
error: validation.message,
|
||||||
|
userType: userType || 'va'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render reset form with token
|
// ✅ Render reset password form
|
||||||
res.render('auth/reset-password-form', { token, userType, error: null });
|
return res.render('auth/reset-password-form', {
|
||||||
|
title: "Reset Password",
|
||||||
|
layout: 'layouts/auth',
|
||||||
|
token,
|
||||||
|
userType,
|
||||||
|
error: null
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`PasswordResetController - Render form error: ${error.message}`);
|
logger.error(`PasswordResetController - Render form error: ${error.message}`);
|
||||||
res.render('auth/reset-password-error', { error: 'Failed to load reset form' });
|
|
||||||
|
return res.render('auth/reset-password-error', {
|
||||||
|
title: "Reset Password",
|
||||||
|
layout: 'layouts/auth',
|
||||||
|
error: 'Failed to load reset form',
|
||||||
|
userType: 'va'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Process password reset form submission (shared for both VA and Client)
|
// Process password reset form submission (shared for both VA and Client)
|
||||||
async function process_reset(req, res) {
|
async function process_reset(req, res) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
161
src/server.js
161
src/server.js
|
|
@ -59,7 +59,9 @@ app.use('/api', apiRoutes);
|
||||||
|
|
||||||
app.get("/download-tracker", (req, res) => {
|
app.get("/download-tracker", (req, res) => {
|
||||||
res.render("download_tracker", {
|
res.render("download_tracker", {
|
||||||
version: "3.2.1"
|
title: 'Download tracker',
|
||||||
|
version: "3.2.1",
|
||||||
|
layout: 'layouts/main'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -74,42 +76,64 @@ app.get("/download-file", (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/varegister/:sf_token?', (req, res) => {
|
app.get('/varegister/:sf_token?', (req, res) => {
|
||||||
const { sf_token } = req.params;
|
const { sf_token } = req.params;
|
||||||
|
|
||||||
try {
|
// No token → invalid request
|
||||||
const decoded = jwt.verify(sf_token, process.env.JWT_SECRET_SF_TOKEN);
|
if (!sf_token) {
|
||||||
|
return res.status(404).render('not_found', {
|
||||||
|
title: 'Page Not Found',
|
||||||
|
layout: 'layouts/error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (decoded) {
|
try {
|
||||||
const email = decoded.email;
|
const decoded = jwt.verify(sf_token, process.env.JWT_SECRET_SF_TOKEN);
|
||||||
res.render('va_register', { title: "VA Registration", email });
|
|
||||||
} else {
|
|
||||||
res.render('not_found', { title: "VA Registration" });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
res.render('not_found', { title: "VA Registration" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// if(verified) {
|
// Token valid
|
||||||
// res.render('va_register', { title: "VA Registration" });
|
return res.render('va_register', {
|
||||||
// } else {
|
title: 'VA Registration',
|
||||||
// res.render('not_founf', { title: "VA Registration" });
|
email: decoded.email,
|
||||||
// }
|
layout: 'layouts/varegisterlayout'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Invalid / expired token
|
||||||
|
return res.status(401).render('not_found', {
|
||||||
|
title: 'Invalid or Expired Link',
|
||||||
|
layout: 'layouts/error'
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/clientregister/:sf_token?', (req, res) => {
|
|
||||||
const { sf_token } = req.params;
|
|
||||||
try {
|
|
||||||
const decoded = jwt.verify(sf_token, process.env.JWT_SECRET_SF_TOKEN);
|
|
||||||
|
|
||||||
if (decoded) {
|
app.get('/clientregister/:sf_token?', (req, res) => {
|
||||||
const email = decoded.email;
|
const { sf_token } = req.params;
|
||||||
res.render('va_client_register', { title: "Client Registration", email });
|
|
||||||
} else {
|
// No token → invalid request
|
||||||
res.render('not_found', { title: "Client Registration" });
|
if (!sf_token) {
|
||||||
}
|
return res.status(404).render('not_found', {
|
||||||
} catch (error) {
|
title: 'Page Not Found',
|
||||||
res.render('not_found', { title: "Client Registration" });
|
layout: 'layouts/error'
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(sf_token, process.env.JWT_SECRET_SF_TOKEN);
|
||||||
|
|
||||||
|
// Token valid
|
||||||
|
return res.render('va_client_register', {
|
||||||
|
title: 'Client Registration',
|
||||||
|
email: decoded.email,
|
||||||
|
layout: 'layouts/vaclientlayout'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Invalid / expired token
|
||||||
|
return res.status(401).render('not_found', {
|
||||||
|
title: 'Invalid or Expired Link',
|
||||||
|
layout: 'layouts/error'
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/auth/password/reset-form', render_reset_form);
|
app.get('/auth/password/reset-form', render_reset_form);
|
||||||
|
|
@ -119,7 +143,11 @@ app.get("/client/login", (req, res) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get("/client/forgot-password", (req, res) => {
|
app.get("/client/forgot-password", (req, res) => {
|
||||||
res.render("forgot_password")
|
|
||||||
|
res.render("forgot_password", {
|
||||||
|
title: "Forgot Password",
|
||||||
|
layout: "layouts/forgotpasswordlayout"
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
// Client Dashboard Routes with new file names
|
// Client Dashboard Routes with new file names
|
||||||
|
|
@ -184,11 +212,12 @@ app.get('/client/virtual-assistants/view-logs', async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.get("/live/:projectId/:vaId", async (req, res) => {
|
app.get("/live/:projectId/:vaId", async (req, res) => {
|
||||||
const { projectId, vaId } = req.params;
|
const { projectId, vaId } = req.params;
|
||||||
|
|
||||||
if (!projectId || !vaId) {
|
if (!projectId || !vaId) {
|
||||||
return res.status(400).send({ "message": "Page not found" });
|
return res.status(400).render("not_found", { title: "Page not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientId = null;
|
let clientId = null;
|
||||||
|
|
@ -199,36 +228,45 @@ app.get("/live/:projectId/:vaId", async (req, res) => {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error fetching client for project:", err.message);
|
console.error("Error fetching client for project:", err.message);
|
||||||
return;
|
return res.status(500).render("not_found", { title: "Server Error" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clientId) return res.status(400).send({ "message": "Page not found" });
|
if (!clientId) {
|
||||||
|
return res.status(400).render("not_found", { title: "Page not found" });
|
||||||
res.render("va_live_tracker", { title: "VA Live Tracker", projectId, vaId, clientId })
|
|
||||||
})
|
|
||||||
|
|
||||||
app.get("/live/:projectId/:vaId", async (req, res) => {
|
|
||||||
const { projectId, vaId } = req.params;
|
|
||||||
|
|
||||||
if (!projectId || !vaId) {
|
|
||||||
return res.status(400).send({ "message": "Page not found" });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientId = null;
|
res.render("va_live_tracker", {
|
||||||
try {
|
title: "VA Live Tracker",
|
||||||
const clientRes = await ProjectModel.findById(projectId);
|
projectId,
|
||||||
if (clientRes) {
|
vaId,
|
||||||
clientId = clientRes.clientId.toString();
|
clientId,
|
||||||
}
|
layout: "layouts/valivetrackerlayout" // ✅ ADD THIS
|
||||||
} catch (err) {
|
});
|
||||||
console.error("Error fetching client for project:", err.message);
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!clientId) return res.status(400).send({ "message": "Page not found" });
|
|
||||||
|
|
||||||
res.render("va_live_tracker", { title: "VA Live Tracker", projectId, vaId, clientId })
|
// app.get("/live/:projectId/:vaId", async (req, res) => {
|
||||||
})
|
// const { projectId, vaId } = req.params;
|
||||||
|
|
||||||
|
// if (!projectId || !vaId) {
|
||||||
|
// return res.status(400).send({ "message": "Page not found" });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let clientId = null;
|
||||||
|
// try {
|
||||||
|
// const clientRes = await ProjectModel.findById(projectId);
|
||||||
|
// if (clientRes) {
|
||||||
|
// clientId = clientRes.clientId.toString();
|
||||||
|
// }
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error("Error fetching client for project:", err.message);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!clientId) return res.status(400).send({ "message": "Page not found" });
|
||||||
|
|
||||||
|
// res.render("va_live_tracker", { title: "VA Live Tracker", projectId, vaId, clientId })
|
||||||
|
// })
|
||||||
|
|
||||||
// Handling Socket
|
// Handling Socket
|
||||||
import { VAModel, ProjectModel, ClientModel } from './models/vaModel.js';
|
import { VAModel, ProjectModel, ClientModel } from './models/vaModel.js';
|
||||||
|
|
@ -522,12 +560,13 @@ app.post("/post-test", (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
res.status(404).render("not_found", {
|
res.status(404).render("not_found", {
|
||||||
title: "404 - Page Not Found"
|
layout: "layouts/error",
|
||||||
});
|
title: "404 - Page Not Found"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(PORT, "0.0.0.0", () => {
|
server.listen(PORT, () => {
|
||||||
mongoose
|
mongoose
|
||||||
.connect(
|
.connect(
|
||||||
process.env.MONGO_URI,
|
process.env.MONGO_URI,
|
||||||
|
|
|
||||||
|
|
@ -1,121 +1,4 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>DocVA | Reset Password</title>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:root[data-theme="light"] {
|
|
||||||
--bg: #ffffff;
|
|
||||||
--text: #111827;
|
|
||||||
--card-bg: #ffffff;
|
|
||||||
--border: #e5e7eb;
|
|
||||||
--btn-bg: #0a0a8f;
|
|
||||||
--btn-text: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[data-theme="dark"] {
|
|
||||||
--bg: #121212;
|
|
||||||
--text: #f9fafb;
|
|
||||||
--card-bg: #1e1e1e;
|
|
||||||
--border: #334155;
|
|
||||||
--btn-bg: #3b82f6;
|
|
||||||
--btn-text: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
font-family: "Inter", sans-serif;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
transition: background-color 0.3s, color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 380px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 170px;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 2rem;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
||||||
transition: background 0.3s, border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.8rem;
|
|
||||||
margin-bottom: 1.2rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: #fff;
|
|
||||||
color: #111;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
transition: border 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[data-theme="dark"] input {
|
|
||||||
background: #0f172a;
|
|
||||||
color: #fff;
|
|
||||||
border: 1px solid #475569;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
background: var(--btn-bg);
|
|
||||||
color: var(--btn-text);
|
|
||||||
border: none;
|
|
||||||
padding: 0.8rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
margin-top: 1rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--text);
|
|
||||||
text-decoration: none;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
opacity: 1;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>DocTracker</h2>
|
<h2>DocTracker</h2>
|
||||||
<form class="card">
|
<form class="card">
|
||||||
|
|
@ -200,6 +83,3 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>
|
||||||
|
<%= title || 'Auth' %>
|
||||||
|
</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" href="/public/images/favicon.svg" sizes="any">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- body %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>404 - Page Not Found</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #ffffff;
|
||||||
|
--text: #222222;
|
||||||
|
--subtext: #555555;
|
||||||
|
--button-bg: #6c63ff;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode automatic */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--bg: #0f0f0f;
|
||||||
|
--text: #ffffff;
|
||||||
|
--subtext: #bbbbbb;
|
||||||
|
--button-bg: #6c63ff;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
font-family: Inter, system-ui, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 420px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 7rem;
|
||||||
|
font-weight: 800;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--subtext);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding: 14px 26px;
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 150px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 4.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<%- body %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>DocVA | Reset Password</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root[data-theme="light"] {
|
||||||
|
--bg: #ffffff;
|
||||||
|
--text: #111827;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--border: #e5e7eb;
|
||||||
|
--btn-bg: #0a0a8f;
|
||||||
|
--btn-text: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] {
|
||||||
|
--bg: #121212;
|
||||||
|
--text: #f9fafb;
|
||||||
|
--card-bg: #1e1e1e;
|
||||||
|
--border: #334155;
|
||||||
|
--btn-bg: #3b82f6;
|
||||||
|
--btn-text: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 170px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: background 0.3s, border-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.8rem;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: #fff;
|
||||||
|
color: #111;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: border 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] input {
|
||||||
|
background: #0f172a;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--btn-bg);
|
||||||
|
color: var(--btn-text);
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text);
|
||||||
|
text-decoration: none;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
opacity: 1;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- body %>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -13,5 +13,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<%- body %>
|
<%- body %>
|
||||||
|
|
||||||
|
<script src="/public/js/common/theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Client Registration</title>
|
||||||
|
|
||||||
|
<link href="/public/css/va-register.css" rel="stylesheet">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- body %>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Live Screen Feed</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
background: #0f0f0f;
|
||||||
|
color: white;
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SIDEBAR */
|
||||||
|
.sidebar {
|
||||||
|
width: 280px;
|
||||||
|
background: #181818;
|
||||||
|
border-right: 1px solid #2b2b2b;
|
||||||
|
padding: 15px;
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: transform 0.35s ease;
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar h3 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
background: #252525;
|
||||||
|
border: 1px solid #333;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app:hover {
|
||||||
|
background: #333;
|
||||||
|
transform: translateX(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app.active {
|
||||||
|
background: #6c63ff;
|
||||||
|
border-color: #6c63ff;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 0 10px rgba(108, 99, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAIN CONTENT */
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#selectedAppTitle {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#liveImage {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 900px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #444;
|
||||||
|
object-fit: contain;
|
||||||
|
background: black;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
font-size: 16px;
|
||||||
|
opacity: 0.8;
|
||||||
|
color: #bbb;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MOBILE RESPONSIVE */
|
||||||
|
.mobile-toggle {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 15px;
|
||||||
|
left: 15px;
|
||||||
|
background: #6C63FF;
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
z-index: 9999;
|
||||||
|
/* VERY HIGH: always visible */
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-toggle span {
|
||||||
|
display: block;
|
||||||
|
width: 26px;
|
||||||
|
height: 3px;
|
||||||
|
background: #fff;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar Hidden on Mobile */
|
||||||
|
@media (max-width: 780px) {
|
||||||
|
body {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.open {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-toggle {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark overlay when sidebar opens */
|
||||||
|
#overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
display: none;
|
||||||
|
z-index: 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
#overlay.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- body %>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Virtual Assistant Registration</title>
|
||||||
|
|
||||||
|
<link href="/public/css/va-register.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
select option:disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- body %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,105 +1,10 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>404 - Page Not Found</title>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg: #ffffff;
|
|
||||||
--text: #222222;
|
|
||||||
--subtext: #555555;
|
|
||||||
--button-bg: #6c63ff;
|
|
||||||
--button-text: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark mode automatic */
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--bg: #0f0f0f;
|
|
||||||
--text: #ffffff;
|
|
||||||
--subtext: #bbbbbb;
|
|
||||||
--button-bg: #6c63ff;
|
|
||||||
--button-text: #ffffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100vh;
|
|
||||||
font-family: Inter, system-ui, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 420px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 7rem;
|
|
||||||
font-weight: 800;
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
margin-top: 10px;
|
|
||||||
color: var(--subtext);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 14px 26px;
|
|
||||||
background: var(--button-bg);
|
|
||||||
color: var(--button-text);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
border-radius: 12px;
|
|
||||||
display: inline-block;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
opacity: 0.85;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 150px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
h1 {
|
|
||||||
font-size: 4.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<img src="/public/images/logo-white-vertical.svg" alt="404 Icon">
|
<img src="/public/images/logo-white-vertical.svg" alt="404 Icon">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<p>Oops! The page you're looking for does not exist.</p>
|
<p>Oops! The page you're looking for does not exist.</p>
|
||||||
<a href="/" class="btn">Go Home</a>
|
<a href="/client/login" class="btn">Go Home</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,6 +29,3 @@
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyTheme);
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyTheme);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,16 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<div class="container">
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Client Registration</title>
|
|
||||||
|
|
||||||
<link href="/public/css/va-register.css" rel="stylesheet">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<!-- Sidebar for desktop -->
|
<!-- Sidebar for desktop -->
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
|
|
@ -21,7 +9,7 @@
|
||||||
<nav class="progress-steps">
|
<nav class="progress-steps">
|
||||||
<div class="step active" data-step="1" onclick="goToStep(1)">
|
<div class="step active" data-step="1" onclick="goToStep(1)">
|
||||||
<div class="step-number">1</div>
|
<div class="step-number">1</div>
|
||||||
<!-- <div class="step-number">✓</div> -->
|
<!-- <div class="step-number">✓</div> -->
|
||||||
<div class="step-text">Create Your Account</div>
|
<div class="step-text">Create Your Account</div>
|
||||||
<div class="step-connector"></div>
|
<div class="step-connector"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -59,7 +47,7 @@
|
||||||
<ol>
|
<ol>
|
||||||
<li class="step active" data-step="1" onclick="goToStep(1)">
|
<li class="step active" data-step="1" onclick="goToStep(1)">
|
||||||
<div class="step-number">1</div>
|
<div class="step-number">1</div>
|
||||||
<!-- <div class="step-number">✓</div> -->
|
<!-- <div class="step-number">✓</div> -->
|
||||||
<div class="step-text">Create Account</div>
|
<div class="step-text">Create Account</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="step" data-step="2" onclick="goToStep(2)">
|
<li class="step" data-step="2" onclick="goToStep(2)">
|
||||||
|
|
@ -116,7 +104,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="email" id="email" class="input-field" placeholder=" " value="<%= email %>" readonly disabled required>
|
<input type="email" id="email" class="input-field" placeholder=" " value="<%= email %>"
|
||||||
|
readonly disabled required>
|
||||||
<label for="email" class="input-label">Email *</label>
|
<label for="email" class="input-label">Email *</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -160,17 +149,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="city" class="input-field" placeholder=" " oninput="validateCity(this)" onkeypress="return preventInvalidCityChars(event)" required>
|
<input type="text" id="city" class="input-field" placeholder=" "
|
||||||
|
oninput="validateCity(this)" onkeypress="return preventInvalidCityChars(event)"
|
||||||
|
required>
|
||||||
<label for="city" class="input-label">City *</label>
|
<label for="city" class="input-label">City *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="city-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="city-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
City name should contain letters and cannot be only numbers or special characters
|
City name should contain letters and cannot be only numbers or special characters
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<!-- <input type="text" id="state" class="input-field" placeholder=" " required> -->
|
<!-- <input type="text" id="state" class="input-field" placeholder=" " required> -->
|
||||||
<select id="state" class="select-field" required>
|
<select id="state" class="select-field" required>
|
||||||
<option value="" disable selected style="display: none"></option>
|
<option value="" disable selected style="display: none"></option>
|
||||||
<!-- States will be populated by JavaScript -->
|
<!-- States will be populated by JavaScript -->
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -180,10 +172,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="zip" class="input-field" placeholder=" " oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)" maxlength="10" required>
|
<input type="text" id="zip" class="input-field" placeholder=" "
|
||||||
|
oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)"
|
||||||
|
maxlength="10" required>
|
||||||
<label for="zip" class="input-label">ZIP *</label>
|
<label for="zip" class="input-label">ZIP *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="zip-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="zip-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Invalid Zip code
|
Invalid Zip code
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -219,17 +214,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="billing-city" class="input-field" placeholder=" " oninput="validateCity(this)" onkeypress="return preventInvalidCityChars(event)" required>
|
<input type="text" id="billing-city" class="input-field" placeholder=" "
|
||||||
|
oninput="validateCity(this)" onkeypress="return preventInvalidCityChars(event)"
|
||||||
|
required>
|
||||||
<label for="billing-city" class="input-label">City *</label>
|
<label for="billing-city" class="input-label">City *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="billing-city-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="billing-city-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
City name should contain letters and cannot be only numbers or special characters
|
City name should contain letters and cannot be only numbers or special characters
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<!-- <input type="text" id="billing-state" class="input-field" placeholder=" " required> -->
|
<!-- <input type="text" id="billing-state" class="input-field" placeholder=" " required> -->
|
||||||
<select id="billing-state" class="select-field" required>
|
<select id="billing-state" class="select-field" required>
|
||||||
<option value="" disable selected style="display: none"></option>
|
<option value="" disable selected style="display: none"></option>
|
||||||
<!-- States will be populated by JavaScript -->
|
<!-- States will be populated by JavaScript -->
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -238,10 +236,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="billing-zip" class="input-field" placeholder=" " oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)" maxlength="10" required>
|
<input type="text" id="billing-zip" class="input-field" placeholder=" "
|
||||||
|
oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)"
|
||||||
|
maxlength="10" required>
|
||||||
<label for="billing-zip" class="input-label">ZIP *</label>
|
<label for="billing-zip" class="input-label">ZIP *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="billing-zip-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="billing-zip-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
ZIP code should contain only numbers
|
ZIP code should contain only numbers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -274,19 +275,26 @@
|
||||||
<form id="signatory-form">
|
<form id="signatory-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="position" class="input-field" placeholder=" " oninput="validatePosition(this)" onkeypress="return preventInvalidPositionChars(event)" required>
|
<input type="text" id="position" class="input-field" placeholder=" "
|
||||||
<label for="position" class="input-label">Position (Owner / CEO / Manager / etc.) *</label>
|
oninput="validatePosition(this)"
|
||||||
|
onkeypress="return preventInvalidPositionChars(event)" required>
|
||||||
|
<label for="position" class="input-label">Position (Owner / CEO / Manager / etc.)
|
||||||
|
*</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="position-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="position-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Position should only contain letters, spaces, and common punctuation
|
Position should only contain letters, spaces, and common punctuation
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="signatory-name" class="input-field" placeholder=" " oninput="validateSignatoryName(this)" onkeypress="return preventNumbers(event)" required>
|
<input type="text" id="signatory-name" class="input-field" placeholder=" "
|
||||||
|
oninput="validateSignatoryName(this)" onkeypress="return preventNumbers(event)"
|
||||||
|
required>
|
||||||
<label for="signatory-name" class="input-label">Name *</label>
|
<label for="signatory-name" class="input-label">Name *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="signatory-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="signatory-name-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Name should not contain numbers
|
Name should not contain numbers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -324,26 +332,33 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="expiry-date" class="input-field" placeholder=" " required>
|
<input type="text" id="expiry-date" class="input-field" placeholder=" "
|
||||||
|
required>
|
||||||
<label for="expiry-date" class="input-label">Expiry Date (MM/YY) *</label>
|
<label for="expiry-date" class="input-label">Expiry Date (MM/YY) *</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="security-code" class="input-field" placeholder=" " oninput="validateCVVVV(this)" nkeypress="return preventInvalidCVVChars(event)" maxlength="4" required>
|
<input type="text" id="security-code" class="input-field" placeholder=" "
|
||||||
|
oninput="validateCVVVV(this)"
|
||||||
|
nkeypress="return preventInvalidCVVChars(event)" maxlength="4" required>
|
||||||
<label for="security-code" class="input-label">Security Code *</label>
|
<label for="security-code" class="input-label">Security Code *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="security-code-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="security-code-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Please enter a valid security code
|
Please enter a valid security code
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="card-zip" class="input-field" placeholder=" " oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)" maxlength="10" required>
|
<input type="text" id="card-zip" class="input-field" placeholder=" "
|
||||||
|
oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)"
|
||||||
|
maxlength="10" required>
|
||||||
<label for="card-zip" class="input-label">ZIP Code *</label>
|
<label for="card-zip" class="input-label">ZIP Code *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="card-zip-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="card-zip-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
ZIP code should contain only numbers
|
ZIP code should contain only numbers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -355,19 +370,25 @@
|
||||||
<div id="bank-details" class="payment-details">
|
<div id="bank-details" class="payment-details">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="bank-name" class="input-field" placeholder=" " oninput="validateBankName(this)" onkeypress="return preventInvalidBankNameChars(event)" required>
|
<input type="text" id="bank-name" class="input-field" placeholder=" "
|
||||||
|
oninput="validateBankName(this)"
|
||||||
|
onkeypress="return preventInvalidBankNameChars(event)" required>
|
||||||
<label for="bank-name" class="input-label">Bank Name *</label>
|
<label for="bank-name" class="input-label">Bank Name *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="bank-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="bank-name-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Bank name should contain letters and cannot be only numbers or special characters
|
Bank name should contain letters and cannot be only numbers or special characters
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="account-number" class="input-field" placeholder=" " oninput="validateAccountNumber(this)" onkeypress="return preventInvalidAccountNumberChars(event)" required>
|
<input type="text" id="account-number" class="input-field" placeholder=" "
|
||||||
|
oninput="validateAccountNumber(this)"
|
||||||
|
onkeypress="return preventInvalidAccountNumberChars(event)" required>
|
||||||
<label for="account-number" class="input-label">Account Number *</label>
|
<label for="account-number" class="input-label">Account Number *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="account-number-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="account-number-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Please enter a valid account number
|
Please enter a valid account number
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -389,10 +410,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input type="text" id="account-holder-name" class="input-field" placeholder=" " oninput="validateAccountHolderName(this)" onkeypress="return preventInvalidAccountHolderChars(event)" required>
|
<input type="text" id="account-holder-name" class="input-field" placeholder=" "
|
||||||
|
oninput="validateAccountHolderName(this)"
|
||||||
|
onkeypress="return preventInvalidAccountHolderChars(event)" required>
|
||||||
<label for="account-holder-name" class="input-label">Account Holder Name *</label>
|
<label for="account-holder-name" class="input-label">Account Holder Name *</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback" id="account-holder-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
<div class="invalid-feedback" id="account-holder-name-error"
|
||||||
|
style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||||
Account holder name should only contain letters and spaces
|
Account holder name should only contain letters and spaces
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -409,27 +433,26 @@
|
||||||
<div class="success-message">
|
<div class="success-message">
|
||||||
<div class="success-icon">✓</div>
|
<div class="success-icon">✓</div>
|
||||||
<h3>Registration Complete!</h3>
|
<h3>Registration Complete!</h3>
|
||||||
<p>Your account has been successfully created. You will receive a confirmation email shortly.</p>
|
<p>Your account has been successfully created. You will receive a confirmation email shortly.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/public/js/va-client-register.js"></script>
|
<script src="/public/js/va-client-register.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function setTheme(theme) {
|
function setTheme(theme) {
|
||||||
document.documentElement.setAttribute('data-theme', theme);
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
localStorage.setItem('theme', theme);
|
localStorage.setItem('theme', theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSystemTheme() {
|
function getSystemTheme() {
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize theme
|
// Initialize theme
|
||||||
const savedTheme = getSystemTheme();
|
const savedTheme = getSystemTheme();
|
||||||
setTheme(savedTheme);
|
setTheme(savedTheme);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,293 +1,124 @@
|
||||||
<!DOCTYPE html>
|
<!-- Mobile Toggle Button -->
|
||||||
<html lang="en">
|
<div class="mobile-toggle" id="toggleBtn">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<head>
|
<!-- Mobile Overlay -->
|
||||||
<meta charset="UTF-8" />
|
<div id="overlay"></div>
|
||||||
<title>Live Screen Feed</title>
|
|
||||||
|
|
||||||
<style>
|
<div class="sidebar" id="sidebar">
|
||||||
* {
|
<h3>Visible Apps</h3>
|
||||||
box-sizing: border-box;
|
<div id="appsList"></div>
|
||||||
margin: 0;
|
</div>
|
||||||
padding: 0;
|
|
||||||
|
<div class="content">
|
||||||
|
<h2 id="selectedAppTitle">Select an app to view</h2>
|
||||||
|
<img id="liveImage" alt="Live Feed" />
|
||||||
|
<div id="placeholder" class="placeholder">No app selected</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script>
|
||||||
|
const vaId = "<%= vaId %>";
|
||||||
|
const clientId = "<%= clientId %>";
|
||||||
|
const projectId = "<%= projectId %>";
|
||||||
|
const token = localStorage.getItem("knoptpq");
|
||||||
|
|
||||||
|
const socket = io({
|
||||||
|
auth: {
|
||||||
|
token: token,
|
||||||
|
userType: "client",
|
||||||
|
projectId: projectId,
|
||||||
|
vaId: vaId
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.emit("register", {
|
||||||
|
role: "client",
|
||||||
|
id: vaId,
|
||||||
|
projectId: "<%= projectId %>",
|
||||||
|
clientId: clientId
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const sidebar = document.getElementById("sidebar");
|
||||||
|
const toggleBtn = document.getElementById("toggleBtn");
|
||||||
|
const overlay = document.getElementById("overlay");
|
||||||
|
|
||||||
|
const appsList = document.getElementById("appsList");
|
||||||
|
const liveImage = document.getElementById("liveImage");
|
||||||
|
const selectedAppTitle = document.getElementById("selectedAppTitle");
|
||||||
|
const placeholder = document.getElementById("placeholder");
|
||||||
|
|
||||||
|
let currentApp = null;
|
||||||
|
|
||||||
|
/* MOBILE SIDEBAR TOGGLE */
|
||||||
|
toggleBtn.onclick = () => {
|
||||||
|
sidebar.classList.add("open");
|
||||||
|
overlay.classList.add("show");
|
||||||
|
};
|
||||||
|
|
||||||
|
overlay.onclick = () => {
|
||||||
|
sidebar.classList.remove("open");
|
||||||
|
overlay.classList.remove("show");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.on("connected", () => {
|
||||||
|
socket.emit("requestVisibleApps", { token });
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Receive visible apps */
|
||||||
|
socket.on("receiveVisibleApps", ({ apps, vaId: senderVaId }) => {
|
||||||
|
if (vaId !== senderVaId) return;
|
||||||
|
|
||||||
|
appsList.innerHTML = "";
|
||||||
|
|
||||||
|
if (!apps?.length) {
|
||||||
|
appsList.innerHTML = "<p style='color:#777;'>No visible apps</p>";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
apps.forEach(app => {
|
||||||
height: 100vh;
|
const div = document.createElement("div");
|
||||||
display: flex;
|
div.classList.add("app");
|
||||||
background: #0f0f0f;
|
div.textContent = app.name || "Unnamed App";
|
||||||
color: white;
|
|
||||||
font-family: "Inter", sans-serif;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SIDEBAR */
|
div.onclick = () => {
|
||||||
.sidebar {
|
document.querySelectorAll(".app").forEach(el => el.classList.remove("active"));
|
||||||
width: 280px;
|
div.classList.add("active");
|
||||||
background: #181818;
|
|
||||||
border-right: 1px solid #2b2b2b;
|
|
||||||
padding: 15px;
|
|
||||||
overflow-y: auto;
|
|
||||||
transition: transform 0.35s ease;
|
|
||||||
z-index: 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3 {
|
currentApp = app.name;
|
||||||
margin-bottom: 15px;
|
selectedAppTitle.textContent = app.name;
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app {
|
|
||||||
background: #252525;
|
|
||||||
border: 1px solid #333;
|
|
||||||
padding: 12px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app:hover {
|
|
||||||
background: #333;
|
|
||||||
transform: translateX(3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.app.active {
|
|
||||||
background: #6c63ff;
|
|
||||||
border-color: #6c63ff;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 0 10px rgba(108, 99, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MAIN CONTENT */
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
padding: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#selectedAppTitle {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#liveImage {
|
|
||||||
width: 90%;
|
|
||||||
max-width: 900px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 2px solid #444;
|
|
||||||
object-fit: contain;
|
|
||||||
background: black;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
font-size: 16px;
|
|
||||||
opacity: 0.8;
|
|
||||||
color: #bbb;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MOBILE RESPONSIVE */
|
|
||||||
.mobile-toggle {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
top: 15px;
|
|
||||||
left: 15px;
|
|
||||||
background: #6C63FF;
|
|
||||||
padding: 14px;
|
|
||||||
border-radius: 8px;
|
|
||||||
z-index: 9999;
|
|
||||||
/* VERY HIGH: always visible */
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-toggle span {
|
|
||||||
display: block;
|
|
||||||
width: 26px;
|
|
||||||
height: 3px;
|
|
||||||
background: #fff;
|
|
||||||
margin: 5px 0;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar Hidden on Mobile */
|
|
||||||
@media (max-width: 780px) {
|
|
||||||
body {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 100%;
|
|
||||||
transform: translateX(-100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.open {
|
|
||||||
transform: translateX(0%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-toggle {
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding-top: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark overlay when sidebar opens */
|
|
||||||
#overlay {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.45);
|
|
||||||
display: none;
|
|
||||||
z-index: 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
#overlay.show {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<!-- Mobile Toggle Button -->
|
|
||||||
<div class="mobile-toggle" id="toggleBtn">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mobile Overlay -->
|
|
||||||
<div id="overlay"></div>
|
|
||||||
|
|
||||||
<div class="sidebar" id="sidebar">
|
|
||||||
<h3>Visible Apps</h3>
|
|
||||||
<div id="appsList"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<h2 id="selectedAppTitle">Select an app to view</h2>
|
|
||||||
<img id="liveImage" alt="Live Feed" />
|
|
||||||
<div id="placeholder" class="placeholder">No app selected</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
|
||||||
<script>
|
|
||||||
const vaId = "<%= vaId %>";
|
|
||||||
const clientId = "<%= clientId %>";
|
|
||||||
const projectId = "<%= projectId %>";
|
|
||||||
const token = localStorage.getItem("knoptpq");
|
|
||||||
|
|
||||||
const socket = io({
|
|
||||||
auth: {
|
|
||||||
token: token,
|
|
||||||
userType: "client",
|
|
||||||
projectId: projectId,
|
|
||||||
vaId: vaId
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.emit("register", {
|
|
||||||
role: "client",
|
|
||||||
id: vaId,
|
|
||||||
projectId: "<%= projectId %>",
|
|
||||||
clientId: clientId
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const sidebar = document.getElementById("sidebar");
|
|
||||||
const toggleBtn = document.getElementById("toggleBtn");
|
|
||||||
const overlay = document.getElementById("overlay");
|
|
||||||
|
|
||||||
const appsList = document.getElementById("appsList");
|
|
||||||
const liveImage = document.getElementById("liveImage");
|
|
||||||
const selectedAppTitle = document.getElementById("selectedAppTitle");
|
|
||||||
const placeholder = document.getElementById("placeholder");
|
|
||||||
|
|
||||||
let currentApp = null;
|
|
||||||
|
|
||||||
/* MOBILE SIDEBAR TOGGLE */
|
|
||||||
toggleBtn.onclick = () => {
|
|
||||||
sidebar.classList.add("open");
|
|
||||||
overlay.classList.add("show");
|
|
||||||
};
|
|
||||||
|
|
||||||
overlay.onclick = () => {
|
|
||||||
sidebar.classList.remove("open");
|
|
||||||
overlay.classList.remove("show");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.on("connected", () => {
|
|
||||||
socket.emit("requestVisibleApps", { token });
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Receive visible apps */
|
|
||||||
socket.on("receiveVisibleApps", ({ apps, vaId: senderVaId }) => {
|
|
||||||
if (vaId !== senderVaId) return;
|
|
||||||
|
|
||||||
appsList.innerHTML = "";
|
|
||||||
|
|
||||||
if (!apps?.length) {
|
|
||||||
appsList.innerHTML = "<p style='color:#777;'>No visible apps</p>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
apps.forEach(app => {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.classList.add("app");
|
|
||||||
div.textContent = app.name || "Unnamed App";
|
|
||||||
|
|
||||||
div.onclick = () => {
|
|
||||||
document.querySelectorAll(".app").forEach(el => el.classList.remove("active"));
|
|
||||||
div.classList.add("active");
|
|
||||||
|
|
||||||
currentApp = app.name;
|
|
||||||
selectedAppTitle.textContent = app.name;
|
|
||||||
|
|
||||||
placeholder.style.display = "none";
|
|
||||||
liveImage.style.display = "none";
|
|
||||||
|
|
||||||
socket.emit("selectApp", { appTitle: app.name, clientId, vaId, projectId });
|
|
||||||
|
|
||||||
// Close sidebar on mobile
|
|
||||||
sidebar.classList.remove("open");
|
|
||||||
overlay.classList.remove("show");
|
|
||||||
};
|
|
||||||
|
|
||||||
appsList.appendChild(div);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("authError", () => {
|
|
||||||
socket.disconnect();
|
|
||||||
localStorage.removeItem("knoptpq");
|
|
||||||
window.location.href = "/";
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Receive images */
|
|
||||||
socket.on("receiveImage", (base64, title) => {
|
|
||||||
if (currentApp && title === currentApp) {
|
|
||||||
liveImage.src = base64;
|
|
||||||
liveImage.style.display = "block";
|
|
||||||
placeholder.style.display = "none";
|
placeholder.style.display = "none";
|
||||||
}
|
liveImage.style.display = "none";
|
||||||
|
|
||||||
|
socket.emit("selectApp", { appTitle: app.name, clientId, vaId, projectId });
|
||||||
|
|
||||||
|
// Close sidebar on mobile
|
||||||
|
sidebar.classList.remove("open");
|
||||||
|
overlay.classList.remove("show");
|
||||||
|
};
|
||||||
|
|
||||||
|
appsList.appendChild(div);
|
||||||
});
|
});
|
||||||
</script>
|
});
|
||||||
|
|
||||||
</body>
|
socket.on("authError", () => {
|
||||||
|
socket.disconnect();
|
||||||
|
localStorage.removeItem("knoptpq");
|
||||||
|
window.location.href = "/client/login";
|
||||||
|
});
|
||||||
|
|
||||||
</html>
|
/* Receive images */
|
||||||
|
socket.on("receiveImage", (base64, title) => {
|
||||||
|
if (currentApp && title === currentApp) {
|
||||||
|
liveImage.src = base64;
|
||||||
|
liveImage.style.display = "block";
|
||||||
|
placeholder.style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -1,25 +1,4 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Virtual Assistant Registration</title>
|
|
||||||
|
|
||||||
<link href="/public/css/va-register.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<style>
|
|
||||||
select option:disabled {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
background-color: white;
|
|
||||||
color: #2d2d2d;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- Sidebar for desktop -->
|
<!-- Sidebar for desktop -->
|
||||||
|
|
@ -346,5 +325,4 @@
|
||||||
const savedTheme = getSystemTheme();
|
const savedTheme = getSystemTheme();
|
||||||
setTheme(savedTheme);
|
setTheme(savedTheme);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue