bid-extension/content.js

406 lines
9.9 KiB
JavaScript

const CONFIG = {
// API_BASE_URL: "http://localhost:4000/api/v1",
API_BASE_URL: "https://bids.apactech.io/api/v1",
};
let PREV_DATA = null;
function removeFalsyValues(obj, excludeKeys = []) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value || excludeKeys.includes(key)) {
acc[key] = value;
}
return acc;
}, {});
}
function extractDomain(url) {
try {
const parsedUrl = new URL(url);
return parsedUrl.origin;
} catch (error) {
return null;
}
}
const webs = {
grays: "https://www.grays.com",
langtons: "https://www.langtons.com.au",
lawsons: "https://www.lawsons.com.au",
pickles: "https://www.pickles.com.au",
allbids: "https://www.allbids.com.au",
};
function extractModelId(url) {
switch (extractDomain(url)) {
case webs.grays: {
const match = url.match(/\/lot\/([\d-]+)\//);
return match ? match[1] : null;
}
case webs.langtons: {
const match = url.match(/auc-var-\d+/);
return match[0];
}
case webs.lawsons: {
const match = url.split("_");
return match ? match[1] : null;
}
case webs.pickles: {
const model = url.split("/").pop();
return model ? model : null;
}
case webs.allbids: {
const match = url.match(/-(\d+)(?:[\?#]|$)/);
return match ? match[1] : null;
}
}
}
const isValidUrl = (url) => {
return !!Object.keys(webs).find((item) => url.includes(webs[item]));
};
const showPage = async (pageLink = "pages/popup/popup.html") => {
const url = window.location.href; // sửa lỗi ở đây
if (!isValidUrl(url)) return;
try {
const res = await fetch(chrome.runtime.getURL(pageLink));
const html = await res.text();
const wrapper = document.createElement("div");
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
} catch (err) {
console.error("Failed to load popup page:", err);
}
};
const getKey = () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get("key", (result) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result.key || null);
}
});
});
};
async function handleCreate(event, formElements) {
event.preventDefault();
const key = await getKey();
if (!key) {
showKey();
return;
}
const maxPrice = parseFloat(formElements.maxPrice.value);
const plusPrice = parseFloat(formElements.plusPrice.value);
const quantity = parseInt(formElements.quantity.value, 10);
const payload = {
url: formElements.url.value.trim(),
max_price: isNaN(maxPrice) ? null : maxPrice,
plus_price: isNaN(plusPrice) ? null : plusPrice,
quantity: isNaN(quantity) ? null : quantity,
};
// Validate required fields
if (!payload.url || payload.max_price === null) {
alert("Please fill out the URL and Max Price fields correctly.");
return;
}
try {
const response = await fetch(`${CONFIG.API_BASE_URL}/bids`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: key,
},
body: JSON.stringify(removeFalsyValues(payload)),
});
const result = await response.json();
alert(result.message);
// showInfo
await showInfo(extractModelId(payload.url), formElements);
// handleChangeTitleButton
handleChangeTitleButton(true, formElements);
} catch (error) {
alert("Error: " + error.message);
console.error("API Error:", error);
}
}
async function handleUpdate(event, formElements, id) {
event.preventDefault();
const key = await getKey();
if (!key) {
showKey();
return;
}
const maxPrice = parseFloat(formElements.maxPrice.value);
const plusPrice = parseFloat(formElements.plusPrice.value);
const quantity = parseInt(formElements.quantity.value, 10);
const payload = {
max_price: isNaN(maxPrice) ? null : maxPrice,
plus_price: isNaN(plusPrice) ? null : plusPrice,
quantity: isNaN(quantity) ? null : quantity,
};
// Validate required fields
if (payload.max_price === null) {
alert("Please fill out the URL and Max Price fields correctly.");
return;
}
try {
const response = await fetch(`${CONFIG.API_BASE_URL}/bids/info/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: key,
},
body: JSON.stringify(removeFalsyValues(payload)),
});
const result = await response.json();
alert(result.message);
} catch (error) {
alert("Error: " + error.message);
console.error("API Error:", error);
}
}
const showBid = () => {
const formKey = document.getElementById("form-key");
const formBid = document.getElementById("form-bid");
formKey.style.display = "none";
formBid.style.display = "block";
};
const showKey = async () => {
chrome.storage.local.set({ key: null }, async () => {
console.log("abc");
});
const key = await getKey();
const formKey = document.getElementById("form-key");
const formBid = document.getElementById("form-bid");
const keyEl = document.querySelector("#form-key #key");
formBid.style.display = "none";
formKey.style.display = "block";
if (key && keyEl) {
keyEl.value = key;
}
};
const handleToogle = async () => {
const btn = document.getElementById("toggle-bid-extension");
const panel = document.getElementById("bid-extension");
if (btn && panel) {
btn.addEventListener("click", async () => {
const isHidden = panel.style.display === "none";
panel.style.display = isHidden ? "block" : "none";
if (isHidden) {
await handleShowForm();
}
});
} else {
console.error("Không tìm thấy nút hoặc panel!");
}
};
const handleShowForm = async () => {
const formBid = document.getElementById("form-bid");
const formKey = document.getElementById("form-key");
const keyBtn = document.getElementById("key-btn");
const currentKey = await getKey();
if (!currentKey) {
await showKey();
} else {
showBid();
}
keyBtn?.addEventListener("click", () => {
showKey();
});
};
const handleChangeTitleButton = (result, formElements) => {
if (result) {
formElements.createBtn.textContent = "Update";
} else {
formElements.createBtn.textContent = "Create";
}
};
const handleSaveKey = () => {
const form = document.querySelector("#form-key form");
if (!form) return;
form.addEventListener("submit", async (e) => {
e.preventDefault();
const inputKey = form.querySelector("#key");
if (!inputKey) return;
const keyValue = inputKey.value.trim();
if (!keyValue) {
alert("Please enter a key");
return;
}
// Lưu vào chrome.storage.local
chrome.storage.local.set({ key: keyValue }, async () => {
alert("Key saved successfully!");
showBid();
if (!isValidModel()) return;
await showInfo();
});
});
};
const isValidModel = () => {
const currentUrl = window.location.href;
const model = extractModelId(currentUrl);
return !!model;
};
const createInfoColumn = (data, formElements) => {
const inputsContainer = document.querySelector("#bid-extension .inputs");
const urlCol = document.querySelector("#url-col");
if (!inputsContainer || !urlCol) return;
// 1. Thêm ID và Name vào đầu inputsContainer
const otherEls = `
<div class="col">
<label>ID</label>
<input readonly value="${data?.id || "None"}" type="text" id="id" />
</div>
<div class="col">
<label>Name</label>
<textarea readonly id="maxPrice">${data?.name || "None"}</textarea>
</div>
`;
inputsContainer.insertAdjacentHTML("afterbegin", otherEls);
// 2. Tạo và chèn Current Price ngay sau #url-col
const currentPriceDiv = document.createElement("div");
currentPriceDiv.className = "col";
currentPriceDiv.innerHTML = `
<label>Current price</label>
<input readonly type="text" value="${
data?.current_price || "None"
}" id="currentPrice" />
`;
urlCol.parentNode.insertBefore(currentPriceDiv, urlCol.nextSibling);
formElements.quantity.value = data?.quantity || 1;
formElements.plusPrice.value = data?.plus_price || 0;
};
const showInfo = async (model, formElements) => {
const key = await getKey();
if (!key) {
showKey();
return;
}
try {
const response = await fetch(`${CONFIG.API_BASE_URL}/bids/${model}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: key,
},
});
const result = await response.json();
if (!result || result?.status_code !== 200 || !result?.data) {
if (result.status_code !== 404) {
alert(result.message);
}
PREV_DATA = null;
return null;
}
formElements.maxPrice.value = result.data.max_price;
createInfoColumn(result.data, formElements);
PREV_DATA = result;
return result;
} catch (error) {
alert("Error: " + error.message);
console.error("API Error:", error);
}
};
(async () => {
await showPage();
handleToogle();
const formElements = {
url: document.querySelector("#form-bid #url"),
maxPrice: document.querySelector("#form-bid #maxPrice"),
plusPrice: document.querySelector("#form-bid #plusPrice"),
quantity: document.querySelector("#form-bid #quantity"),
createBtn: document.querySelector("#form-bid #createBtn"),
form: document.querySelector("#form-bid form"),
};
const style = document.createElement("link");
style.rel = "stylesheet";
style.href = chrome.runtime.getURL("assets/css/index.css");
document.head.appendChild(style);
handleSaveKey();
const currentUrl = window.location.href;
const model = extractModelId(currentUrl);
if (!model) return;
// set url on form
formElements.url.value = currentUrl;
await showInfo(model, formElements);
handleChangeTitleButton(!!PREV_DATA, formElements);
formElements.form.addEventListener("submit", (e) =>
PREV_DATA
? handleUpdate(e, formElements, PREV_DATA.data.id)
: handleCreate(e, formElements)
);
})();