Spaces:
Running
Running
Upload 7 files
Browse files- javascript/cart.js +184 -184
- javascript/main.js +503 -504
- javascript/profile.js +259 -242
- javascript/search.js +18 -9
javascript/cart.js
CHANGED
@@ -1,187 +1,187 @@
|
|
1 |
-
async function updateCartIcon() {
|
2 |
-
const cart = await getCarts();
|
3 |
-
const count = cart.length;
|
4 |
-
const cartIcon = document.getElementById("cart-icon");
|
5 |
-
if (cartIcon) {
|
6 |
-
cartIcon.innerHTML = `
|
7 |
-
<i class="bi bi-cart"></i>
|
8 |
-
<span class="m-1">
|
9 |
-
<span class="nd-720">Keranjang</span>
|
10 |
-
<span class="nd-512">(${count})</span>
|
11 |
-
</span>`;
|
12 |
-
}
|
13 |
-
}
|
14 |
-
|
15 |
-
document.addEventListener("DOMContentLoaded", async function () {
|
16 |
-
setInterval(await updateCartIcon,
|
17 |
-
});
|
18 |
-
|
19 |
-
async function promptQuantityAndAdd(productId) {
|
20 |
-
checkUID("addToCart");
|
21 |
-
let [qtyStr, isCancel] = await prompt(
|
22 |
-
"Masukkan jumlah produk yang ingin ditambahkan ke keranjang:",
|
23 |
-
"number"
|
24 |
-
);
|
25 |
-
|
26 |
-
console.log(qtyStr, isCancel);
|
27 |
-
|
28 |
-
if (isCancel) {
|
29 |
-
return;
|
30 |
-
}
|
31 |
-
|
32 |
-
if (!qtyStr) {
|
33 |
-
alert("Jumlah tidak boleh kosong.", false);
|
34 |
-
return;
|
35 |
-
}
|
36 |
-
|
37 |
-
let qty = parseInt(qtyStr);
|
38 |
-
|
39 |
-
if (isNaN(qty) || qty <= 0) {
|
40 |
-
alert("Masukkan jumlah produk yang valid.", false);
|
41 |
-
return;
|
42 |
-
}
|
43 |
-
|
44 |
-
await addToCart(productId, qty);
|
45 |
-
}
|
46 |
-
|
47 |
-
async function addToCart(productId, quantity = 1) {
|
48 |
-
checkUID("addToCart");
|
49 |
-
|
50 |
-
console.log(productId, quantity);
|
51 |
-
|
52 |
-
const cart = await getCarts();
|
53 |
-
|
54 |
-
let userData = await getUserData();
|
55 |
-
|
56 |
-
for (let i = 0; i < quantity; i++) {
|
57 |
-
cart.push(productId);
|
58 |
-
}
|
59 |
-
|
60 |
-
userData.cart = cart;
|
61 |
-
localStorage.setItem("cart", JSON.stringify(cart));
|
62 |
-
|
63 |
-
await updateUserDataToGitHub(userData);
|
64 |
-
await updateCartIcon();
|
65 |
-
|
66 |
-
alert(
|
67 |
-
`Produk telah ditambahkan ke keranjang sebanyak ${quantity} item.`,
|
68 |
-
false
|
69 |
-
);
|
70 |
-
}
|
71 |
-
|
72 |
-
function addToCartFromProduct(productId) {
|
73 |
-
checkUID("addToCart");
|
74 |
-
|
75 |
-
const qtyInput = document.getElementById("quantity");
|
76 |
-
let quantity = parseInt(qtyInput.value);
|
77 |
-
if (isNaN(quantity) || quantity <= 0) {
|
78 |
-
alert("Masukkan jumlah produk yang valid.", false);
|
79 |
-
return;
|
80 |
-
}
|
81 |
-
|
82 |
-
addToCart(productId, quantity);
|
83 |
-
}
|
84 |
-
|
85 |
-
async function loadCart() {
|
86 |
-
do {
|
87 |
-
const cart = await getCarts();
|
88 |
-
const params = new URLSearchParams(window.location.search);
|
89 |
const message = params.get("message") || "";
|
90 |
|
91 |
-
if (message) alert(message, false);
|
92 |
-
|
93 |
-
let cartCount = {};
|
94 |
-
cart.forEach((id) => {
|
95 |
-
cartCount[id] = (cartCount[id] || 0) + 1;
|
96 |
-
});
|
97 |
-
|
98 |
-
const cartItemsContainer = document.getElementById("cart-items");
|
99 |
-
cartItemsContainer.innerHTML = "";
|
100 |
-
let total = 0;
|
101 |
-
|
102 |
-
if (Object.keys(cartCount).length === 0) {
|
103 |
-
cartItemsContainer.innerHTML = "<p>Keranjang kosong.</p>";
|
104 |
-
} else {
|
105 |
-
cartItemsContainer.innerHTML = `<span class="text-center text-muted">${message}</span>`;
|
106 |
-
let index = 1;
|
107 |
-
for (let id in cartCount) {
|
108 |
-
const product = products.find((p) => p.id === parseInt(id));
|
109 |
-
if (product) {
|
110 |
-
const qty = cartCount[id];
|
111 |
-
const subtotal = product.price * qty;
|
112 |
-
total += subtotal;
|
113 |
-
cartItemsContainer.innerHTML += `
|
114 |
-
<div class="d-flex justify-content-between align-items-center border-bottom pb-2 mb-2">
|
115 |
-
<div>
|
116 |
-
<h5>${product.name}</h5>
|
117 |
-
<p class="mb-0">
|
118 |
-
${formatRupiah(product.price)} x
|
119 |
-
<input class="form-control form-control-lg" type="number" value="${qty}" min="1" style="width:60px;"
|
120 |
-
onchange="updateQuantity(${product.id}, this.value)">
|
121 |
-
</p>
|
122 |
-
</div>
|
123 |
-
<button class="btn btn-danger btn-lg" onclick="removeFromCart(${
|
124 |
-
product.id
|
125 |
-
})">Hapus</button>
|
126 |
-
</div>`;
|
127 |
-
index++;
|
128 |
-
}
|
129 |
-
}
|
130 |
-
}
|
131 |
-
const cartSummary = document.getElementById("cart-summary");
|
132 |
-
cartSummary.innerHTML = `<h4>Total: ${formatRupiah(total)}</h4>`;
|
133 |
-
|
134 |
-
if (cartItemsContainer.innerHTML.trim() == "") {
|
135 |
-
cartItemsContainer.innerHTML = "";
|
136 |
-
alert("Jika daftar keranjang tidak muncul, silahkan muat ulang halaman ini", false)
|
137 |
-
}
|
138 |
-
|
139 |
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
140 |
-
} while (cartItemsContainer.innerHTML.trim() == "");
|
141 |
-
}
|
142 |
-
|
143 |
-
async function updateQuantity(productId, newQty) {
|
144 |
-
newQty = parseInt(newQty);
|
145 |
-
if (isNaN(newQty) || newQty <= 0) {
|
146 |
-
alert("Masukkan jumlah produk yang valid.", false);
|
147 |
-
return;
|
148 |
-
}
|
149 |
-
|
150 |
-
const cart = await getCarts();
|
151 |
-
const newCart = cart.filter((id) => id !== productId);
|
152 |
-
|
153 |
-
for (let i = 0; i < newQty; i++) {
|
154 |
-
newCart.push(productId);
|
155 |
-
}
|
156 |
-
localStorage.setItem("cart", JSON.stringify(newCart));
|
157 |
-
|
158 |
-
let userData = await getUserData();
|
159 |
-
userData.cart = newCart;
|
160 |
-
await updateUserDataToGitHub(userData);
|
161 |
-
|
162 |
-
await loadCart();
|
163 |
-
await updateCartIcon();
|
164 |
-
}
|
165 |
-
|
166 |
-
async function removeFromCart(productId) {
|
167 |
-
const cart = await getCarts();
|
168 |
-
const index = cart.indexOf(productId);
|
169 |
-
if (index !== -1) {
|
170 |
-
cart.splice(index, 1);
|
171 |
-
localStorage.setItem("cart", JSON.stringify(cart));
|
172 |
-
|
173 |
-
let userData = await getUserData();
|
174 |
-
userData.cart = cart;
|
175 |
-
|
176 |
-
await updateUserDataToGitHub(userData);
|
177 |
-
await loadCart();
|
178 |
-
await updateCartIcon();
|
179 |
-
}
|
180 |
-
}
|
181 |
-
|
182 |
-
document.addEventListener("DOMContentLoaded", function () {
|
183 |
-
AOS.init();
|
184 |
-
if (document.getElementById("cart-items")) {
|
185 |
-
loadCart();
|
186 |
-
}
|
187 |
});
|
|
|
1 |
+
async function updateCartIcon() {
|
2 |
+
const cart = await getCarts();
|
3 |
+
const count = cart.length;
|
4 |
+
const cartIcon = document.getElementById("cart-icon");
|
5 |
+
if (cartIcon) {
|
6 |
+
cartIcon.innerHTML = `
|
7 |
+
<i class="bi bi-cart"></i>
|
8 |
+
<span class="m-1">
|
9 |
+
<span class="nd-720">Keranjang</span>
|
10 |
+
<span class="nd-512">(${count})</span>
|
11 |
+
</span>`;
|
12 |
+
}
|
13 |
+
}
|
14 |
+
|
15 |
+
document.addEventListener("DOMContentLoaded", async function () {
|
16 |
+
setInterval(await updateCartIcon, 10000);
|
17 |
+
});
|
18 |
+
|
19 |
+
async function promptQuantityAndAdd(productId) {
|
20 |
+
checkUID("addToCart");
|
21 |
+
let [qtyStr, isCancel] = await prompt(
|
22 |
+
"Masukkan jumlah produk yang ingin ditambahkan ke keranjang:",
|
23 |
+
"number"
|
24 |
+
);
|
25 |
+
|
26 |
+
console.log(qtyStr, isCancel);
|
27 |
+
|
28 |
+
if (isCancel) {
|
29 |
+
return;
|
30 |
+
}
|
31 |
+
|
32 |
+
if (!qtyStr) {
|
33 |
+
alert("Jumlah tidak boleh kosong.", false);
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
+
let qty = parseInt(qtyStr);
|
38 |
+
|
39 |
+
if (isNaN(qty) || qty <= 0) {
|
40 |
+
alert("Masukkan jumlah produk yang valid.", false);
|
41 |
+
return;
|
42 |
+
}
|
43 |
+
|
44 |
+
await addToCart(productId, qty);
|
45 |
+
}
|
46 |
+
|
47 |
+
async function addToCart(productId, quantity = 1) {
|
48 |
+
checkUID("addToCart");
|
49 |
+
|
50 |
+
console.log(productId, quantity);
|
51 |
+
|
52 |
+
const cart = await getCarts();
|
53 |
+
|
54 |
+
let userData = await getUserData();
|
55 |
+
|
56 |
+
for (let i = 0; i < quantity; i++) {
|
57 |
+
cart.push(productId);
|
58 |
+
}
|
59 |
+
|
60 |
+
userData.cart = cart;
|
61 |
+
localStorage.setItem("cart", JSON.stringify(cart));
|
62 |
+
|
63 |
+
await updateUserDataToGitHub(userData);
|
64 |
+
await updateCartIcon();
|
65 |
+
|
66 |
+
alert(
|
67 |
+
`Produk telah ditambahkan ke keranjang sebanyak ${quantity} item.`,
|
68 |
+
false
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
function addToCartFromProduct(productId) {
|
73 |
+
checkUID("addToCart");
|
74 |
+
|
75 |
+
const qtyInput = document.getElementById("quantity");
|
76 |
+
let quantity = parseInt(qtyInput.value);
|
77 |
+
if (isNaN(quantity) || quantity <= 0) {
|
78 |
+
alert("Masukkan jumlah produk yang valid.", false);
|
79 |
+
return;
|
80 |
+
}
|
81 |
+
|
82 |
+
addToCart(productId, quantity);
|
83 |
+
}
|
84 |
+
|
85 |
+
async function loadCart() {
|
86 |
+
do {
|
87 |
+
const cart = await getCarts();
|
88 |
+
const params = new URLSearchParams(window.location.search);
|
89 |
const message = params.get("message") || "";
|
90 |
|
91 |
+
if (message) alert(message, false);
|
92 |
+
|
93 |
+
let cartCount = {};
|
94 |
+
cart.forEach((id) => {
|
95 |
+
cartCount[id] = (cartCount[id] || 0) + 1;
|
96 |
+
});
|
97 |
+
|
98 |
+
const cartItemsContainer = document.getElementById("cart-items");
|
99 |
+
cartItemsContainer.innerHTML = "";
|
100 |
+
let total = 0;
|
101 |
+
|
102 |
+
if (Object.keys(cartCount).length === 0) {
|
103 |
+
cartItemsContainer.innerHTML = "<p>Keranjang kosong.</p>";
|
104 |
+
} else {
|
105 |
+
cartItemsContainer.innerHTML = `<span class="text-center text-muted">${message}</span>`;
|
106 |
+
let index = 1;
|
107 |
+
for (let id in cartCount) {
|
108 |
+
const product = products.find((p) => p.id === parseInt(id));
|
109 |
+
if (product) {
|
110 |
+
const qty = cartCount[id];
|
111 |
+
const subtotal = product.price * qty;
|
112 |
+
total += subtotal;
|
113 |
+
cartItemsContainer.innerHTML += `
|
114 |
+
<div class="d-flex justify-content-between align-items-center border-bottom pb-2 mb-2">
|
115 |
+
<div>
|
116 |
+
<h5>${product.name}</h5>
|
117 |
+
<p class="mb-0">
|
118 |
+
${formatRupiah(product.price)} x
|
119 |
+
<input class="form-control form-control-lg" type="number" value="${qty}" min="1" style="width:60px;"
|
120 |
+
onchange="updateQuantity(${product.id}, this.value)">
|
121 |
+
</p>
|
122 |
+
</div>
|
123 |
+
<button class="btn btn-danger btn-lg" onclick="removeFromCart(${
|
124 |
+
product.id
|
125 |
+
})">Hapus</button>
|
126 |
+
</div>`;
|
127 |
+
index++;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
}
|
131 |
+
const cartSummary = document.getElementById("cart-summary");
|
132 |
+
cartSummary.innerHTML = `<h4>Total: ${formatRupiah(total)}</h4>`;
|
133 |
+
|
134 |
+
if (cartItemsContainer.innerHTML.trim() == "") {
|
135 |
+
cartItemsContainer.innerHTML = "";
|
136 |
+
alert("Jika daftar keranjang tidak muncul, silahkan muat ulang halaman ini", false)
|
137 |
+
}
|
138 |
+
|
139 |
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
140 |
+
} while (cartItemsContainer.innerHTML.trim() == "");
|
141 |
+
}
|
142 |
+
|
143 |
+
async function updateQuantity(productId, newQty) {
|
144 |
+
newQty = parseInt(newQty);
|
145 |
+
if (isNaN(newQty) || newQty <= 0) {
|
146 |
+
alert("Masukkan jumlah produk yang valid.", false);
|
147 |
+
return;
|
148 |
+
}
|
149 |
+
|
150 |
+
const cart = await getCarts();
|
151 |
+
const newCart = cart.filter((id) => id !== productId);
|
152 |
+
|
153 |
+
for (let i = 0; i < newQty; i++) {
|
154 |
+
newCart.push(productId);
|
155 |
+
}
|
156 |
+
localStorage.setItem("cart", JSON.stringify(newCart));
|
157 |
+
|
158 |
+
let userData = await getUserData();
|
159 |
+
userData.cart = newCart;
|
160 |
+
await updateUserDataToGitHub(userData);
|
161 |
+
|
162 |
+
await loadCart();
|
163 |
+
await updateCartIcon();
|
164 |
+
}
|
165 |
+
|
166 |
+
async function removeFromCart(productId) {
|
167 |
+
const cart = await getCarts();
|
168 |
+
const index = cart.indexOf(productId);
|
169 |
+
if (index !== -1) {
|
170 |
+
cart.splice(index, 1);
|
171 |
+
localStorage.setItem("cart", JSON.stringify(cart));
|
172 |
+
|
173 |
+
let userData = await getUserData();
|
174 |
+
userData.cart = cart;
|
175 |
+
|
176 |
+
await updateUserDataToGitHub(userData);
|
177 |
+
await loadCart();
|
178 |
+
await updateCartIcon();
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
document.addEventListener("DOMContentLoaded", function () {
|
183 |
+
AOS.init();
|
184 |
+
if (document.getElementById("cart-items")) {
|
185 |
+
loadCart();
|
186 |
+
}
|
187 |
});
|
javascript/main.js
CHANGED
@@ -1,505 +1,504 @@
|
|
1 |
-
function checkUID(m = "") {
|
2 |
-
const userData = JSON.parse(localStorage.getItem("userData")) || {};
|
3 |
-
const urlNow = window.location.href;
|
4 |
-
const isNotValidUID = userData.length > 0 && !userData.uid;
|
5 |
-
|
6 |
-
const message =
|
7 |
-
m === "addToCart"
|
8 |
-
? "Silakan masuk terlebih dahulu untuk menambahkan produk ke keranjang."
|
9 |
-
: isNotValidUID
|
10 |
-
? "Sesi Anda telah habis. Silakan masuk kembali untuk melanjutkan."
|
11 |
-
: m === "jelajah"
|
12 |
-
? "Anda telah menjelajah
|
13 |
-
: null;
|
14 |
-
|
15 |
-
if (
|
16 |
-
(Object.keys(userData).length === 0 || isNotValidUID) &&
|
17 |
-
!urlNow.includes("profile")
|
18 |
-
) {
|
19 |
-
localStorage.clear();
|
20 |
-
window.location.href =
|
21 |
-
`profile.html?redirect=${urlNow}` +
|
22 |
-
(message ? `&message=${message}` : "");
|
23 |
-
}
|
24 |
-
}
|
25 |
-
|
26 |
-
setTimeout(() => {
|
27 |
-
checkUID("jelajah");
|
28 |
-
}, 5000);
|
29 |
-
|
30 |
-
function formatRupiah(number) {
|
31 |
-
return "Rp " + number.toLocaleString("id-ID");
|
32 |
-
}
|
33 |
-
|
34 |
-
async function loadProducts() {
|
35 |
-
const products = await fetchProductsData();
|
36 |
-
|
37 |
-
const productList = document.getElementById("product-list");
|
38 |
-
if (!productList) return;
|
39 |
-
productList.innerHTML = "";
|
40 |
-
products.forEach((product) => {
|
41 |
-
const col = document.createElement("div");
|
42 |
-
col.className = "col-lg-3 col-md-4 col-sm-6";
|
43 |
-
|
44 |
-
const card = document.createElement("div");
|
45 |
-
card.className = "card h-100";
|
46 |
-
|
47 |
-
let thumbnail = "assets/";
|
48 |
-
if (product.files && product.files.length > 0) {
|
49 |
-
thumbnail += product.files[0];
|
50 |
-
}
|
51 |
-
let thumbHTML = "";
|
52 |
-
if (thumbnail.match(/\.(jpg|jpeg|png|gif)$/i)) {
|
53 |
-
thumbHTML = `<img src="${thumbnail}" class="card-img-top" alt="${product.name}">`;
|
54 |
-
} else {
|
55 |
-
thumbHTML = `<img src="img/placeholder.jpg" class="card-img-top" alt="${product.name}">`;
|
56 |
-
}
|
57 |
-
|
58 |
-
const cardBody = document.createElement("div");
|
59 |
-
cardBody.className = "card-body d-flex flex-column";
|
60 |
-
|
61 |
-
const title = document.createElement("h5");
|
62 |
-
title.className = "card-title";
|
63 |
-
title.innerText = product.name;
|
64 |
-
|
65 |
-
const desc = document.createElement("p");
|
66 |
-
desc.className = "card-text";
|
67 |
-
desc.innerText = product.description;
|
68 |
-
|
69 |
-
const price = document.createElement("p");
|
70 |
-
price.className = "card-text fw-bold";
|
71 |
-
price.innerText = formatRupiah(product.price);
|
72 |
-
|
73 |
-
const detailLink = document.createElement("a");
|
74 |
-
detailLink.href = "product.html?id=" + product.id;
|
75 |
-
detailLink.className = "btn btn-primary btn-lg mt-auto";
|
76 |
-
detailLink.innerText = "Lihat Detail";
|
77 |
-
|
78 |
-
const cartBtn = document.createElement("button");
|
79 |
-
cartBtn.className = "btn btn-success btn-lg mt-2";
|
80 |
-
cartBtn.innerText = "Tambah Keranjang";
|
81 |
-
cartBtn.onclick = async function () {
|
82 |
-
await promptQuantityAndAdd(product.id);
|
83 |
-
};
|
84 |
-
|
85 |
-
card.innerHTML = thumbHTML;
|
86 |
-
cardBody.appendChild(title);
|
87 |
-
cardBody.appendChild(desc);
|
88 |
-
cardBody.appendChild(price);
|
89 |
-
cardBody.appendChild(detailLink);
|
90 |
-
cardBody.appendChild(cartBtn);
|
91 |
-
card.appendChild(cardBody);
|
92 |
-
col.appendChild(card);
|
93 |
-
productList.appendChild(col);
|
94 |
-
});
|
95 |
-
}
|
96 |
-
|
97 |
-
async function loadProductDetail() {
|
98 |
-
const params = new URLSearchParams(window.location.search);
|
99 |
-
const productId = parseInt(params.get("id"));
|
100 |
-
const container = document.getElementById("product-detail");
|
101 |
-
if (!container) return;
|
102 |
-
|
103 |
-
if (!productId) {
|
104 |
-
container.innerText = "Produk tidak ditemukan.";
|
105 |
-
return;
|
106 |
-
}
|
107 |
-
|
108 |
-
const products = await fetchProductsData();
|
109 |
-
const product = products.find((p) => p.id === productId);
|
110 |
-
if (!product) {
|
111 |
-
container.innerText = "Produk tidak ditemukan.";
|
112 |
-
return;
|
113 |
-
}
|
114 |
-
let carouselHTML = "";
|
115 |
-
if (product.files && product.files.length > 0) {
|
116 |
-
carouselHTML += `<div id="carouselProduct" class="carousel slide" data-bs-ride="carousel">`;
|
117 |
-
carouselHTML += `<div class="carousel-indicators">`;
|
118 |
-
product.files.forEach((file, index) => {
|
119 |
-
carouselHTML += `<button type="button" data-bs-target="#carouselProduct" data-bs-slide-to="${index}" ${
|
120 |
-
index === 0 ? 'class="active" aria-current="true"' : ""
|
121 |
-
} aria-label="Slide ${index + 1}"></button>`;
|
122 |
-
});
|
123 |
-
carouselHTML += `</div>`;
|
124 |
-
carouselHTML += `<div class="carousel-inner">`;
|
125 |
-
product.files.forEach((file, index) => {
|
126 |
-
file = "assets/" + file;
|
127 |
-
carouselHTML += `<div class="carousel-item ${
|
128 |
-
index === 0 ? "active" : ""
|
129 |
-
}">`;
|
130 |
-
if (file.match(/\.(jpg|jpeg|png|gif)$/i)) {
|
131 |
-
carouselHTML += `<img src="${file}" class="d-block w-100" alt="${product.name}">`;
|
132 |
-
} else if (file.match(/\.(mp4|webm)$/i)) {
|
133 |
-
carouselHTML += `<video class="d-block w-100" controls>
|
134 |
-
<source src="${file}" type="video/mp4">
|
135 |
-
Browser Anda tidak mendukung video.
|
136 |
-
</video>`;
|
137 |
-
}
|
138 |
-
carouselHTML += `</div>`;
|
139 |
-
});
|
140 |
-
carouselHTML += `</div>`;
|
141 |
-
carouselHTML += `<button class="carousel-control-prev" type="button" data-bs-target="#carouselProduct" data-bs-slide="prev">
|
142 |
-
<span class="carousel-control-prev-icon bg-primary rounded-circle p-4" aria-hidden="true"></span>
|
143 |
-
<span class="visually-hidden">Previous</span>
|
144 |
-
</button>
|
145 |
-
<button class="carousel-control-next" type="button" data-bs-target="#carouselProduct" data-bs-slide="next">
|
146 |
-
<span class="carousel-control-next-icon bg-primary rounded-circle p-4" aria-hidden="true"></span>
|
147 |
-
<span class="visually-hidden">Next</span>
|
148 |
-
</button>`;
|
149 |
-
carouselHTML += `</div>`;
|
150 |
-
}
|
151 |
-
container.innerHTML = `
|
152 |
-
<div class="row">
|
153 |
-
<div class="col-md-6">
|
154 |
-
${carouselHTML}
|
155 |
-
</div>
|
156 |
-
<div class="col-md-6">
|
157 |
-
<h2>${product.name}</h2>
|
158 |
-
<p>${product.description}</p>
|
159 |
-
<h4 class="fw-bold">${formatRupiah(product.price)}</h4>
|
160 |
-
<div class="mb-2">
|
161 |
-
<label for="quantity" class="form-label">Jumlah:</label>
|
162 |
-
<input type="number" id="quantity" class="form-control form-control-lg" value="1" min="1" style="max-width:150px;">
|
163 |
-
</div>
|
164 |
-
<button class="btn btn-success btn-lg" onclick="addToCartFromProduct(${
|
165 |
-
product.id
|
166 |
-
})">
|
167 |
-
Tambahkan ke Keranjang
|
168 |
-
</button>
|
169 |
-
</div>
|
170 |
-
</div>
|
171 |
-
`;
|
172 |
-
AOS.refresh();
|
173 |
-
}
|
174 |
-
|
175 |
-
function formatTransactionDate(key) {
|
176 |
-
const parts = key.split("_");
|
177 |
-
if (parts.length !== 7) return key;
|
178 |
-
|
179 |
-
const [year, month, day, hour, minute, second, ms] = parts;
|
180 |
-
|
181 |
-
const dateObj = new Date(
|
182 |
-
year,
|
183 |
-
parseInt(month) - 1,
|
184 |
-
day,
|
185 |
-
hour,
|
186 |
-
minute,
|
187 |
-
second,
|
188 |
-
ms
|
189 |
-
);
|
190 |
-
|
191 |
-
const formattedDate = dateObj.toLocaleDateString("id-ID", {
|
192 |
-
day: "2-digit",
|
193 |
-
month: "long",
|
194 |
-
year: "numeric",
|
195 |
-
});
|
196 |
-
|
197 |
-
const formattedTime = dateObj.toLocaleTimeString("id-ID", {
|
198 |
-
hour: "2-digit",
|
199 |
-
minute: "2-digit",
|
200 |
-
hour12: false,
|
201 |
-
});
|
202 |
-
|
203 |
-
return [formattedDate, formattedTime];
|
204 |
-
}
|
205 |
-
|
206 |
-
async function loadTransactions() {
|
207 |
-
const userData = await getUserData();
|
208 |
-
const transactions = userData.transactions || {};
|
209 |
-
|
210 |
-
if (
|
211 |
-
document.getElementById("transaction-
|
212 |
-
"<p>Anda belum melakukan transaksi apapun. Silahkan beli produk terlebih dahulu.</p>";
|
213 |
-
alert("Anda belum melakukan transaksi apapun. Silahkan beli produk terlebih dahulu.", false)
|
214 |
-
return;
|
215 |
-
}
|
216 |
-
|
217 |
-
const transactionList = document.getElementById("transaction-list");
|
218 |
-
if (!transactionList) return;
|
219 |
-
transactionList.innerHTML = "";
|
220 |
-
|
221 |
-
Object.entries(transactions)
|
222 |
-
.sort((a, b) => {
|
223 |
-
const parseDateFromKey = (key) => {
|
224 |
-
const [year, month, day, hour, minute, second, ms] =
|
225 |
-
key.split("_");
|
226 |
-
return new Date(year, month - 1, day, hour, minute, second, ms);
|
227 |
-
};
|
228 |
-
|
229 |
-
return parseDateFromKey(b[0]) - parseDateFromKey(a[0]);
|
230 |
-
})
|
231 |
-
.forEach(([transactionKey, transaction]) => {
|
232 |
-
let total = 0;
|
233 |
-
|
234 |
-
transaction.products.forEach((product) => {
|
235 |
-
total += product.price * product.total;
|
236 |
-
});
|
237 |
-
|
238 |
-
let paymentIcon = "";
|
239 |
-
switch (transaction.payment_method) {
|
240 |
-
case "bank":
|
241 |
-
paymentIcon = "bi-building";
|
242 |
-
break;
|
243 |
-
case "qr":
|
244 |
-
paymentIcon = "bi-qr-code";
|
245 |
-
break;
|
246 |
-
case "card":
|
247 |
-
paymentIcon = "bi-credit-card";
|
248 |
-
break;
|
249 |
-
default:
|
250 |
-
paymentIcon = "bi-currency-dollar";
|
251 |
-
}
|
252 |
-
|
253 |
-
const col = document.createElement("div");
|
254 |
-
col.className =
|
255 |
-
"transactions-body col-lg-4 col-md-6 col-sm-12 mt-4";
|
256 |
-
|
257 |
-
const card = document.createElement("div");
|
258 |
-
card.className = "card h-100";
|
259 |
-
|
260 |
-
const cardBody = document.createElement("div");
|
261 |
-
cardBody.className = "card-body d-flex flex-column";
|
262 |
-
|
263 |
-
cardBody.style.position = "relative";
|
264 |
-
|
265 |
-
const td = formatTransactionDate(transactionKey);
|
266 |
-
|
267 |
-
cardBody.innerHTML = `
|
268 |
-
<div class="payment-icon-bg">
|
269 |
-
<i class="bi ${paymentIcon}" style="font-size: 2.5rem;"></i>
|
270 |
-
<span class="m-2">Metode: ${
|
271 |
-
transaction.payment_method ? transaction.payment_method : "N/A"
|
272 |
-
}</span>
|
273 |
-
</div>
|
274 |
-
<div class="content">
|
275 |
-
<div class="d-flex justify-content-between align-items-center">
|
276 |
-
<div class="transaction-total fw-bold">
|
277 |
-
Total: ${formatRupiah(total)}
|
278 |
-
</div>
|
279 |
-
</div>
|
280 |
-
<div class="d-flex justify-content-between align-items-center mt-auto">
|
281 |
-
<div class="transaction-date">
|
282 |
-
Tanggal: ${td[0]} <br> Waktu: ${td[1]}
|
283 |
-
</div>
|
284 |
-
</div>
|
285 |
-
</div>
|
286 |
-
`;
|
287 |
-
|
288 |
-
card.appendChild(cardBody);
|
289 |
-
col.appendChild(card);
|
290 |
-
transactionList.appendChild(col);
|
291 |
-
|
292 |
-
col.addEventListener(
|
293 |
-
"click",
|
294 |
-
() =>
|
295 |
-
(window.location.href = `transaction.html?date=${transactionKey}`)
|
296 |
-
);
|
297 |
-
});
|
298 |
-
}
|
299 |
-
|
300 |
-
let transactionKey;
|
301 |
-
|
302 |
-
async function updateActionButtonsPosition() {
|
303 |
-
const wrapper = document.querySelector(".receipt-wrapper");
|
304 |
-
const actionButtons = document.querySelector(".action-buttons");
|
305 |
-
if (!wrapper || !actionButtons) {
|
306 |
-
console.error("Element tidak ditemukan!");
|
307 |
-
return;
|
308 |
-
}
|
309 |
-
|
310 |
-
const receiptHeight = wrapper.getBoundingClientRect().height + 128;
|
311 |
-
actionButtons.setAttribute("style", `top: ${receiptHeight}px !important`);
|
312 |
-
}
|
313 |
-
|
314 |
-
async function loadTransactionDetail() {
|
315 |
-
const params = new URLSearchParams(window.location.search);
|
316 |
-
const transactionKey = params.get("date");
|
317 |
-
const detailsContainer = document.getElementById("transaction-details");
|
318 |
-
const mainElement = document.querySelector("main");
|
319 |
-
|
320 |
-
if (!transactionKey) {
|
321 |
-
detailsContainer.innerHTML = "<p>Transaksi tidak ditemukan.</p>";
|
322 |
-
return;
|
323 |
-
}
|
324 |
-
|
325 |
-
const userData = await getUserData();
|
326 |
-
|
327 |
-
if (!userData.transactions || !userData.transactions[transactionKey]) {
|
328 |
-
mainElement.innerHTML = "<p>Transaksi tidak ditemukan.</p>";
|
329 |
-
return;
|
330 |
-
}
|
331 |
-
|
332 |
-
const transaction = userData.transactions[transactionKey];
|
333 |
-
|
334 |
-
const td = formatTransactionDate(transactionKey);
|
335 |
-
|
336 |
-
let total = 0;
|
337 |
-
transaction.products.forEach((prod) => {
|
338 |
-
total += prod.price * prod.total;
|
339 |
-
});
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
<
|
345 |
-
<
|
346 |
-
<p>
|
347 |
-
<p>
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
<th>
|
354 |
-
<th>
|
355 |
-
<th>
|
356 |
-
<th>
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
const
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
<td>${
|
369 |
-
<td>${prod.
|
370 |
-
<td>${
|
371 |
-
<td>${
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
<td
|
380 |
-
|
381 |
-
</
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
<p>
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
wrapper.
|
394 |
-
|
395 |
-
detailsContainer.
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
.
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
receiptWrapperElement.
|
436 |
-
|
437 |
-
|
438 |
-
receiptWrapperElement.
|
439 |
-
|
440 |
-
|
441 |
-
const
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
.
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
.
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
.
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap
|
476 |
-
<link rel="stylesheet" href="https://
|
477 |
-
<
|
478 |
-
<
|
479 |
-
|
480 |
-
|
481 |
-
<script src="https://
|
482 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/
|
483 |
-
<script src="https://
|
484 |
-
<script src="https://
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
}
|
505 |
});
|
|
|
1 |
+
function checkUID(m = "") {
|
2 |
+
const userData = JSON.parse(localStorage.getItem("userData")) || {};
|
3 |
+
const urlNow = window.location.href;
|
4 |
+
const isNotValidUID = userData.length > 0 && !userData.uid;
|
5 |
+
|
6 |
+
const message =
|
7 |
+
m === "addToCart"
|
8 |
+
? "Silakan masuk terlebih dahulu untuk menambahkan produk ke keranjang."
|
9 |
+
: isNotValidUID
|
10 |
+
? "Sesi Anda telah habis. Silakan masuk kembali untuk melanjutkan."
|
11 |
+
: m === "jelajah"
|
12 |
+
? "Anda telah menjelajah selama 5 detik. Silakan masuk terlebih dahulu untuk mendapatkan pengalaman yang lebih baik."
|
13 |
+
: null;
|
14 |
+
|
15 |
+
if (
|
16 |
+
(Object.keys(userData).length === 0 || isNotValidUID) &&
|
17 |
+
!urlNow.includes("profile")
|
18 |
+
) {
|
19 |
+
localStorage.clear();
|
20 |
+
window.location.href =
|
21 |
+
`profile.html?redirect=${urlNow}` +
|
22 |
+
(message ? `&message=${message}` : "");
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
setTimeout(() => {
|
27 |
+
checkUID("jelajah");
|
28 |
+
}, 5000);
|
29 |
+
|
30 |
+
function formatRupiah(number) {
|
31 |
+
return "Rp " + number.toLocaleString("id-ID");
|
32 |
+
}
|
33 |
+
|
34 |
+
async function loadProducts() {
|
35 |
+
const products = await fetchProductsData();
|
36 |
+
|
37 |
+
const productList = document.getElementById("product-list");
|
38 |
+
if (!productList) return;
|
39 |
+
productList.innerHTML = "";
|
40 |
+
products.forEach((product) => {
|
41 |
+
const col = document.createElement("div");
|
42 |
+
col.className = "col-lg-3 col-md-4 col-sm-6";
|
43 |
+
|
44 |
+
const card = document.createElement("div");
|
45 |
+
card.className = "card h-100";
|
46 |
+
|
47 |
+
let thumbnail = "assets/";
|
48 |
+
if (product.files && product.files.length > 0) {
|
49 |
+
thumbnail += product.files[0];
|
50 |
+
}
|
51 |
+
let thumbHTML = "";
|
52 |
+
if (thumbnail.match(/\.(jpg|jpeg|png|gif)$/i)) {
|
53 |
+
thumbHTML = `<img src="${thumbnail}" class="card-img-top" alt="${product.name}">`;
|
54 |
+
} else {
|
55 |
+
thumbHTML = `<img src="img/placeholder.jpg" class="card-img-top" alt="${product.name}">`;
|
56 |
+
}
|
57 |
+
|
58 |
+
const cardBody = document.createElement("div");
|
59 |
+
cardBody.className = "card-body d-flex flex-column";
|
60 |
+
|
61 |
+
const title = document.createElement("h5");
|
62 |
+
title.className = "card-title";
|
63 |
+
title.innerText = product.name;
|
64 |
+
|
65 |
+
const desc = document.createElement("p");
|
66 |
+
desc.className = "card-text";
|
67 |
+
desc.innerText = product.description;
|
68 |
+
|
69 |
+
const price = document.createElement("p");
|
70 |
+
price.className = "card-text fw-bold";
|
71 |
+
price.innerText = formatRupiah(product.price);
|
72 |
+
|
73 |
+
const detailLink = document.createElement("a");
|
74 |
+
detailLink.href = "product.html?id=" + product.id;
|
75 |
+
detailLink.className = "btn btn-primary btn-lg mt-auto";
|
76 |
+
detailLink.innerText = "Lihat Detail";
|
77 |
+
|
78 |
+
const cartBtn = document.createElement("button");
|
79 |
+
cartBtn.className = "btn btn-success btn-lg mt-2";
|
80 |
+
cartBtn.innerText = "Tambah Keranjang";
|
81 |
+
cartBtn.onclick = async function () {
|
82 |
+
await promptQuantityAndAdd(product.id);
|
83 |
+
};
|
84 |
+
|
85 |
+
card.innerHTML = thumbHTML;
|
86 |
+
cardBody.appendChild(title);
|
87 |
+
cardBody.appendChild(desc);
|
88 |
+
cardBody.appendChild(price);
|
89 |
+
cardBody.appendChild(detailLink);
|
90 |
+
cardBody.appendChild(cartBtn);
|
91 |
+
card.appendChild(cardBody);
|
92 |
+
col.appendChild(card);
|
93 |
+
productList.appendChild(col);
|
94 |
+
});
|
95 |
+
}
|
96 |
+
|
97 |
+
async function loadProductDetail() {
|
98 |
+
const params = new URLSearchParams(window.location.search);
|
99 |
+
const productId = parseInt(params.get("id"));
|
100 |
+
const container = document.getElementById("product-detail");
|
101 |
+
if (!container) return;
|
102 |
+
|
103 |
+
if (!productId) {
|
104 |
+
container.innerText = "Produk tidak ditemukan.";
|
105 |
+
return;
|
106 |
+
}
|
107 |
+
|
108 |
+
const products = await fetchProductsData();
|
109 |
+
const product = products.find((p) => p.id === productId);
|
110 |
+
if (!product) {
|
111 |
+
container.innerText = "Produk tidak ditemukan.";
|
112 |
+
return;
|
113 |
+
}
|
114 |
+
let carouselHTML = "";
|
115 |
+
if (product.files && product.files.length > 0) {
|
116 |
+
carouselHTML += `<div id="carouselProduct" class="carousel slide" data-bs-ride="carousel">`;
|
117 |
+
carouselHTML += `<div class="carousel-indicators">`;
|
118 |
+
product.files.forEach((file, index) => {
|
119 |
+
carouselHTML += `<button type="button" data-bs-target="#carouselProduct" data-bs-slide-to="${index}" ${
|
120 |
+
index === 0 ? 'class="active" aria-current="true"' : ""
|
121 |
+
} aria-label="Slide ${index + 1}"></button>`;
|
122 |
+
});
|
123 |
+
carouselHTML += `</div>`;
|
124 |
+
carouselHTML += `<div class="carousel-inner">`;
|
125 |
+
product.files.forEach((file, index) => {
|
126 |
+
file = "assets/" + file;
|
127 |
+
carouselHTML += `<div class="carousel-item ${
|
128 |
+
index === 0 ? "active" : ""
|
129 |
+
}">`;
|
130 |
+
if (file.match(/\.(jpg|jpeg|png|gif)$/i)) {
|
131 |
+
carouselHTML += `<img src="${file}" class="d-block w-100" alt="${product.name}">`;
|
132 |
+
} else if (file.match(/\.(mp4|webm)$/i)) {
|
133 |
+
carouselHTML += `<video class="d-block w-100" controls>
|
134 |
+
<source src="${file}" type="video/mp4">
|
135 |
+
Browser Anda tidak mendukung video.
|
136 |
+
</video>`;
|
137 |
+
}
|
138 |
+
carouselHTML += `</div>`;
|
139 |
+
});
|
140 |
+
carouselHTML += `</div>`;
|
141 |
+
carouselHTML += `<button class="carousel-control-prev" type="button" data-bs-target="#carouselProduct" data-bs-slide="prev">
|
142 |
+
<span class="carousel-control-prev-icon bg-primary rounded-circle p-4" aria-hidden="true"></span>
|
143 |
+
<span class="visually-hidden">Previous</span>
|
144 |
+
</button>
|
145 |
+
<button class="carousel-control-next" type="button" data-bs-target="#carouselProduct" data-bs-slide="next">
|
146 |
+
<span class="carousel-control-next-icon bg-primary rounded-circle p-4" aria-hidden="true"></span>
|
147 |
+
<span class="visually-hidden">Next</span>
|
148 |
+
</button>`;
|
149 |
+
carouselHTML += `</div>`;
|
150 |
+
}
|
151 |
+
container.innerHTML = `
|
152 |
+
<div class="row">
|
153 |
+
<div class="col-md-6">
|
154 |
+
${carouselHTML}
|
155 |
+
</div>
|
156 |
+
<div class="col-md-6">
|
157 |
+
<h2>${product.name}</h2>
|
158 |
+
<p>${product.description}</p>
|
159 |
+
<h4 class="fw-bold">${formatRupiah(product.price)}</h4>
|
160 |
+
<div class="mb-2">
|
161 |
+
<label for="quantity" class="form-label">Jumlah:</label>
|
162 |
+
<input type="number" id="quantity" class="form-control form-control-lg" value="1" min="1" style="max-width:150px;">
|
163 |
+
</div>
|
164 |
+
<button class="btn btn-success btn-lg" onclick="addToCartFromProduct(${
|
165 |
+
product.id
|
166 |
+
})">
|
167 |
+
Tambahkan ke Keranjang
|
168 |
+
</button>
|
169 |
+
</div>
|
170 |
+
</div>
|
171 |
+
`;
|
172 |
+
AOS.refresh();
|
173 |
+
}
|
174 |
+
|
175 |
+
function formatTransactionDate(key) {
|
176 |
+
const parts = key.split("_");
|
177 |
+
if (parts.length !== 7) return key;
|
178 |
+
|
179 |
+
const [year, month, day, hour, minute, second, ms] = parts;
|
180 |
+
|
181 |
+
const dateObj = new Date(
|
182 |
+
year,
|
183 |
+
parseInt(month) - 1,
|
184 |
+
day,
|
185 |
+
hour,
|
186 |
+
minute,
|
187 |
+
second,
|
188 |
+
ms
|
189 |
+
);
|
190 |
+
|
191 |
+
const formattedDate = dateObj.toLocaleDateString("id-ID", {
|
192 |
+
day: "2-digit",
|
193 |
+
month: "long",
|
194 |
+
year: "numeric",
|
195 |
+
});
|
196 |
+
|
197 |
+
const formattedTime = dateObj.toLocaleTimeString("id-ID", {
|
198 |
+
hour: "2-digit",
|
199 |
+
minute: "2-digit",
|
200 |
+
hour12: false,
|
201 |
+
});
|
202 |
+
|
203 |
+
return [formattedDate, formattedTime];
|
204 |
+
}
|
205 |
+
|
206 |
+
async function loadTransactions() {
|
207 |
+
const userData = await getUserData() || {transactions: {}};
|
208 |
+
const transactions = userData.transactions || {};
|
209 |
+
|
210 |
+
if (Object.keys(transactions).length === 0) {
|
211 |
+
document.getElementById("transaction-list").innerHTML =
|
212 |
+
"<p>Anda belum melakukan transaksi apapun. Silahkan beli produk terlebih dahulu.</p>";
|
213 |
+
alert("Anda belum melakukan transaksi apapun. Silahkan beli produk terlebih dahulu.", false)
|
214 |
+
return;
|
215 |
+
}
|
216 |
+
|
217 |
+
const transactionList = document.getElementById("transaction-list");
|
218 |
+
if (!transactionList) return;
|
219 |
+
transactionList.innerHTML = "";
|
220 |
+
|
221 |
+
Object.entries(transactions)
|
222 |
+
.sort((a, b) => {
|
223 |
+
const parseDateFromKey = (key) => {
|
224 |
+
const [year, month, day, hour, minute, second, ms] =
|
225 |
+
key.split("_");
|
226 |
+
return new Date(year, month - 1, day, hour, minute, second, ms);
|
227 |
+
};
|
228 |
+
|
229 |
+
return parseDateFromKey(b[0]) - parseDateFromKey(a[0]);
|
230 |
+
})
|
231 |
+
.forEach(([transactionKey, transaction]) => {
|
232 |
+
let total = 0;
|
233 |
+
|
234 |
+
transaction.products.forEach((product) => {
|
235 |
+
total += product.price * product.total;
|
236 |
+
});
|
237 |
+
|
238 |
+
let paymentIcon = "";
|
239 |
+
switch (transaction.payment_method) {
|
240 |
+
case "bank":
|
241 |
+
paymentIcon = "bi-building";
|
242 |
+
break;
|
243 |
+
case "qr":
|
244 |
+
paymentIcon = "bi-qr-code";
|
245 |
+
break;
|
246 |
+
case "card":
|
247 |
+
paymentIcon = "bi-credit-card";
|
248 |
+
break;
|
249 |
+
default:
|
250 |
+
paymentIcon = "bi-currency-dollar";
|
251 |
+
}
|
252 |
+
|
253 |
+
const col = document.createElement("div");
|
254 |
+
col.className =
|
255 |
+
"transactions-body col-lg-4 col-md-6 col-sm-12 mt-4";
|
256 |
+
|
257 |
+
const card = document.createElement("div");
|
258 |
+
card.className = "card h-100";
|
259 |
+
|
260 |
+
const cardBody = document.createElement("div");
|
261 |
+
cardBody.className = "card-body d-flex flex-column";
|
262 |
+
|
263 |
+
cardBody.style.position = "relative";
|
264 |
+
|
265 |
+
const td = formatTransactionDate(transactionKey);
|
266 |
+
|
267 |
+
cardBody.innerHTML = `
|
268 |
+
<div class="payment-icon-bg">
|
269 |
+
<i class="bi ${paymentIcon}" style="font-size: 2.5rem;"></i>
|
270 |
+
<span class="m-2">Metode: ${
|
271 |
+
transaction.payment_method ? transaction.payment_method : "N/A"
|
272 |
+
}</span>
|
273 |
+
</div>
|
274 |
+
<div class="content">
|
275 |
+
<div class="d-flex justify-content-between align-items-center">
|
276 |
+
<div class="transaction-total fw-bold">
|
277 |
+
Total: ${formatRupiah(total)}
|
278 |
+
</div>
|
279 |
+
</div>
|
280 |
+
<div class="d-flex justify-content-between align-items-center mt-auto">
|
281 |
+
<div class="transaction-date">
|
282 |
+
Tanggal: ${td[0]} <br> Waktu: ${td[1]}
|
283 |
+
</div>
|
284 |
+
</div>
|
285 |
+
</div>
|
286 |
+
`;
|
287 |
+
|
288 |
+
card.appendChild(cardBody);
|
289 |
+
col.appendChild(card);
|
290 |
+
transactionList.appendChild(col);
|
291 |
+
|
292 |
+
col.addEventListener(
|
293 |
+
"click",
|
294 |
+
() =>
|
295 |
+
(window.location.href = `transaction.html?date=${transactionKey}`)
|
296 |
+
);
|
297 |
+
});
|
298 |
+
}
|
299 |
+
|
300 |
+
let transactionKey;
|
301 |
+
|
302 |
+
async function updateActionButtonsPosition() {
|
303 |
+
const wrapper = document.querySelector(".receipt-wrapper");
|
304 |
+
const actionButtons = document.querySelector(".action-buttons");
|
305 |
+
if (!wrapper || !actionButtons) {
|
306 |
+
console.error("Element tidak ditemukan!");
|
307 |
+
return;
|
308 |
+
}
|
309 |
+
|
310 |
+
const receiptHeight = wrapper.getBoundingClientRect().height + 128;
|
311 |
+
actionButtons.setAttribute("style", `top: ${receiptHeight}px !important`);
|
312 |
+
}
|
313 |
+
|
314 |
+
async function loadTransactionDetail() {
|
315 |
+
const params = new URLSearchParams(window.location.search);
|
316 |
+
const transactionKey = params.get("date");
|
317 |
+
const detailsContainer = document.getElementById("transaction-details");
|
318 |
+
const mainElement = document.querySelector("main");
|
319 |
+
|
320 |
+
if (!transactionKey) {
|
321 |
+
detailsContainer.innerHTML = "<p>Transaksi tidak ditemukan.</p>";
|
322 |
+
return;
|
323 |
+
}
|
324 |
+
|
325 |
+
const userData = await getUserData();
|
326 |
+
|
327 |
+
if (!userData.transactions || !userData.transactions[transactionKey]) {
|
328 |
+
mainElement.innerHTML = "<p>Transaksi tidak ditemukan.</p>";
|
329 |
+
return;
|
330 |
+
}
|
331 |
+
|
332 |
+
const transaction = userData.transactions[transactionKey];
|
333 |
+
|
334 |
+
const td = formatTransactionDate(transactionKey);
|
335 |
+
|
336 |
+
let total = 0;
|
337 |
+
transaction.products.forEach((prod) => {
|
338 |
+
total += prod.price * prod.total;
|
339 |
+
});
|
340 |
+
|
341 |
+
let html = `
|
342 |
+
<div class="receipt-header">
|
343 |
+
<h2>TonS E-Commerce</h2>
|
344 |
+
<hr>
|
345 |
+
<p>Sidoarjo</p>
|
346 |
+
<p>Telp: 0896-6804-1554</p>
|
347 |
+
<p>Tanggal: ${td[0]} ${td[1]}</p>
|
348 |
+
</div>
|
349 |
+
<table class="receipt-table">
|
350 |
+
<thead>
|
351 |
+
<tr>
|
352 |
+
<th>No</th>
|
353 |
+
<th>Produk</th>
|
354 |
+
<th>Harga</th>
|
355 |
+
<th>Qty</th>
|
356 |
+
<th>Subtotal</th>
|
357 |
+
</tr>
|
358 |
+
</thead>
|
359 |
+
<tbody>
|
360 |
+
`;
|
361 |
+
|
362 |
+
transaction.products.forEach((prod, index) => {
|
363 |
+
const qty = prod.total;
|
364 |
+
const subtotal = prod.price * qty;
|
365 |
+
html += `
|
366 |
+
<tr>
|
367 |
+
<td>${index + 1}</td>
|
368 |
+
<td>${prod.name}</td>
|
369 |
+
<td>${formatRupiah(prod.price)}</td>
|
370 |
+
<td>${qty}</td>
|
371 |
+
<td>${formatRupiah(subtotal)}</td>
|
372 |
+
</tr>
|
373 |
+
`;
|
374 |
+
});
|
375 |
+
|
376 |
+
html += `
|
377 |
+
<tr class="total-row">
|
378 |
+
<td colspan="4" class="text-end"><strong>Total</strong></td>
|
379 |
+
<td><strong>${formatRupiah(total)}</strong></td>
|
380 |
+
</tr>
|
381 |
+
</tbody>
|
382 |
+
</table>
|
383 |
+
<div class="receipt-footer">
|
384 |
+
<p>Terima kasih telah berbelanja di TonS</p>
|
385 |
+
<p>Harap simpan struk ini sebagai bukti pembelian</p>
|
386 |
+
</div>
|
387 |
+
`;
|
388 |
+
|
389 |
+
detailsContainer.innerHTML = html;
|
390 |
+
|
391 |
+
const wrapper = document.createElement("div");
|
392 |
+
wrapper.className = "receipt-wrapper";
|
393 |
+
wrapper.innerHTML = detailsContainer.innerHTML;
|
394 |
+
detailsContainer.innerHTML = "";
|
395 |
+
detailsContainer.appendChild(wrapper);
|
396 |
+
|
397 |
+
await updateActionButtonsPosition();
|
398 |
+
|
399 |
+
window.addEventListener("resize", async () => {
|
400 |
+
requestAnimationFrame(updateActionButtonsPosition);
|
401 |
+
});
|
402 |
+
}
|
403 |
+
|
404 |
+
document.addEventListener("DOMContentLoaded", async function () {
|
405 |
+
AOS.init();
|
406 |
+
|
407 |
+
if (document.getElementById("product-list")) {
|
408 |
+
loadProducts();
|
409 |
+
}
|
410 |
+
|
411 |
+
if (document.getElementById("product-detail")) {
|
412 |
+
loadProductDetail();
|
413 |
+
}
|
414 |
+
|
415 |
+
if (document.getElementById("transaction-details")) {
|
416 |
+
await loadTransactionDetail();
|
417 |
+
|
418 |
+
await updateActionButtonsPosition();
|
419 |
+
|
420 |
+
window.addEventListener("resize", async () => {
|
421 |
+
requestAnimationFrame(updateActionButtonsPosition);
|
422 |
+
});
|
423 |
+
|
424 |
+
document
|
425 |
+
.getElementById("download-pdf")
|
426 |
+
.addEventListener("click", async () => {
|
427 |
+
const mainElement = document.getElementById(
|
428 |
+
"transaction-details"
|
429 |
+
);
|
430 |
+
const receiptWrapperElement =
|
431 |
+
document.querySelector(".receipt-wrapper");
|
432 |
+
|
433 |
+
const widthElement =
|
434 |
+
receiptWrapperElement.offsetWidth -
|
435 |
+
receiptWrapperElement.offsetLeft;
|
436 |
+
const heightElement =
|
437 |
+
receiptWrapperElement.offsetHeight -
|
438 |
+
receiptWrapperElement.offsetTop;
|
439 |
+
|
440 |
+
const params = new URLSearchParams(window.location.search);
|
441 |
+
const transactionKey = params.get("date");
|
442 |
+
|
443 |
+
try {
|
444 |
+
await html2pdf()
|
445 |
+
.from(mainElement)
|
446 |
+
.set({
|
447 |
+
margin: 10,
|
448 |
+
filename: `receipt_${transactionKey}.pdf`,
|
449 |
+
image: { type: "png", quality: 3 },
|
450 |
+
html2canvas: { scale: 3, useCORS: true },
|
451 |
+
jsPDF: {
|
452 |
+
unit: "px",
|
453 |
+
format: [widthElement, heightElement],
|
454 |
+
orientation: "portrait",
|
455 |
+
},
|
456 |
+
})
|
457 |
+
.toPdf()
|
458 |
+
.save();
|
459 |
+
} catch (error) {
|
460 |
+
console.error("Error generating PDF:", error);
|
461 |
+
}
|
462 |
+
});
|
463 |
+
|
464 |
+
document
|
465 |
+
.getElementById("print-receipt")
|
466 |
+
.addEventListener("click", async () => {
|
467 |
+
const mainElement = document.getElementById(
|
468 |
+
"transaction-details"
|
469 |
+
);
|
470 |
+
|
471 |
+
const lastMainElement = mainElement.innerHTML;
|
472 |
+
|
473 |
+
mainElement.innerHTML += `
|
474 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
|
475 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
476 |
+
<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css" />
|
477 |
+
<script src="https://printjs-4de6.kxcdn.com/print.min.css"></script>
|
478 |
+
<link rel="stylesheet" href="stylesheet/styles.css">
|
479 |
+
|
480 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" defer></script>
|
481 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.2/html2pdf.bundle.min.js"></script>
|
482 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.13/html-to-image.js"></script>
|
483 |
+
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js" defer></script>
|
484 |
+
<script src="https://printjs-4de6.kxcdn.com/print.min.js"></script>
|
485 |
+
`;
|
486 |
+
|
487 |
+
await printJS("transaction-details", "html");
|
488 |
+
|
489 |
+
mainElement.innerHTML = lastMainElement;
|
490 |
+
});
|
491 |
+
}
|
492 |
+
|
493 |
+
if (document.getElementById("transaction-list")) {
|
494 |
+
loadTransactions();
|
495 |
+
}
|
496 |
+
|
497 |
+
let backButton = document.getElementById("backButton");
|
498 |
+
if (backButton) {
|
499 |
+
backButton.addEventListener("click", (e) => {
|
500 |
+
e.preventDefault();
|
501 |
+
window.history.back();
|
502 |
+
});
|
503 |
+
}
|
|
|
504 |
});
|
javascript/profile.js
CHANGED
@@ -1,243 +1,260 @@
|
|
1 |
-
const CLIENT_ID =
|
2 |
-
"819389601271-fl7jsirpdkepkcou78erki5hmi30rrbm.apps.googleusercontent.com";
|
3 |
-
let tokenClient;
|
4 |
-
let accessToken;
|
5 |
-
|
6 |
-
async function handleCredentialResponse(response) {
|
7 |
-
const idToken = response.credential;
|
8 |
-
const payload = JSON.parse(atob(idToken.split(".")[1]));
|
9 |
-
const uid = payload.sub;
|
10 |
-
|
11 |
-
localStorage.setItem("userData", JSON.stringify({
|
12 |
-
uid: uid,
|
13 |
-
name: payload.name,
|
14 |
-
photo_profile: payload.picture,
|
15 |
-
email: payload.email,
|
16 |
-
}));
|
17 |
-
|
18 |
-
let userData = await getUserData();
|
19 |
-
if (!userData || Object.keys(userData).length === 0) {
|
20 |
-
userData = {
|
21 |
-
uid: uid,
|
22 |
-
name: payload.name,
|
23 |
-
photo_profile: payload.picture,
|
24 |
-
email: payload.email,
|
25 |
-
};
|
26 |
-
}
|
27 |
-
|
28 |
-
await updateUserDataToGitHub(userData);
|
29 |
-
|
30 |
-
localStorage.setItem("cart", JSON.stringify(userData.cart || []));
|
31 |
-
|
32 |
-
window.location.reload();
|
33 |
-
}
|
34 |
-
|
35 |
-
function handleAccessTokenResponse(response) {
|
36 |
-
accessToken = response.access_token;
|
37 |
-
console.log("Access Token:", accessToken);
|
38 |
-
fetchUserAdditionalInfo();
|
39 |
-
}
|
40 |
-
|
41 |
-
async function fetchUserAdditionalInfo() {
|
42 |
-
let userData = JSON.parse(localStorage.getItem("userData"));
|
43 |
-
if (userData && userData.uid && accessToken) {
|
44 |
-
try {
|
45 |
-
const res = await fetch(
|
46 |
-
"https://people.googleapis.com/v1/people/me?personFields=phoneNumbers,addresses",
|
47 |
-
{
|
48 |
-
headers: {
|
49 |
-
Authorization: `Bearer ${accessToken}`,
|
50 |
-
},
|
51 |
-
}
|
52 |
-
);
|
53 |
-
if (!res.ok)
|
54 |
-
throw new Error(
|
55 |
-
"Gagal mendapatkan data tambahan: " + res.status
|
56 |
-
);
|
57 |
-
const additionalData = await res.json();
|
58 |
-
console.log("Data tambahan:", additionalData);
|
59 |
-
|
60 |
-
if (
|
61 |
-
additionalData.phoneNumbers &&
|
62 |
-
additionalData.phoneNumbers.length > 0
|
63 |
-
) {
|
64 |
-
userData.phone = additionalData.phoneNumbers[0].value;
|
65 |
-
}
|
66 |
-
if (
|
67 |
-
additionalData.addresses &&
|
68 |
-
additionalData.addresses.length > 0
|
69 |
-
) {
|
70 |
-
userData.address = additionalData.addresses[0].formattedValue;
|
71 |
-
}
|
72 |
-
localStorage.setItem("userData", JSON.stringify(userData));
|
73 |
-
console.log("User Data Lengkap:", userData);
|
74 |
-
} catch (err) {
|
75 |
-
console.error("Error fetching additional info:", err);
|
76 |
-
}
|
77 |
-
} else {
|
78 |
-
console.warn("Data UID atau access token tidak tersedia.");
|
79 |
-
}
|
80 |
-
}
|
81 |
-
|
82 |
-
async function renderGoogleLoginButton() {
|
83 |
-
const container = document.getElementById("profile-container");
|
84 |
-
|
85 |
-
document
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
const
|
90 |
-
const
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
}
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
<
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
243 |
});
|
|
|
1 |
+
const CLIENT_ID =
|
2 |
+
"819389601271-fl7jsirpdkepkcou78erki5hmi30rrbm.apps.googleusercontent.com";
|
3 |
+
let tokenClient;
|
4 |
+
let accessToken;
|
5 |
+
|
6 |
+
async function handleCredentialResponse(response) {
|
7 |
+
const idToken = response.credential;
|
8 |
+
const payload = JSON.parse(atob(idToken.split(".")[1]));
|
9 |
+
const uid = payload.sub;
|
10 |
+
|
11 |
+
localStorage.setItem("userData", JSON.stringify({
|
12 |
+
uid: uid,
|
13 |
+
name: payload.name,
|
14 |
+
photo_profile: payload.picture,
|
15 |
+
email: payload.email,
|
16 |
+
}));
|
17 |
+
|
18 |
+
let userData = await getUserData();
|
19 |
+
if (!userData || Object.keys(userData).length === 0) {
|
20 |
+
userData = {
|
21 |
+
uid: uid,
|
22 |
+
name: payload.name,
|
23 |
+
photo_profile: payload.picture,
|
24 |
+
email: payload.email,
|
25 |
+
};
|
26 |
+
}
|
27 |
+
|
28 |
+
await updateUserDataToGitHub(userData);
|
29 |
+
|
30 |
+
localStorage.setItem("cart", JSON.stringify(userData.cart || []));
|
31 |
+
|
32 |
+
window.location.reload();
|
33 |
+
}
|
34 |
+
|
35 |
+
function handleAccessTokenResponse(response) {
|
36 |
+
accessToken = response.access_token;
|
37 |
+
console.log("Access Token:", accessToken);
|
38 |
+
fetchUserAdditionalInfo();
|
39 |
+
}
|
40 |
+
|
41 |
+
async function fetchUserAdditionalInfo() {
|
42 |
+
let userData = JSON.parse(localStorage.getItem("userData"));
|
43 |
+
if (userData && userData.uid && accessToken) {
|
44 |
+
try {
|
45 |
+
const res = await fetch(
|
46 |
+
"https://people.googleapis.com/v1/people/me?personFields=phoneNumbers,addresses",
|
47 |
+
{
|
48 |
+
headers: {
|
49 |
+
Authorization: `Bearer ${accessToken}`,
|
50 |
+
},
|
51 |
+
}
|
52 |
+
);
|
53 |
+
if (!res.ok)
|
54 |
+
throw new Error(
|
55 |
+
"Gagal mendapatkan data tambahan: " + res.status
|
56 |
+
);
|
57 |
+
const additionalData = await res.json();
|
58 |
+
console.log("Data tambahan:", additionalData);
|
59 |
+
|
60 |
+
if (
|
61 |
+
additionalData.phoneNumbers &&
|
62 |
+
additionalData.phoneNumbers.length > 0
|
63 |
+
) {
|
64 |
+
userData.phone = additionalData.phoneNumbers[0].value;
|
65 |
+
}
|
66 |
+
if (
|
67 |
+
additionalData.addresses &&
|
68 |
+
additionalData.addresses.length > 0
|
69 |
+
) {
|
70 |
+
userData.address = additionalData.addresses[0].formattedValue;
|
71 |
+
}
|
72 |
+
localStorage.setItem("userData", JSON.stringify(userData));
|
73 |
+
console.log("User Data Lengkap:", userData);
|
74 |
+
} catch (err) {
|
75 |
+
console.error("Error fetching additional info:", err);
|
76 |
+
}
|
77 |
+
} else {
|
78 |
+
console.warn("Data UID atau access token tidak tersedia.");
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
async function renderGoogleLoginButton() {
|
83 |
+
const container = document.getElementById("profile-container");
|
84 |
+
|
85 |
+
document.querySelectorAll("#googleSignInButton").forEach((elm) => elm.remove());
|
86 |
+
|
87 |
+
const urlNow = window.location.href;
|
88 |
+
const urlParams = new URLSearchParams(window.location.search);
|
89 |
+
const redirectUrl = urlParams.get("redirect") || urlNow;
|
90 |
+
const message =
|
91 |
+
urlParams.get("message") ||
|
92 |
+
"Silakan masuk ke akun Anda terlebih dahulu untuk melanjutkan:";
|
93 |
+
|
94 |
+
container.innerHTML = `
|
95 |
+
<p class="text-center" style="text-decoration: underline; font-style: italic;">${message}</p>
|
96 |
+
<span class="text-center m-4">Klik tombol di bawah ini untuk masuk:</span>
|
97 |
+
<div id="googleSignInButton"></div>
|
98 |
+
`;
|
99 |
+
|
100 |
+
if (message.trim()) alert(message, false);
|
101 |
+
|
102 |
+
async function loadGoogleAPI(callback) {
|
103 |
+
if (typeof google !== "undefined") {
|
104 |
+
callback();
|
105 |
+
return;
|
106 |
+
}
|
107 |
+
|
108 |
+
console.log("Memuat Google API...");
|
109 |
+
const script = document.createElement("script");
|
110 |
+
script.src = "https://accounts.google.com/gsi/client";
|
111 |
+
script.async = true;
|
112 |
+
script.defer = true;
|
113 |
+
|
114 |
+
script.onload = function () {
|
115 |
+
console.log("Google API berhasil dimuat.");
|
116 |
+
if (typeof google !== "undefined") {
|
117 |
+
callback();
|
118 |
+
} else {
|
119 |
+
console.error("Google API gagal dimuat.");
|
120 |
+
alert("Jika tombol masuk tidak tampil, silakan muat ulang halaman ini", false);
|
121 |
+
}
|
122 |
+
};
|
123 |
+
|
124 |
+
script.onerror = function () {
|
125 |
+
console.error("Gagal memuat Google API.");
|
126 |
+
alert("Gagal memuat Google Login. Coba gunakan browser lain atau muat ulang halaman ini.", false);
|
127 |
+
};
|
128 |
+
|
129 |
+
document.head.appendChild(script);
|
130 |
+
}
|
131 |
+
|
132 |
+
function initializeGoogleLogin() {
|
133 |
+
google.accounts.id.initialize({
|
134 |
+
client_id: CLIENT_ID,
|
135 |
+
callback: async (response) => {
|
136 |
+
await handleCredentialResponse(response);
|
137 |
+
window.location.href = redirectUrl;
|
138 |
+
},
|
139 |
+
auto_select: false,
|
140 |
+
button_auto_select: false,
|
141 |
+
scope: "email profile https://www.googleapis.com/auth/user.phonenumbers.read https://www.googleapis.com/auth/user.addresses.read",
|
142 |
+
});
|
143 |
+
|
144 |
+
google.accounts.id.prompt();
|
145 |
+
|
146 |
+
google.accounts.id.renderButton(
|
147 |
+
document.getElementById("googleSignInButton"),
|
148 |
+
{ theme: "outline", size: "large" }
|
149 |
+
);
|
150 |
+
}
|
151 |
+
|
152 |
+
loadGoogleAPI(() => {
|
153 |
+
initializeGoogleLogin();
|
154 |
+
});
|
155 |
+
}
|
156 |
+
|
157 |
+
async function initProfile() {
|
158 |
+
const userData = await getUserData();
|
159 |
+
if (userData && Object.keys(userData).length > 0) {
|
160 |
+
renderProfileForm(userData);
|
161 |
+
} else {
|
162 |
+
await renderGoogleLoginButton();
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
function renderProfileForm(userData) {
|
167 |
+
const container = document.getElementById("profile-container");
|
168 |
+
container.innerHTML = `
|
169 |
+
<form id="profileForm" class="fs-5">
|
170 |
+
<div class="mb-2 d-flex flex-row align-items-center justify-content-center">
|
171 |
+
<img id="photo_profile" class="img-fluid img-thumbnail rounded-circle" src=${userData.photo_profile}>
|
172 |
+
<input class="d-none" type="file" id="fileInput" accept="image/*">
|
173 |
+
</div>
|
174 |
+
<div class="mb-2">
|
175 |
+
<input class="form-control form-control-lg" type="text" id="name" placeholder="Nama" value="${
|
176 |
+
userData.name || ""
|
177 |
+
}" required>
|
178 |
+
</div>
|
179 |
+
<div class="mb-2">
|
180 |
+
<input class="form-control form-control-lg" type="email" id="email" placeholder="Email" value="${
|
181 |
+
userData.email || ""
|
182 |
+
}" required disabled>
|
183 |
+
</div>
|
184 |
+
<div class="mb-2">
|
185 |
+
<input class="form-control form-control-lg" type="text" id="phone" placeholder="No. HP" value="${
|
186 |
+
userData.phone || ""
|
187 |
+
}" required>
|
188 |
+
</div>
|
189 |
+
<div class="mb-2">
|
190 |
+
<input class="form-control form-control-lg" type="text" id="address" placeholder="Alamat" value="${
|
191 |
+
userData.address || ""
|
192 |
+
}" required>
|
193 |
+
</div>
|
194 |
+
<br>
|
195 |
+
<button class="btn btn-primary btn-lg" type="button" id="saveProfile">Simpan</button>
|
196 |
+
<br><br>
|
197 |
+
<button class="btn btn-danger btn-lg" type="button" id="logout">Keluar Akun</button>
|
198 |
+
</form>`;
|
199 |
+
|
200 |
+
document.getElementById("photo_profile").addEventListener("click", function () {
|
201 |
+
document.getElementById("fileInput").click();
|
202 |
+
});
|
203 |
+
|
204 |
+
document.getElementById("fileInput").addEventListener("change", function (event) {
|
205 |
+
const file = event.target.files[0];
|
206 |
+
if (file) {
|
207 |
+
if (file.size > 1 * 1024 * 1024) {
|
208 |
+
alert("Ukuran file terlalu besar! Maksimal 1MB.", false);
|
209 |
+
return;
|
210 |
+
}
|
211 |
+
|
212 |
+
const reader = new FileReader();
|
213 |
+
reader.onload = function (e) {
|
214 |
+
document.getElementById("photo_profile").src = e.target.result;
|
215 |
+
};
|
216 |
+
reader.readAsDataURL(file);
|
217 |
+
}
|
218 |
+
});
|
219 |
+
|
220 |
+
document
|
221 |
+
.getElementById("saveProfile")
|
222 |
+
.addEventListener("click", async function () {
|
223 |
+
const updatedUser = {
|
224 |
+
uid: userData.uid,
|
225 |
+
name: document.getElementById("name").value.trim(),
|
226 |
+
email: userData.email,
|
227 |
+
phone: document.getElementById("phone").value.trim(),
|
228 |
+
address: document.getElementById("address").value.trim(),
|
229 |
+
photo_profile: document.getElementById("photo_profile").src,
|
230 |
+
cart: userData.cart ? userData.cart : [],
|
231 |
+
transactions: userData.transactions
|
232 |
+
? userData.transactions
|
233 |
+
: [],
|
234 |
+
};
|
235 |
+
|
236 |
+
if (
|
237 |
+
!updatedUser.name ||
|
238 |
+
!updatedUser.email ||
|
239 |
+
!updatedUser.phone ||
|
240 |
+
!updatedUser.address
|
241 |
+
) {
|
242 |
+
alert("Semua input wajib diisi!", false);
|
243 |
+
return;
|
244 |
+
}
|
245 |
+
|
246 |
+
localStorage.setItem("userData", JSON.stringify(updatedUser));
|
247 |
+
await updateUserDataToGitHub(updatedUser);
|
248 |
+
alert("Profil berhasil disimpan!", false);
|
249 |
+
});
|
250 |
+
|
251 |
+
document.getElementById("logout").addEventListener("click", function () {
|
252 |
+
localStorage.removeItem("userData");
|
253 |
+
window.location.reload();
|
254 |
+
});
|
255 |
+
}
|
256 |
+
|
257 |
+
document.addEventListener("DOMContentLoaded", function () {
|
258 |
+
AOS.init();
|
259 |
+
initProfile();
|
260 |
});
|
javascript/search.js
CHANGED
@@ -19,6 +19,9 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
19 |
return;
|
20 |
}
|
21 |
|
|
|
|
|
|
|
22 |
if (productQuery) {
|
23 |
const products = await fetchProductsData();
|
24 |
|
@@ -102,9 +105,11 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
102 |
}
|
103 |
|
104 |
if (transactionQuery) {
|
105 |
-
const userData = await getUserData();
|
106 |
-
|
107 |
-
|
|
|
|
|
108 |
const parseDateFromKey = (key) => {
|
109 |
const [year, month, day, hour, minute, second, ms] = key.split("_");
|
110 |
return new Date(year, month - 1, day, hour, minute, second, ms);
|
@@ -134,11 +139,13 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
134 |
}
|
135 |
}
|
136 |
|
|
|
|
|
137 |
return (
|
138 |
-
key.includes(
|
139 |
-
td[0].includes(
|
140 |
-
td[1].includes(
|
141 |
-
pm.includes(
|
142 |
total.toString().includes(transactionQuery) ||
|
143 |
Object.values(productCount).join(",").includes(transactionQuery)
|
144 |
);
|
@@ -227,12 +234,14 @@ document.addEventListener("DOMContentLoaded", async function () {
|
|
227 |
messageElem.textContent = "Transaksi tidak ditemukan.";
|
228 |
messageElem.className = "text-center";
|
229 |
resultList.appendChild(messageElem);
|
|
|
230 |
}
|
231 |
} else {
|
232 |
const messageElem = document.createElement("p");
|
233 |
-
messageElem.textContent = "Anda belum melakukan transaksi apapun.";
|
234 |
messageElem.className = "text-center";
|
235 |
resultList.appendChild(messageElem);
|
|
|
236 |
}
|
237 |
}
|
238 |
-
});
|
|
|
19 |
return;
|
20 |
}
|
21 |
|
22 |
+
const searchInput = document.getElementById("searchInput")
|
23 |
+
if (searchInput) searchInput.value = (productQuery ? productQuery : "") + " " + (transactionQuery ? transactionQuery : "");
|
24 |
+
|
25 |
if (productQuery) {
|
26 |
const products = await fetchProductsData();
|
27 |
|
|
|
105 |
}
|
106 |
|
107 |
if (transactionQuery) {
|
108 |
+
const userData = await getUserData() || {transactions: {}};
|
109 |
+
const transactions = userData.transactions || {};
|
110 |
+
|
111 |
+
if (Object.keys(transactions).length > 0) {
|
112 |
+
const entries = Object.entries(transactions).sort((a, b) => {
|
113 |
const parseDateFromKey = (key) => {
|
114 |
const [year, month, day, hour, minute, second, ms] = key.split("_");
|
115 |
return new Date(year, month - 1, day, hour, minute, second, ms);
|
|
|
139 |
}
|
140 |
}
|
141 |
|
142 |
+
const lowerQuery = transactionQuery.toLowerCase();
|
143 |
+
|
144 |
return (
|
145 |
+
key.toLowerCase().includes(lowerQuery) ||
|
146 |
+
td[0].toLowerCase().includes(lowerQuery) ||
|
147 |
+
td[1].toLowerCase.includes(lowerQuery) ||
|
148 |
+
pm.toLowerCase.includes(lowerQuery) ||
|
149 |
total.toString().includes(transactionQuery) ||
|
150 |
Object.values(productCount).join(",").includes(transactionQuery)
|
151 |
);
|
|
|
234 |
messageElem.textContent = "Transaksi tidak ditemukan.";
|
235 |
messageElem.className = "text-center";
|
236 |
resultList.appendChild(messageElem);
|
237 |
+
alert(messageElem.textContent, false);
|
238 |
}
|
239 |
} else {
|
240 |
const messageElem = document.createElement("p");
|
241 |
+
messageElem.textContent = "Anda belum melakukan transaksi apapun. Silahkan beli produk terlebih dahulu.";
|
242 |
messageElem.className = "text-center";
|
243 |
resultList.appendChild(messageElem);
|
244 |
+
alert(messageElem.textContent, false);
|
245 |
}
|
246 |
}
|
247 |
+
});
|