725 lines
22 KiB
HTML
725 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Brankas-Q</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: #f5f5f5;
|
|
}
|
|
|
|
#stream {
|
|
width: 320px;
|
|
height: 240px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.navbar {
|
|
background-color: white;
|
|
padding: 1rem 2rem;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.navbar h1 {
|
|
margin: 0;
|
|
color: #2f6aa3;
|
|
}
|
|
|
|
.container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
padding: 2rem;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.left-panel,
|
|
.right-panel {
|
|
background: white;
|
|
padding: 1rem;
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.left-panel {
|
|
flex: 1;
|
|
min-width: 280px;
|
|
}
|
|
|
|
.right-panel {
|
|
flex: 2;
|
|
min-width: 300px;
|
|
}
|
|
|
|
.image-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 240px;
|
|
aspect-ratio: 4 / 3;
|
|
background-color: #e0e0e0;
|
|
margin-bottom: 1rem;
|
|
overflow: hidden;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.stream-row {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.stream-row button {
|
|
flex: 1;
|
|
}
|
|
|
|
button {
|
|
margin: 0.25rem 0;
|
|
padding: 0.5rem 1rem;
|
|
border: none;
|
|
border-radius: 5px;
|
|
color: white;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.stream-btn {
|
|
background-color: #b52b2b;
|
|
}
|
|
|
|
.detect-btn {
|
|
background-color: #2e8b57;
|
|
}
|
|
|
|
.recognise-btn {
|
|
background-color: #4f2e8b;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.delete-btn {
|
|
background-color: #6a2626;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.status-btn {
|
|
background-color: #2d7f9c;
|
|
padding: 1rem 1rem;
|
|
}
|
|
|
|
.header-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.header-row h2 {
|
|
margin: 0;
|
|
}
|
|
|
|
.add-btn {
|
|
background-color: #2e8b57;
|
|
padding: 0.5rem 1rem;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
width: 120px;
|
|
text-align: center;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
table th,
|
|
table td {
|
|
border-bottom: 1px solid #ddd;
|
|
padding: 0.75rem;
|
|
text-align: left;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.action-icons {
|
|
cursor: pointer;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.right-panel.form-panel h2 {
|
|
margin-top: 0;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.right-panel.form-panel p {
|
|
margin-top: 0;
|
|
margin-bottom: 1rem;
|
|
color: #444;
|
|
}
|
|
|
|
.form-panel {
|
|
display: none;
|
|
}
|
|
|
|
.form-panel.active {
|
|
display: block;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.form-group input,
|
|
.form-group select {
|
|
width: 100%;
|
|
padding: 0.4rem;
|
|
margin-top: 0.25rem;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.form-buttons {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
justify-content: space-between;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.required {
|
|
color: red;
|
|
}
|
|
|
|
.cancel-btn {
|
|
background-color: #e0e0e0;
|
|
flex: 1;
|
|
color: #2e2e2e;
|
|
}
|
|
|
|
.enroll-btn {
|
|
background-color: #2e8b57;
|
|
flex: 1;
|
|
}
|
|
|
|
.status-flash-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
margin-bottom: 0.5rem;
|
|
gap: 10px;
|
|
}
|
|
|
|
.status-btn {
|
|
flex: 9;
|
|
padding: 10px;
|
|
}
|
|
|
|
.flash-btn {
|
|
flex: 1;
|
|
padding: 10px;
|
|
background-color: #e0e0e0;
|
|
border: none;
|
|
cursor: pointer;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
flex-direction: column;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.form-buttons {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.header-row {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.stream-row {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="navbar">
|
|
<img src="https://i.imghippo.com/files/lw7907Ks.png" alt="" border="0" style="height: 35px;">
|
|
</div>
|
|
|
|
<div class="container">
|
|
<div class="left-panel">
|
|
<div class="image-container" id="stream-container"> <img id="stream" src=""> </div>
|
|
<div class="status-flash-row">
|
|
<button id="status-display" class="status-btn">
|
|
<span id="current-status"></span>
|
|
</button>
|
|
<button id="flash-button" class="flash-btn">💡</button>
|
|
</div>
|
|
<div class="stream-row">
|
|
<button class="stream-btn" id="button-stream">STREAM CAMERA</button>
|
|
<button class="detect-btn" id="button-detect">DETECT FACES</button>
|
|
</div>
|
|
<button class="recognise-btn" id="button-recognise">RECOGNIZE</button>
|
|
<button class="delete-btn" id="button-delete-all">DELETE ALL FACES</button>
|
|
<div id="face-list" style="margin-top: 10px; color: #333;"></div>
|
|
</div>
|
|
|
|
<div class="right-panel" id="user-panel">
|
|
<div class="header-row">
|
|
<h2>Captured Faces</h2>
|
|
<button class="add-btn" onclick="showForm()">Add User</button>
|
|
</div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Gender</th>
|
|
<th>Registered</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="userTable">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="right-panel form-panel" id="form-panel">
|
|
<h2>New User</h2>
|
|
<p>Setelah mengisi formulir, arahkan wajah Anda ke kamera lalu klik "Enroll Face"</p>
|
|
<div class="form-group">
|
|
<label>Name <span class="required">*</span></label>
|
|
<input id="name" type="text" placeholder="Enter your name" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Email <span class="required">*</span></label>
|
|
<input id="email" type="email" placeholder="Enter your email" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Age <span class="required">*</span></label>
|
|
<input id="age" type="number" placeholder="Enter your age" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Gender <span class="required">*</span></label>
|
|
<select id="gender" required>
|
|
<option value="">Choose an option</option>
|
|
<option value="Male">Male</option>
|
|
<option value="Female">Female</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Phone <span class="required">*</span></label>
|
|
<input id="phone" type="tel" placeholder="xxxx xxxx xxxx" required>
|
|
</div>
|
|
<div class="form-buttons">
|
|
<button class="cancel-btn" onclick="cancelForm()">Cancel</button>
|
|
<button class="enroll-btn" id="button-capture">Enroll Face</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://www.gstatic.com/firebasejs/9.22.2/firebase-app-compat.js"></script>
|
|
<script src="https://www.gstatic.com/firebasejs/9.22.2/firebase-firestore-compat.js"></script>
|
|
<script>
|
|
const firebaseConfig = {
|
|
apiKey: "AIzaSyDAulqgtU760t6_Ki8gBbvPmsqumgFeYco",
|
|
authDomain: "brankas-q1.firebaseapp.com",
|
|
projectId: "brankas-q1",
|
|
storageBucket: "brankas-q1.appspot.com",
|
|
messagingSenderId: "194299707390",
|
|
appId: "1:194299707390:web:da93f6642093d5791ecaaf",
|
|
measurementId: "G-QJ2SKQZN80"
|
|
};
|
|
firebase.initializeApp(firebaseConfig);
|
|
const db = firebase.firestore();
|
|
let isFirstUser = false;
|
|
db.collection("users").onSnapshot((snapshot) => {
|
|
const table = document.getElementById("userTable");
|
|
table.innerHTML = "";
|
|
isFirstUser = snapshot.empty;
|
|
|
|
// 👉 Cek jumlah user
|
|
const userCount = snapshot.size;
|
|
|
|
// 👉 Atur tombol Add User
|
|
const addButton = document.querySelector(".add-btn");
|
|
if (userCount >= 2) {
|
|
addButton.disabled = true;
|
|
addButton.style.opacity = 0.5;
|
|
addButton.style.cursor = "not-allowed";
|
|
addButton.title = "Maksimal 2 pengguna terdaftar.";
|
|
} else {
|
|
addButton.disabled = false;
|
|
addButton.style.opacity = 1;
|
|
addButton.style.cursor = "pointer";
|
|
addButton.title = "";
|
|
}
|
|
|
|
snapshot.forEach((doc) => {
|
|
const user = doc.data();
|
|
const row = document.createElement("tr");
|
|
row.innerHTML = `
|
|
<td>${user.name}</td>
|
|
<td>${user.gender}</td>
|
|
<td>${user.registered}</td>
|
|
<td>
|
|
<span class="action-icons" onclick="editUser('${doc.id}')">📝</span>
|
|
<span class="action-icons" onclick="deleteUser('${doc.id}')">🚫</span>
|
|
</td>
|
|
`;
|
|
table.appendChild(row);
|
|
});
|
|
});
|
|
|
|
|
|
let waitingForPIN = false;
|
|
let pendingUserData = null;
|
|
|
|
|
|
const view = document.getElementById("stream");
|
|
var baseHost = document.location.origin;
|
|
const WS_URL = "ws://" + window.location.hostname + ":82";
|
|
const ws = new WebSocket(WS_URL);
|
|
|
|
ws.onopen = () => {
|
|
console.log("WebSocket connected to", WS_URL);
|
|
document.getElementById("current-status").textContent = "CONNECTED";
|
|
ws.send("getfacelist");
|
|
};
|
|
ws.onclose = () => {
|
|
console.log("WebSocket closed");
|
|
document.getElementById("current-status").textContent = "DISCONNECTED";
|
|
};
|
|
ws.onerror = (err) => {
|
|
console.error("WebSocket error:", err);
|
|
document.getElementById("current-status").textContent = "CONNECTION ERROR";
|
|
};
|
|
window.ws = ws;
|
|
|
|
let justSetPIN = false;
|
|
ws.onmessage = async (event) => {
|
|
let message;
|
|
|
|
if (typeof event.data === "string") {
|
|
message = event.data;
|
|
} else if (event.data instanceof Blob) {
|
|
const urlObject = URL.createObjectURL(event.data);
|
|
view.src = urlObject;
|
|
return;
|
|
}
|
|
|
|
if (message.startsWith("listface:")) {
|
|
const recognizedName = message.substring(9);
|
|
document.getElementById("current-status").textContent = `${recognizedName} face recognized`;
|
|
} else if (message === "capture:done") {
|
|
if (isFirstUser && pendingUserData) {
|
|
try {
|
|
await db.collection("users").add(pendingUserData);
|
|
console.log("Data user pertama disimpan ke Firebase.");
|
|
pendingUserData = null;
|
|
isFirstUser = false;
|
|
setTimeout(() => location.reload(), 1000);
|
|
} catch (err) {
|
|
console.error("Gagal simpan data pertama:", err);
|
|
alert("Gagal menyimpan data ke Firebase!");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// User kedua dan seterusnya: tunggu getpin, lalu baru simpan user
|
|
if (pendingUserData) {
|
|
ws.send("getpin");
|
|
waitingForPIN = true;
|
|
}
|
|
|
|
} else if (message === "neednewpin") {
|
|
openPINModal();
|
|
} else if (message.startsWith("pin:") && waitingForPIN) {
|
|
waitingForPIN = false;
|
|
const pin = message.substring(4);
|
|
alert("PIN brankas saat ini: " + pin);
|
|
|
|
if (!pendingUserData) {
|
|
console.warn("PIN diterima tapi tidak ada data user tertunda.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await db.collection("users").add(pendingUserData);
|
|
console.log("Data user disimpan ke Firebase setelah menerima PIN.");
|
|
} catch (err) {
|
|
console.error("Gagal simpan ke Firebase setelah PIN:", err);
|
|
alert("Gagal menyimpan data ke Firebase!");
|
|
}
|
|
pendingUserData = null;
|
|
|
|
if (justSetPIN) {
|
|
justSetPIN = false;
|
|
return;
|
|
}
|
|
|
|
setTimeout(() => location.reload(), 1000);
|
|
|
|
} else if (message === "resetpin") {
|
|
const newPIN = prompt("Semua wajah telah dihapus. Masukkan PIN baru untuk brankas:");
|
|
if (newPIN && newPIN.length === 6 && /^\d+$/.test(newPIN)) {
|
|
ws.send("setpin:" + newPIN);
|
|
alert("PIN baru berhasil disimpan.");
|
|
} else {
|
|
alert("PIN tidak valid. Harus 6 digit angka.");
|
|
}
|
|
} else if (message.startsWith("facelist:")) {
|
|
const names = message.substring(9).split(",").filter(n => n.trim());
|
|
const faceListDiv = document.getElementById("face-list");
|
|
if (names.length === 0) {
|
|
faceListDiv.innerHTML = "<strong>Tidak ada wajah terdaftar.</strong>";
|
|
} else {
|
|
faceListDiv.innerHTML = "<strong>Wajah Terdaftar:</strong><ul>" +
|
|
names.map(n => `<li>${n}</li>`).join("") + "</ul>";
|
|
}
|
|
} else {
|
|
document.getElementById("current-status").textContent = message;
|
|
}
|
|
};
|
|
|
|
|
|
const streamButton = document.getElementById("button-stream");
|
|
const detectButton = document.getElementById("button-detect");
|
|
const captureButton = document.getElementById("button-capture");
|
|
const recogniseButton = document.getElementById("button-recognise");
|
|
const deleteAllButton = document.getElementById("button-delete-all");
|
|
|
|
a = new AudioContext();
|
|
function alertSound(w, x, y) {
|
|
v = a.createOscillator();
|
|
u = a.createGain();
|
|
v.connect(u);
|
|
v.frequency.value = x;
|
|
v.type = "square";
|
|
u.connect(a.destination);
|
|
u.gain.value = w * 0.01;
|
|
v.start(a.currentTime);
|
|
v.stop(a.currentTime + y * 0.001);
|
|
}
|
|
|
|
let editingUserId = null;
|
|
captureButton.onclick = async () => {
|
|
if (waitingForPIN) {
|
|
// alert("Sedang menunggu respons dari ESP32-CAM...");
|
|
return;
|
|
}
|
|
const name = document.getElementById("name").value;
|
|
const email = document.getElementById("email").value;
|
|
const age = document.getElementById("age").value;
|
|
const gender = document.getElementById("gender").value;
|
|
const phone = document.getElementById("phone").value;
|
|
|
|
if (!name || !email || !age || !gender || !phone) {
|
|
alert("Semua field wajib diisi!");
|
|
return;
|
|
}
|
|
const timestamp = new Date().toLocaleString("id-ID", {
|
|
day: "2-digit", month: "2-digit", year: "numeric",
|
|
hour: "2-digit", minute: "2-digit", hour12: false
|
|
});
|
|
const userData = { name, email, age: Number(age), gender, phone, registered: timestamp };
|
|
|
|
if (isFirstUser) {
|
|
pendingUserData = userData;
|
|
openPINModal();
|
|
} else {
|
|
pendingUserData = userData;
|
|
waitingForPIN = true;
|
|
ws.send("capture:" + name);
|
|
// alert("Tunggu proses perekaman wajah dari ESP32-CAM...");
|
|
}
|
|
|
|
};
|
|
streamButton.onclick = () => {
|
|
ws.send("stream");
|
|
};
|
|
detectButton.onclick = () => {
|
|
ws.send("detect");
|
|
};
|
|
recogniseButton.onclick = () => {
|
|
ws.send("recognise");
|
|
};
|
|
deleteAllButton.onclick = () => {
|
|
if (confirm("Yakin ingin menghapus semua wajah dari ESP32-CAM?")) {
|
|
ws.send("delete:allfaces");
|
|
}
|
|
};
|
|
|
|
let flashOn = false;
|
|
document.getElementById("flash-button").addEventListener("click", function () {
|
|
flashOn = !flashOn;
|
|
const message = flashOn ? "flash:on" : "flash:off";
|
|
ws.send(message);
|
|
this.style.backgroundColor = flashOn ? "#f1c40f" : "#bbbbbb";
|
|
});
|
|
|
|
function showForm() {
|
|
const addButton = document.querySelector(".add-btn");
|
|
if (addButton.disabled) {
|
|
alert("Maksimal 2 pengguna saja yang dapat didaftarkan.");
|
|
return;
|
|
}
|
|
|
|
document.getElementById("user-panel").style.display = "none";
|
|
document.getElementById("form-panel").classList.add("active");
|
|
document.getElementById("name").value = "";
|
|
document.getElementById("email").value = "";
|
|
document.getElementById("age").value = "";
|
|
document.getElementById("gender").value = "";
|
|
document.getElementById("phone").value = "";
|
|
document.getElementById("name").readOnly = false;
|
|
document.querySelector("#form-panel h2").textContent = "New User";
|
|
document.querySelector("#form-panel p").textContent = "Setelah mengisi formulir, arahkan wajah Anda ke kamera lalu klik \"Enroll Face\"";
|
|
document.getElementById("button-capture").textContent = "Enroll Face";
|
|
editingUserId = null;
|
|
}
|
|
|
|
|
|
function cancelForm() {
|
|
document.getElementById("user-panel").style.display = "block";
|
|
document.getElementById("form-panel").classList.remove("active");
|
|
pendingUserData = null;
|
|
waitingForPIN = false;
|
|
|
|
const form = document.getElementById("userForm");
|
|
if (form) {
|
|
form.reset();
|
|
}
|
|
document.getElementById("name").readOnly = false;
|
|
editingUserId = null;
|
|
const titleEl = document.getElementById("form-title");
|
|
if (titleEl) {
|
|
titleEl.textContent = "New User";
|
|
}
|
|
const captureBtn = document.getElementById("button-capture");
|
|
if (captureBtn) {
|
|
captureBtn.textContent = "Enroll Face";
|
|
}
|
|
}
|
|
|
|
async function editUser(id) {
|
|
const doc = await db.collection("users").doc(id).get();
|
|
if (!doc.exists) return alert("User tidak ditemukan");
|
|
const data = doc.data();
|
|
document.getElementById("name").value = data.name;
|
|
document.getElementById("email").value = data.email;
|
|
document.getElementById("age").value = data.age;
|
|
document.getElementById("gender").value = data.gender;
|
|
document.getElementById("phone").value = data.phone;
|
|
document.getElementById("name").readOnly = true;
|
|
editingUserId = id;
|
|
document.getElementById("user-panel").style.display = "none";
|
|
document.getElementById("form-panel").classList.add("active");
|
|
document.querySelector("#form-panel h2").textContent = "Edit User";
|
|
document.querySelector("#form-panel p").textContent = "Ubah data pengguna lalu klik 'Save'.";
|
|
const captureBtn = document.getElementById("button-capture");
|
|
captureBtn.textContent = "Save";
|
|
}
|
|
|
|
async function deleteUser(id) {
|
|
if (confirm("Hapus user ini dari sistem?")) {
|
|
try {
|
|
const userDoc = await db.collection("users").doc(id).get();
|
|
if (!userDoc.exists) {
|
|
alert("Data user tidak ditemukan.");
|
|
return;
|
|
}
|
|
const userData = userDoc.data();
|
|
const person_name = userData.name;
|
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
alert("ESP32-CAM tidak terhubung. Tidak dapat menghapus wajah.");
|
|
return;
|
|
}
|
|
const deleteFromESP32 = new Promise((resolve, reject) => {
|
|
const timeout = setTimeout(() => {
|
|
reject("Timeout dari ESP32-CAM");
|
|
}, 3000);
|
|
function handleWSResponse(event) {
|
|
if (event.data === "remove:ok") {
|
|
clearTimeout(timeout);
|
|
ws.removeEventListener("message", handleWSResponse);
|
|
resolve();
|
|
} else if (event.data.startsWith("remove:error")) {
|
|
clearTimeout(timeout);
|
|
ws.removeEventListener("message", handleWSResponse);
|
|
reject("ESP32-CAM gagal hapus wajah.");
|
|
}
|
|
}
|
|
ws.addEventListener("message", handleWSResponse);
|
|
ws.send("remove:" + person_name);
|
|
});
|
|
await deleteFromESP32;
|
|
await db.collection("users").doc(id).delete();
|
|
alert("User berhasil dihapus.");
|
|
} catch (e) {
|
|
console.error(e);
|
|
alert("Gagal menghapus user: " + e);
|
|
}
|
|
}
|
|
}
|
|
|
|
function openPINModal() {
|
|
document.getElementById("pinModal").style.display = "flex";
|
|
}
|
|
|
|
function closePINModal() {
|
|
document.getElementById("pinModal").style.display = "none";
|
|
}
|
|
|
|
function submitPIN() {
|
|
const pin = document.getElementById("newPIN").value.trim();
|
|
if (pin.length !== 6 || !/^\d+$/.test(pin)) {
|
|
alert("PIN harus 6 digit angka.");
|
|
return;
|
|
}
|
|
|
|
if (!pendingUserData) {
|
|
alert("Data user belum lengkap. Silakan ulangi.");
|
|
return;
|
|
}
|
|
|
|
ws.send("newpin:" + pin);
|
|
justSetPIN = true;
|
|
closePINModal();
|
|
|
|
ws.send("capture:" + pendingUserData.name);
|
|
waitingForPIN = true;
|
|
alert("Tunggu proses perekaman wajah dari ESP32-CAM...");
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
<div id="pinModal" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%;
|
|
background-color: rgba(0,0,0,0.5); align-items:center; justify-content:center; z-index:9999;">
|
|
<div style="background:white; padding:20px; border-radius:10px; max-width:300px; width:90%; text-align:center;">
|
|
<h3>Masukkan PIN Brankas</h3>
|
|
<input type="password" id="newPIN" maxlength="6" placeholder="6 digit angka"
|
|
style="padding:10px; width:90%; font-size:16px; margin-top:10px;" />
|
|
<div style="margin-top:15px;">
|
|
<button onclick="submitPIN()"
|
|
style="padding:10px 20px; background:#2e8b57; color:white; border:none; border-radius:5px;">Simpan</button>
|
|
<button onclick="closePINModal()"
|
|
style="padding:10px 20px; background:#ccc; color:black; border:none; border-radius:5px;">Batal</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html> |