update config live / sandbox

This commit is contained in:
Admin 2025-06-19 16:36:34 +07:00
parent e211e0e521
commit 513dca2a9a
5 changed files with 316 additions and 27 deletions

View File

@ -1,19 +1,10 @@
/* #bid-extension body {
margin: 0;
padding: 0;
background-color: #121212;
color: #e0e0e0;
font-family: 'Segoe UI', Tahoma, sans-serif;
width: 320px;
} */
#bid-extension {
margin: 0;
padding: 0;
background-color: #121212;
color: #e0e0e0;
font-family: "Segoe UI", Tahoma, sans-serif;
width: 320px;
width: 500px;
}
#bid-extension .container {
@ -36,8 +27,9 @@
}
#bid-extension input,
#bid-extension select,
#bid-extension textarea {
padding: 8px;
padding: 4px 8px;
background-color: #1e1e1e;
color: #ffffff;
border: 1px solid #333;
@ -45,9 +37,11 @@
font-size: 14px;
width: 100%;
box-sizing: border-box;
height: 32px;
}
#bid-extension input:focus,
#bid-extension select:focus,
#bid-extension textarea:focus {
border-color: #4a90e2;
outline: none;
@ -71,12 +65,12 @@
#bid-extension button {
margin-top: 10px;
padding: 10px;
background: linear-gradient(to right, #4a90e2, #357abd);
color: white;
border: none;
border-radius: 6px;
font-size: 15px;
height: 36px;
cursor: pointer;
transition: background 0.3s ease;
}
@ -129,6 +123,12 @@
height: 14px;
}
#bid-extension .sub-col {
display: flex;
align-items: center;
gap: 10px;
}
#toggle-bid-extension svg {
width: 20px;
height: 20px;

View File

