mirror of
https://gitlab.com/sarlink/kyc.git
synced 2025-02-22 09:12:05 +00:00
[feat] Form input page and validation
This commit is contained in:
parent
d656154c70
commit
dad311cb2d
6
.env.sample
Normal file
6
.env.sample
Normal file
@ -0,0 +1,6 @@
|
||||
REGISTRATION_OPEN=true
|
||||
BRANDING_TITLE=
|
||||
BRANDING_LOGO=
|
||||
BANK_NAME=
|
||||
ACCOUNT_NAME=
|
||||
ACCOUNT_NUMBER=
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -128,3 +128,5 @@ dist
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
sarlink.jpg
|
1511
package-lock.json
generated
1511
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,9 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"build": "tailwindcss -o ./src/public/styles.css",
|
||||
"build:watch": "tailwindcss -o ./src/public/styles.css --watch",
|
||||
"start": "npm run build && node server.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@ -12,7 +14,12 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.6.7",
|
||||
"dotenv": "^16.4.1",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "^4.18.2",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"daisyui": "^4.6.2",
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
}
|
||||
|
47
server.js
47
server.js
@ -1,22 +1,61 @@
|
||||
require("dotenv").config()
|
||||
|
||||
const registrationOpen = process.env.REGISTRATION_OPEN === "true"
|
||||
let registrationOpen, branding;
|
||||
|
||||
function loadEnv() {
|
||||
registrationOpen = process.env.REGISTRATION_OPEN === "true"
|
||||
branding = {
|
||||
title: process.env.BRANDING_TITLE || "SAR Link",
|
||||
logo: process.env.BRANDING_LOGO || "/public/logo.png",
|
||||
bankDetails: {
|
||||
name: process.env.BANK_NAME || "Bank Name",
|
||||
accountNumber: process.env.ACCOUNT_NUMBER || "1234567890",
|
||||
accountName: process.env.ACCOUNT_NAME || "Account Name",
|
||||
},
|
||||
}
|
||||
}
|
||||
loadEnv()
|
||||
|
||||
// watch for changes to .env file
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
fs.watchFile(path.join(__dirname, ".env"), (curr, prev) => {
|
||||
delete require.cache[require.resolve("dotenv")]
|
||||
require("dotenv").config({ override: true })
|
||||
loadEnv()
|
||||
console.log(`Registration is now ${registrationOpen ? "open" : "closed"}`)
|
||||
})
|
||||
|
||||
// initialize express
|
||||
const express = require("express")
|
||||
const app = express()
|
||||
app.set("view engine", "ejs")
|
||||
app.set("views", "./src/views")
|
||||
app.use("/public", express.static("./src/public"))
|
||||
app.use(express.json())
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
|
||||
// add multer
|
||||
const multer = require("multer")
|
||||
const upload = multer()
|
||||
app.use(upload.single("receipt"))
|
||||
// app.use(upload.single("receipt"))
|
||||
|
||||
// routes
|
||||
app.get("/", (req, res) => {
|
||||
res.render(registrationOpen ? "index" : "closed")
|
||||
res.render(registrationOpen ? "index" : "closed", { branding })
|
||||
});
|
||||
|
||||
app.post("/register", upload.single("receipt"), (req, res) => {
|
||||
if (!registrationOpen) {
|
||||
res.render("closed")
|
||||
return
|
||||
}
|
||||
console.log(req.body)
|
||||
console.log(req.file)
|
||||
res.render("success", { branding })
|
||||
})
|
||||
|
||||
const port = process.env.PORT || 4818;
|
||||
app.listen(port, () => {
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
console.log(`Server is running on port ${port}`);
|
||||
});
|
2209
src/public/styles.css
Normal file
2209
src/public/styles.css
Normal file
File diff suppressed because it is too large
Load Diff
3
src/styles/input.css
Normal file
3
src/styles/input.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
20
src/views/closed.ejs
Normal file
20
src/views/closed.ejs
Normal file
@ -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><%- branding.title %> - KYC</title>
|
||||
<link rel="shortcut icon" href="<%- branding.logo %>" type="image/x-icon" />
|
||||
<link rel="stylesheet" href="/public/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="flex h-[100vh] w-[100vw] items-center justify-center">
|
||||
<div class="card w-96 bg-base-100 shadow-xl image-full">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Registration Closed</h2>
|
||||
<p>Adding new devices is currently disabled.</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
337
src/views/index.ejs
Normal file
337
src/views/index.ejs
Normal file
@ -0,0 +1,337 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%- branding.title %> - KYC</title>
|
||||
<link rel="shortcut icon" href="<%- branding.logo %>" type="image/x-icon" />
|
||||
<link rel="stylesheet" href="/public/styles.css" />
|
||||
<script>
|
||||
const blacklistedChars = /\s/g;
|
||||
|
||||
function fillDeviceName() {
|
||||
let deviceType = "";
|
||||
const macAddress = document.getElementById("mac_address").value;
|
||||
// now we find out which format it is
|
||||
// 35:df:61 = customerfirstname-android
|
||||
// 3A-DF-61 = customerfirstname-laptop
|
||||
// 3A:DF:61 = customerfirstname-iphone
|
||||
const macAddressRegex = new RegExp(
|
||||
/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/
|
||||
);
|
||||
if (macAddress === macAddress.toLowerCase()) {
|
||||
deviceType = "android";
|
||||
} else if (macAddress === macAddress.replace(/:/g, "-")) {
|
||||
deviceType = "laptop";
|
||||
} else if (macAddress === macAddress.toUpperCase()) {
|
||||
deviceType = "iphone";
|
||||
} else {
|
||||
deviceType = "phone";
|
||||
}
|
||||
|
||||
const fullName = document.getElementById("customer_name").value;
|
||||
const firstName = fullName.split(" ")[0];
|
||||
const deviceName = `${firstName}-${deviceType}`.toLowerCase();
|
||||
|
||||
const deviceNameInput = document.getElementById("device_name");
|
||||
if (!deviceNameInput.value) deviceNameInput.value = deviceName;
|
||||
}
|
||||
|
||||
function validateMACAddress() {
|
||||
var macAddress = document.getElementById("mac_address");
|
||||
var macAddressRegex = new RegExp(
|
||||
/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/
|
||||
);
|
||||
const macAddressValue = macAddress.value;
|
||||
macAddress.setCustomValidity("");
|
||||
macAddress.classList.remove("input-error");
|
||||
macAddress.classList.remove("input-success");
|
||||
if (!macAddressRegex.test(macAddressValue)) {
|
||||
macAddress.classList.add("input-error");
|
||||
macAddress.setCustomValidity("Invalid MAC Address");
|
||||
macAddress.reportValidity();
|
||||
return false;
|
||||
}
|
||||
macAddress.classList.add("input-success");
|
||||
macAddress.reportValidity();
|
||||
fillDeviceName();
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
const macAddressValid = validateMACAddress();
|
||||
const customerName = document.getElementById("customer_name");
|
||||
customerName.classList.remove("input-error");
|
||||
// customerName.setCustomValidity("");
|
||||
const deviceName = document.getElementById("device_name");
|
||||
deviceName.classList.remove("input-error");
|
||||
// deviceName.setCustomValidity("");
|
||||
const transferReceipt = document.getElementById("transfer_receipt");
|
||||
transferReceipt.classList.remove("input-error");
|
||||
// transferReceipt.setCustomValidity("");
|
||||
|
||||
if (customerName.value === "") {
|
||||
customerName.classList.add("input-error");
|
||||
// customerName.setCustomValidity("Customer Name is required");
|
||||
// customerName.reportValidity();
|
||||
}
|
||||
|
||||
if (deviceName.value === "") {
|
||||
deviceName.classList.add("input-error");
|
||||
// deviceName.setCustomValidity("Device Name is required");
|
||||
// deviceName.reportValidity();
|
||||
}
|
||||
|
||||
if (transferReceipt.files.length === 0) {
|
||||
transferReceipt.classList.add("input-error");
|
||||
// transferReceipt.setCustomValidity("Transfer Receipt is required");
|
||||
// transferReceipt.reportValidity();
|
||||
}
|
||||
|
||||
return (
|
||||
macAddressValid &&
|
||||
customerName.value !== "" &&
|
||||
deviceName.value !== "" &&
|
||||
transferReceipt.files.length > 0
|
||||
);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="navbar bg-base-300">
|
||||
<button class="btn btn-ghost text-xl">
|
||||
<img class="w-10 rounded" src="<%- branding.logo %>" />
|
||||
<%- branding.title %>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<form class="flex justify-center" action="/register" method="post">
|
||||
<div class="w-[80vw]">
|
||||
<div class="join-horizontal mt-8">
|
||||
<div class="join-vertical justify-center">
|
||||
<h1 class="text-3xl font-bold">KYC</h1>
|
||||
<p class="text-base">
|
||||
Please fill in the form to complete your KYC
|
||||
</p>
|
||||
</div>
|
||||
<br />
|
||||
<div class="join-vertical">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-bold text-lg text-base-content"
|
||||
>Customer Name</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="customer_name"
|
||||
name="customer_name"
|
||||
placeholder="John Doe"
|
||||
class="input input-bordered"
|
||||
onchange="validateForm()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="join-vertical">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-bold text-lg text-base-content"
|
||||
>MAC Address</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="mac_address"
|
||||
id="mac_address"
|
||||
placeholder="00-00-00-00-00-00"
|
||||
class="input input-bordered"
|
||||
onchange="validateForm()"
|
||||
oninput="validateMACAddress()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="collapse bg-base-200">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
How do I find my MAC Address?
|
||||
</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
<p>
|
||||
A MAC (Media Access Control) address is a unique identifier
|
||||
assigned to a device's network. It is used to identify the
|
||||
device on a network, helping to differentiate devices on a
|
||||
network.
|
||||
</p>
|
||||
<div class="collapse collapse-arrow bg-base-200">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="my-accordion-1"
|
||||
checked="checked"
|
||||
/>
|
||||
<div class="collapse-title text-xl font-medium">iPhone</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
<div class="overflow-x-auto text-sm breadcrumbs">
|
||||
<ul>
|
||||
<li>Settings</li>
|
||||
<li>General</li>
|
||||
<li>About</li>
|
||||
<li>Wi-Fi Address</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse collapse-arrow bg-base-200">
|
||||
<input type="checkbox" name="my-accordion-1" />
|
||||
<div class="collapse-title text-xl font-medium">Redmi</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
<div class="overflow-x-auto text-sm breadcrumbs">
|
||||
<ul>
|
||||
<li>Settings</li>
|
||||
<li>About</li>
|
||||
<li>Wi-Fi MAC Address</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse collapse-arrow bg-base-200">
|
||||
<input type="checkbox" name="my-accordion-1" />
|
||||
<div class="collapse-title text-xl font-medium">Samsung</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
<div class="overflow-x-auto text-sm breadcrumbs">
|
||||
<ul>
|
||||
<li>Settings</li>
|
||||
<li>About phone</li>
|
||||
<li>Status Information</li>
|
||||
<li>Wi-Fi MAC Address</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse collapse-arrow bg-base-200">
|
||||
<input type="checkbox" name="my-accordion-1" />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
Windows Laptop
|
||||
</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
<div class="overflow-x-auto text-sm breadcrumbs">
|
||||
<ul>
|
||||
<li>Settings</li>
|
||||
<li>Network and Internet</li>
|
||||
<li>Wi-Fi</li>
|
||||
<li>Hardware Properties</li>
|
||||
<li>Physical address (MAC):</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse collapse-arrow bg-base-200">
|
||||
<input type="checkbox" name="my-accordion-1" />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
Other Device
|
||||
</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
Please contact <%- branding.title %> for assistance.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="join-vertical">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-bold text-lg text-base-content"
|
||||
>Device Name</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="device_name"
|
||||
name="device_name"
|
||||
placeholder="iphone-12"
|
||||
class="input input-bordered"
|
||||
onchange="validateForm()"
|
||||
oninput="this.value =
|
||||
this.value.toLowerCase().replace(blacklistedChars, '-');"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div class="collapse bg-base-200">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
Transfer Details
|
||||
</div>
|
||||
<div class="collapse-content overflow-x-auto">
|
||||
<p>
|
||||
Please transfer MVR 100 to the following account and upload
|
||||
the receipt below.
|
||||
</p>
|
||||
<br />
|
||||
<div class="overflow-x-auto text-sm">
|
||||
<ul>
|
||||
<li>Bank: <%- branding.bankDetails.name %></li>
|
||||
<li>
|
||||
Account Number: <%- branding.bankDetails.accountNumber %>
|
||||
</li>
|
||||
<li>
|
||||
Account Name: <%- branding.bankDetails.accountName %>
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-block btn-info"
|
||||
onclick="navigator.clipboard.writeText('<%- branding.bankDetails.accountNumber %>')"
|
||||
>
|
||||
Copy Account Number
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="collapse bg-base-200">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
How to transfer from BML to MIB?
|
||||
</div>
|
||||
<div class="collapse-content"></div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="join-vertical">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-bold text-lg text-base-content"
|
||||
>Transfer Receipt</span
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
type="file"
|
||||
id="transfer_receipt"
|
||||
name="transfer_receipt"
|
||||
class="file-input file-input-bordered w-full max-w-xs"
|
||||
accept="image/*"
|
||||
onchange="validateForm()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button
|
||||
class="btn btn-info btn-block"
|
||||
onclick="if (!validateForm()) event.preventDefault()"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<footer>
|
||||
<div style="height: 10em"></div>
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
25
src/views/success.ejs
Normal file
25
src/views/success.ejs
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%- branding.title %> - KYC</title>
|
||||
<link rel="shortcut icon" href="<%- branding.logo %>" type="image/x-icon" />
|
||||
<link rel="stylesheet" href="/public/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="flex h-[100vh] w-[100vw] items-center justify-center">
|
||||
<div
|
||||
class="card w-96 bg-base-100 shadow-xl bg-primary text-primary-content"
|
||||
>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Success</h2>
|
||||
<p>
|
||||
Your request has been sent to the owner! Please wait until access is
|
||||
granted.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
11
tailwind.config.js
Normal file
11
tailwind.config.js
Normal file
@ -0,0 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{html,js,ejs}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require("daisyui"),
|
||||
],
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user