Adityadn commited on
Commit
b28567d
·
verified ·
1 Parent(s): 100ce4f

Upload 7 files

Browse files
Files changed (4) hide show
  1. javascript/cart.js +184 -184
  2. javascript/main.js +503 -504
  3. javascript/profile.js +259 -242
  4. 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, 100);
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
- // Tambahkan produk sesuai newQty
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 produk 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();
208
- const transactions = userData.transactions || {};
209
-
210
- if (!transactions || Object.keys(transactions).length === 0) {
211
- document.getElementById("transaction-details").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
- // Buat HTML struk
342
- let html = `
343
- <div class="receipt-header">
344
- <h2>TonS E-Commerce</h2>
345
- <hr>
346
- <p>Sidoarjo</p>
347
- <p>Telp: 0896-6804-1554</p>
348
- <p>Tanggal: ${td[0]} ${td[1]}</p>
349
- </div>
350
- <table class="receipt-table">
351
- <thead>
352
- <tr>
353
- <th>No</th>
354
- <th>Produk</th>
355
- <th>Harga</th>
356
- <th>Qty</th>
357
- <th>Subtotal</th>
358
- </tr>
359
- </thead>
360
- <tbody>
361
- `;
362
-
363
- transaction.products.forEach((prod, index) => {
364
- const qty = prod.total;
365
- const subtotal = prod.price * qty;
366
- html += `
367
- <tr>
368
- <td>${index + 1}</td>
369
- <td>${prod.name}</td>
370
- <td>${formatRupiah(prod.price)}</td>
371
- <td>${qty}</td>
372
- <td>${formatRupiah(subtotal)}</td>
373
- </tr>
374
- `;
375
- });
376
-
377
- html += `
378
- <tr class="total-row">
379
- <td colspan="4" class="text-end"><strong>Total</strong></td>
380
- <td><strong>${formatRupiah(total)}</strong></td>
381
- </tr>
382
- </tbody>
383
- </table>
384
- <div class="receipt-footer">
385
- <p>Terima kasih telah berbelanja di TonS</p>
386
- <p>Harap simpan struk ini sebagai bukti pembelian</p>
387
- </div>
388
- `;
389
-
390
- detailsContainer.innerHTML = html;
391
-
392
- const wrapper = document.createElement("div");
393
- wrapper.className = "receipt-wrapper";
394
- wrapper.innerHTML = detailsContainer.innerHTML;
395
- detailsContainer.innerHTML = "";
396
- detailsContainer.appendChild(wrapper);
397
-
398
- await updateActionButtonsPosition();
399
-
400
- window.addEventListener("resize", async () => {
401
- requestAnimationFrame(updateActionButtonsPosition);
402
- });
403
- }
404
-
405
- document.addEventListener("DOMContentLoaded", async function () {
406
- AOS.init();
407
-
408
- if (document.getElementById("product-list")) {
409
- loadProducts();
410
- }
411
-
412
- if (document.getElementById("product-detail")) {
413
- loadProductDetail();
414
- }
415
-
416
- if (document.getElementById("transaction-details")) {
417
- await loadTransactionDetail();
418
-
419
- await updateActionButtonsPosition();
420
-
421
- window.addEventListener("resize", async () => {
422
- requestAnimationFrame(updateActionButtonsPosition);
423
- });
424
-
425
- document
426
- .getElementById("download-pdf")
427
- .addEventListener("click", async () => {
428
- const mainElement = document.getElementById(
429
- "transaction-details"
430
- );
431
- const receiptWrapperElement =
432
- document.querySelector(".receipt-wrapper");
433
-
434
- const widthElement =
435
- receiptWrapperElement.offsetWidth -
436
- receiptWrapperElement.offsetLeft;
437
- const heightElement =
438
- receiptWrapperElement.offsetHeight -
439
- receiptWrapperElement.offsetTop;
440
-
441
- const params = new URLSearchParams(window.location.search);
442
- const transactionKey = params.get("date");
443
-
444
- try {
445
- await html2pdf()
446
- .from(mainElement)
447
- .set({
448
- margin: 10,
449
- filename: `receipt_${transactionKey}.pdf`,
450
- image: { type: "png", quality: 3 },
451
- html2canvas: { scale: 3, useCORS: true },
452
- jsPDF: {
453
- unit: "px",
454
- format: [widthElement, heightElement],
455
- orientation: "portrait",
456
- },
457
- })
458
- .toPdf()
459
- .save();
460
- } catch (error) {
461
- console.error("Error generating PDF:", error);
462
- }
463
- });
464
-
465
- document
466
- .getElementById("print-receipt")
467
- .addEventListener("click", async () => {
468
- const mainElement = document.getElementById(
469
- "transaction-details"
470
- );
471
-
472
- const lastMainElement = mainElement.innerHTML;
473
-
474
- mainElement.innerHTML += `
475
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
476
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
477
- <link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css" />
478
- <script src="https://printjs-4de6.kxcdn.com/print.min.css"></script>
479
- <link rel="stylesheet" href="stylesheet/styles.css">
480
-
481
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" defer></script>
482
- <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.2/html2pdf.bundle.min.js"></script>
483
- <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.13/html-to-image.js"></script>
484
- <script src="https://unpkg.com/aos@2.3.1/dist/aos.js" defer></script>
485
- <script src="https://printjs-4de6.kxcdn.com/print.min.js"></script>
486
- `;
487
-
488
- await printJS("transaction-details", "html");
489
-
490
- mainElement.innerHTML = lastMainElement;
491
- });
492
- }
493
-
494
- if (document.getElementById("transaction-list")) {
495
- loadTransactions();
496
- }
497
-
498
- let backButton = document.getElementById("backButton");
499
- if (backButton) {
500
- backButton.addEventListener("click", (e) => {
501
- e.preventDefault();
502
- window.history.back();
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
- .querySelectorAll("#googleSignInButton")
87
- .forEach((elm) => elm.remove());
88
-
89
- const urlNow = window.location.href;
90
- const urlParams = new URLSearchParams(window.location.search);
91
- const redirectUrl = urlParams.get("redirect") || urlNow;
92
- const message =
93
- urlParams.get("message") ||
94
- "Silakan masuk ke akun Anda terlebih dahulu untuk melanjutkan:";
95
-
96
- container.innerHTML = `
97
- <p class="text-center" style="text-decoration: underline; font-style: italic;">${message}</p>
98
- <span class="text-center m-4">Klik tombol di bawah ini untuk masuk:</span>
99
- <div id="googleSignInButton"></div>
100
- `;
101
-
102
- if (message.trim()) alert(message, false);
103
-
104
- let isGoogle = false;
105
- do {
106
- try {
107
- google.accounts.id.initialize({
108
- client_id: CLIENT_ID,
109
- callback: async (response) => {
110
- await handleCredentialResponse(response);
111
- window.location.href = redirectUrl;
112
- },
113
- auto_select: false,
114
- button_auto_select: false,
115
- scope: "email profile https://www.googleapis.com/auth/user.phonenumbers.read https://www.googleapis.com/auth/user.addresses.read",
116
- });
117
-
118
- google.accounts.id.prompt();
119
-
120
- google.accounts.id.renderButton(
121
- document.getElementById("googleSignInButton"),
122
- { theme: "outline", size: "large" }
123
- );
124
-
125
- isGoogle = true;
126
- } catch (e) {
127
- console.error("Error initializing Google login:", e);
128
- alert("Jika tombol masuk tidak tampil, silahkan muat ulang halaman ini", false)
129
-
130
- container.innerHTML += `
131
- <script src="https://accounts.google.com/gsi/client" async defer></script>
132
- <script src="https://accounts.google.com/gsi/oauth2" async defer></script>
133
- `;
134
- }
135
-
136
- await new Promise((resolve) => setTimeout(resolve, 100));
137
- } while (!isGoogle);
138
- }
139
-
140
- async function initProfile() {
141
- const userData = await getUserData();
142
- if (userData && Object.keys(userData).length > 0) {
143
- renderProfileForm(userData);
144
- } else {
145
- await renderGoogleLoginButton();
146
- }
147
- }
148
-
149
- function renderProfileForm(userData) {
150
- const container = document.getElementById("profile-container");
151
- container.innerHTML = `
152
- <form id="profileForm" class="fs-5">
153
- <div class="mb-2 d-flex flex-row align-items-center justify-content-center">
154
- <img id="photo_profile" class="img-fluid img-thumbnail rounded-circle" src=${userData.photo_profile}>
155
- <input class="d-none" type="file" id="fileInput" accept="image/*">
156
- </div>
157
- <div class="mb-2">
158
- <input class="form-control form-control-lg" type="text" id="name" placeholder="Nama" value="${
159
- userData.name || ""
160
- }" required>
161
- </div>
162
- <div class="mb-2">
163
- <input class="form-control form-control-lg" type="email" id="email" placeholder="Email" value="${
164
- userData.email || ""
165
- }" required disabled>
166
- </div>
167
- <div class="mb-2">
168
- <input class="form-control form-control-lg" type="text" id="phone" placeholder="No. HP" value="${
169
- userData.phone || ""
170
- }" required>
171
- </div>
172
- <div class="mb-2">
173
- <input class="form-control form-control-lg" type="text" id="address" placeholder="Alamat" value="${
174
- userData.address || ""
175
- }" required>
176
- </div>
177
- <br>
178
- <button class="btn btn-primary btn-lg" type="button" id="saveProfile">Simpan</button>
179
- <br><br>
180
- <button class="btn btn-danger btn-lg" type="button" id="logout">Keluar Akun</button>
181
- </form>`;
182
-
183
- document.getElementById("photo_profile").addEventListener("click", function () {
184
- document.getElementById("fileInput").click();
185
- });
186
-
187
- document.getElementById("fileInput").addEventListener("change", function (event) {
188
- const file = event.target.files[0];
189
- if (file) {
190
- if (file.size > 1 * 1024 * 1024) {
191
- alert("Ukuran file terlalu besar! Maksimal 1MB.", false);
192
- return;
193
- }
194
-
195
- const reader = new FileReader();
196
- reader.onload = function (e) {
197
- document.getElementById("photo_profile").src = e.target.result;
198
- };
199
- reader.readAsDataURL(file);
200
- }
201
- });
202
-
203
- document
204
- .getElementById("saveProfile")
205
- .addEventListener("click", async function () {
206
- const updatedUser = {
207
- uid: userData.uid,
208
- name: document.getElementById("name").value.trim(),
209
- email: userData.email,
210
- phone: document.getElementById("phone").value.trim(),
211
- address: document.getElementById("address").value.trim(),
212
- photo_profile: document.getElementById("photo_profile").src,
213
- cart: userData.cart ? userData.cart : [],
214
- transactions: userData.transactions
215
- ? userData.transactions
216
- : [],
217
- };
218
-
219
- if (
220
- !updatedUser.name ||
221
- !updatedUser.email ||
222
- !updatedUser.phone ||
223
- !updatedUser.address
224
- ) {
225
- alert("Semua input wajib diisi!", false);
226
- return;
227
- }
228
-
229
- localStorage.setItem("userData", JSON.stringify(updatedUser));
230
- await updateUserDataToGitHub(updatedUser);
231
- alert("Profil berhasil disimpan!", false);
232
- });
233
-
234
- document.getElementById("logout").addEventListener("click", function () {
235
- localStorage.removeItem("userData");
236
- window.location.reload();
237
- });
238
- }
239
-
240
- document.addEventListener("DOMContentLoaded", function () {
241
- AOS.init();
242
- initProfile();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- if (userData.transactions) {
107
- const entries = Object.entries(userData.transactions).sort((a, b) => {
 
 
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(transactionQuery) ||
139
- td[0].includes(transactionQuery) ||
140
- td[1].includes(transactionQuery) ||
141
- pm.includes(transactionQuery) ||
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
+ });