@ -157,12 +157,73 @@ async function handleUpdate(event, formElements, id) {
quantity: isNaN(quantity) ? null : quantity,
};
const keys = Object.values(formElements.form)
.filter((item) => {
return [
"mode_key",
"early_tracking_seconds",
"arrival_offset_seconds",
].includes(item?.id);
})
.reduce((prev, cur) => {
prev[cur.id] = cur.value;
return prev;
}, {});
const earlyTracking = parseInt(keys.early_tracking_seconds, 10);
const arrivalOffset = parseInt(keys.arrival_offset_seconds, 10);
if (earlyTracking < 600) {
alert("Early Tracking Seconds must be at least 600 seconds (10 minutes).");
return;
}
if (arrivalOffset < 60) {
alert("Arrival Offset Seconds must be at least 60 seconds (1 minute).");
return;
}
// Validate required fields
if (payload.max_price === null) {
alert("Please fill out the URL and Max Price fields correctly.");
return;
}
const localKeyName = keys["mode_key"];
const newKeys = Object.entries(keys).reduce((prev, [key, value]) => {
if (key === "mode_key") {
prev[key] = value;
} else {
prev[`${key}_${localKeyName}`] = Number(value);
}
return prev;
}, {});
let metadata = [];
if (
window.__result.metadata.length > 0 &&
window.__result.metadata.some((item) => item.key_name === "mode_key")
) {
metadata = window.__result.metadata.map((item) => {
if (Object.keys(newKeys).includes(item.key_name)) {
return {
...item,
value: newKeys[item.key_name],
};
}
return { ...item };
});
} else {
metadata = Object.entries(newKeys).map(([key, value]) => {
return {
key_name: key,
value,
};
});
}
try {
const response = await fetch(`${CONFIG.API_BASE_URL}/bids/info/${id}`, {
method: "PUT",
@ -170,7 +231,7 @@ async function handleUpdate(event, formElements, id) {
"Content-Type": "application/json",
Authorization: key,
},
body: JSON.stringify(removeFalsyValues(payload)),
body: JSON.stringify(removeFalsyValues({ ...payload, metadata })),
});
const result = await response.json();
@ -179,6 +240,8 @@ async function handleUpdate(event, formElements, id) {
} catch (error) {
alert("Error: " + error.message);
console.error("API Error:", error);
} finally {
await showInfo();
}
}
@ -289,6 +352,81 @@ const isValidModel = () => {
return !!model;
};
const renderValueModeMetadata = (data, formElements, mode = null) => {
const early_tracking_seconds = getEarlyTrackingSeconds(data, mode);
const arrival_offset_seconds = getArrivalOffsetSeconds(data, mode);
formElements.metadataMode.arrival_offset_seconds.value =
arrival_offset_seconds;
formElements.metadataMode.early_tracking_seconds.value =
early_tracking_seconds;
return { early_tracking_seconds, arrival_offset_seconds };
};
function formatTimeFromMinutes(minutes) {
// Tính ngày, giờ, phút từ số phút
const days = Math.floor(minutes / (60 * 24));
const hours = Math.floor((minutes % (60 * 24)) / 60);
const mins = minutes % 60;
let result = "";
if (days > 0) result += `${days} ${days > 1 ? "days" : "day"} `;
if (hours > 0) result += `${hours} ${hours > 1 ? "hours" : "hour"} `;
if (mins > 0 || result === "") result += `${mins} minutes`;
return result.trim();
}
function toReadableLabel(input) {
return input
.split("_") // Tách chuỗi theo dấu gạch dưới
.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Viết hoa chữ cái đầu
.join(" "); // Nối lại bằng khoảng trắng
}
const setMinusLabel = (input, minutes = null) => {
const label = document.querySelector(`[for="${input?.id}"]`);
const text = toReadableLabel(input.id);
label.textContent = `${text} (${formatTimeFromMinutes(
minutes || Number(input.value) || 300 / 60
)})`;
};
const renderMetadaMode = (data, formElements, mode = null) => {
const mode_key = mode ? mode : getMode(data);
formElements.metadataModeBox.style.display = "flex";
renderValueModeMetadata(data, formElements, mode_key);
Object.values(formElements.form).forEach((item) => {
if (item?.id === "mode_key") {
item.value = mode_key;
item.addEventListener("change", (e) => {
renderValueModeMetadata(data, formElements, e.target.value);
Object.values(formElements.metadataMode).forEach((i) => {
setMinusLabel(i);
});
});
}
});
Object.values(formElements.metadataMode).forEach((item) => {
setMinusLabel(item);
item.addEventListener("input", (e) => {
setMinusLabel(item, Number(e.target.value));
});
});
};
const createInfoColumn = (data, formElements) => {
const inputsContainer = document.querySelector("#bid-extension .inputs");
const urlCol = document.querySelector("#url-col");
@ -297,9 +435,19 @@ const createInfoColumn = (data, formElements) => {
// 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 class="sub-col">
<div class="col">
<label>ID</label>
<input readonly value="${data?.id || "None"}" type="text" id="id" />
</div>
<div class="col">
<label for="mode_key">Mode</label>
<select id="mode_key" name="mode_key">
<option value="live">Live</option>
<option value="sandbox">Sandbox</option>
</select>
</div>
</div>
<div class="col">
@ -324,6 +472,65 @@ const createInfoColumn = (data, formElements) => {
formElements.quantity.value = data?.quantity || 1;
formElements.plusPrice.value = data?.plus_price || 0;
renderMetadaMode(data, formElements);
};
const showCompetitor = () => {
const script = document.createElement("script");
script.src = chrome.runtime.getURL("injected.js");
script.onload = function () {
this.remove();
};
(document.head || document.documentElement).appendChild(script);
// Nghe dữ liệu trả về
window.addEventListener("message", function (event) {
if (event.source !== window) return;
if (event.data && event.data.source === "my-extension") {
const bidHistory = event.data?.bidHistory || [];
const maxProxy = bidHistory.reduce((max, curr) => {
return curr.proxyamount > max.proxyamount ? curr : max;
}, bidHistory[0]);
console.log({ bidHistory });
const competitorEl = document.getElementById("competitor-max-bid-col");
const competitorInput = document.querySelector(
"#competitor-max-bid-col input"
);
competitorEl.style.display = "block";
competitorInput.value = maxProxy?.proxyamount || "None";
}
});
};
const getMode = (data) => {
return (
data.metadata.find((item) => item.key_name === "mode_key")?.value || "live"
);
};
const getEarlyTrackingSeconds = (data, outsiteMode = null) => {
const mode = outsiteMode ? outsiteMode : getMode(data);
return (
data.metadata.find(
(item) => item.key_name === `early_tracking_seconds_${mode}`
)?.value || data.web_bid.early_tracking_seconds
);
};
const getArrivalOffsetSeconds = (data, outsiteMode = null) => {
const mode = outsiteMode ? outsiteMode : getMode(data);
return (
data.metadata.find(
(item) => item.key_name === `arrival_offset_seconds_${mode}`
)?.value || data.web_bid.arrival_offset_seconds
);
};
const showInfo = async (model, formElements) => {
@ -353,6 +560,8 @@ const showInfo = async (model, formElements) => {
return null;
}
window["__result"] = result.data;
formElements.maxPrice.value = result.data.max_price;
createInfoColumn(result.data, formElements);
@ -368,13 +577,22 @@ const showInfo = async (model, formElements) => {
(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"),
modeKey: document.querySelector("#form-bid #mode_key"),
metadataModeBox: document.querySelector("#form-bid #metadataMode"),
metadataMode: {
arrival_offset_seconds: document.querySelector(
"#form-bid #arrival_offset_seconds"
),
early_tracking_seconds: document.querySelector(
"#form-bid #early_tracking_seconds"
),
},
form: document.querySelector("#form-bid form"),
};
@ -391,6 +609,12 @@ const showInfo = async (model, formElements) => {
if (!model) return;
switch (extractDomain(currentUrl)) {
case webs.allbids: {
showCompetitor();
}
}
// set url on form
formElements.url.value = currentUrl;

16
injected.js Normal file
View File

@ -0,0 +1,16 @@
(function () {
let data = null;
const elements = document.querySelectorAll(".ng-scope");
for (let i = 0; i < elements.length; i++) {
const scope = window.angular?.element(elements[i]).scope();
if (scope && scope.auction) {
console.log("Found at index:", i, "Auction:", scope.bidHistory);
data = scope.bidHistory;
break;
}
}
if (data) {
window.postMessage({ source: "my-extension", bidHistory: data }, "*");
}
})();

View File

@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Bid Extension",
"version": "1.2",
"version": "1.3",
"description": "Bid Extension",
"action": {
"default_popup": "pages/popup/popup.html",
@ -28,7 +28,8 @@
"pages/popup/popup.html",
"assets/css/index.css",
"config.js",
"assets/icons/*"
"assets/icons/*",
"injected.js"
],
"matches": ["<all_urls>"]
}

View File

@ -6,6 +6,7 @@
id="toggle-bid-extension"
style="
padding: 12px 20px;
max-height: 44px;
background: #2c2f36;
color: #ffffff;
border: none;
@ -96,7 +97,7 @@
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
padding: 20px;
width: 320px;
width: 500px;
"
>
<!-- Form bid -->
@ -104,25 +105,72 @@
<form class="container">
<h2>Bid</h2>
<div class="inputs">
<div style="display: none" id="competitor-max-bid-col" class="col">
<label>Competior max bid</label>
<input readonly type="text" id="competitor-max-bid" />
</div>
<div id="url-col" class="col">
<label>Url</label>
<input readonly type="text" id="url" />
</div>
<div class="col">
<div id="max-price-col" class="col">
<label>Max price</label>
<input type="number" id="maxPrice" />
</div>
<div class="col">
<label>Plus price</label>
<input type="number" id="plusPrice" />
<div class="sub-col">
<div class="col">
<label>Plus price</label>
<input type="number" id="plusPrice" />
</div>
<div class="col">
<label>Quantity</label>
<input type="number" id="quantity" />
</div>
</div>
<div class="col">
<label>Quantity</label>
<input type="number" id="quantity" />
<div style="display: none" id="metadataMode" class="sub-col">
<div class="col">
<label for="arrival_offset_seconds">Arrival offset seconds</label>
<input
type="number"
id="arrival_offset_seconds"
name="arrival_offset_seconds"
/>
</div>
<div class="col">
<label for="early_tracking_seconds">Early tracking seconds</label>
<input
type="number"
id="early_tracking_seconds"
name="early_tracking_seconds"
/>
</div>
</div>
<!-- <div style="display: none" id="metadata-mode-sandbox" class="sub-col">
<div class="col">
<label>Arrival offset seconds</label>
<input
type="number"
id="arrival_offset_seconds_sandbox"
name="arrival_offset_seconds_sandbox"
/>
</div>
<div class="col">
<label>Early tracking seconds</label>
<input
type="number"
id="early_tracking_seconds_sandbox"
name="early_tracking_seconds_sandbox"
/>
</div>
</div> -->
</div>
<button type="submit" id="createBtn">Create</button>
</form>