bid-tool/auto-bid-admin/src/utils/index.ts

291 lines
8.3 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import moment from "moment";
import { IBid, IMetadata, IWebBid } from "../system/type";
import _ from "lodash";
export function cn(...args: ClassValue[]) {
return twMerge(clsx(args));
}
export const formatTime = (time: string, patent = "DD/MM/YYYY") => {
return moment(time).format(patent);
};
export function removeFalsyValues<T extends Record<string, any>>(
obj: T,
excludeKeys: (keyof T)[] = []
): Partial<T> {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value || excludeKeys.includes(key as keyof T)) {
acc[key as keyof T] = value;
}
return acc;
}, {} as Partial<T>);
}
export function isValidJSON(str: string): boolean {
if (!str || str.length <= 0) return false;
try {
JSON.parse(str);
return true;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return false;
}
}
export function copyToClipboard(text: string, onSuccess?: () => void): void {
if (!navigator.clipboard) {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
document.execCommand("copy");
if (onSuccess) onSuccess();
} catch (err) {
console.error("Không thể copy nội dung: ", err);
}
document.body.removeChild(textarea);
} else {
navigator.clipboard
.writeText(text)
.then(() => {
if (onSuccess) onSuccess();
})
.catch((err) => console.error("Lỗi khi copy nội dung: ", err));
}
}
export function base64ToFile(base64String: string, fileName: string): File {
const [header, base64Content] = base64String.split(",");
const mimeTypeMatch = header.match(/:(.*?);/);
if (!mimeTypeMatch || mimeTypeMatch.length < 2) {
throw new Error("Invalid base64 string");
}
const mimeType = mimeTypeMatch[1];
const binaryString = atob(base64Content);
const byteArray = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
byteArray[i] = binaryString.charCodeAt(i);
}
return new File([byteArray], fileName, { type: mimeType });
}
export function toSlug(str: string, maxLength = 60): string {
if (typeof str !== "string") return ""; // Kiểm tra giá trị đầu vào
// Kiểm tra nếu môi trường hỗ trợ `normalize`
const normalizedStr = str.normalize ? str.normalize("NFD") : str;
return normalizedStr
.replace(/[\u0300-\u036f]/g, "") // Xóa dấu
.replace(/[^a-zA-Z0-9\s-]/g, "") // Chỉ giữ chữ cái, số, khoảng trắng và dấu "-"
.trim() // Xóa khoảng trắng đầu/cuối
.replace(/\s+/g, "-") // Thay khoảng trắng bằng "-"
.replace(/-+/g, "-") // Gộp nhiều dấu "-" thành 1
.toLowerCase() // Chuyển về chữ thường
.slice(0, maxLength) // Giới hạn độ dài
.replace(/^-+|-+$/g, ""); // Xóa "-" đầu/cuối
}
export function estimateReadingTimeInSeconds(
content: string,
wordsPerMinute = 200
): number {
if (!content || typeof content !== "string") return 0;
const wordCount = content.trim().split(/\s+/).length;
return Math.ceil((wordCount / wordsPerMinute) * 60);
}
export function extractDomain(url: string): string | null {
try {
const parsedUrl = new URL(url);
return parsedUrl.origin;
} catch (error) {
return null;
}
}
// Hash chuỗi thành số nguyên
export function hashStringToInt(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash; // convert to 32bit integer
}
return Math.abs(hash);
}
// Biến số thành màu HEX
export function intToHexColor(int: number): string {
const r = (int >> 16) & 0xff;
const g = (int >> 8) & 0xff;
const b = int & 0xff;
return `#${[r, g, b].map((x) => x.toString(16).padStart(2, "0")).join("")}`;
}
export function stringToColor(str: string): string {
const colorPalette = [
"#FF6B6B",
"#FFD93D",
"#FF9F1C",
"#F76C6C",
"#6BCB77",
"#4ECDC4",
"#F7B801",
"#FF6F91",
"#00C9A7",
];
const hash = hashStringToInt(str);
const index = hash % colorPalette.length;
return colorPalette[index];
}
export function findEarlyLoginTime(webBid: IWebBid): string | null {
const now = new Date();
// Bước 1: Lọc ra những bid có close_time hợp lệ
const validChildren = webBid.children.filter((child) => child.close_time);
if (validChildren.length === 0) return null;
// Bước 2: Tìm bid có close_time gần hiện tại nhất
const closestBid = validChildren.reduce((closest, current) => {
const closestDiff = Math.abs(
new Date(closest.close_time!).getTime() - now.getTime()
);
const currentDiff = Math.abs(
new Date(current.close_time!).getTime() - now.getTime()
);
return currentDiff < closestDiff ? current : closest;
});
if (!closestBid.close_time) return null;
// Bước 3: Tính toán thời gian login sớm
const closeTime = new Date(closestBid.close_time);
closeTime.setSeconds(
closeTime.getSeconds() - (webBid.early_tracking_seconds || 0)
);
return closeTime.toISOString();
}
export function extractDomainSmart(url: string) {
const PUBLIC_SUFFIXES = ["com.au", "co.uk", "com.vn", "org.au", "gov.uk"];
try {
const hostname = new URL(url).hostname.replace(/^www\./, ""); // remove "www."
const parts = hostname.split(".");
for (let i = 0; i < PUBLIC_SUFFIXES.length; i++) {
if (hostname.endsWith(PUBLIC_SUFFIXES[i])) {
return parts[parts.length - PUBLIC_SUFFIXES[i].split(".").length - 1];
}
}
return parts[parts.length - 2];
} catch (e) {
return url;
}
}
export function findNearestClosingChild(webBid: IWebBid) {
const now = Date.now();
const validChildren = webBid.children.filter(
(child) => child.close_time && !isNaN(new Date(child.close_time).getTime())
);
if (validChildren.length === 0) {
return null;
}
const nearestChild = _.minBy(validChildren, (child) => {
return Math.abs(new Date(child.close_time!).getTime() - now);
});
return nearestChild || null;
}
export function extractNumber(str: string) {
const match = str.match(/\d+(\.\d+)?/);
return match ? parseFloat(match[0]) : null;
}
export function subtractMinutes(time: string, minutes: number) {
const date = new Date(time);
date.setMinutes(date.getMinutes() - minutes);
return date.toUTCString();
}
export function subtractSeconds(time: string, seconds: number) {
const date = new Date(time);
date.setSeconds(date.getSeconds() - seconds);
return date.toUTCString();
}
export function isTimeReached(targetTime: string) {
if (!targetTime) return false;
const targetDate = new Date(targetTime);
const now = new Date();
return now >= targetDate;
}
export function formatTimeFromMinutes(minutes: number): string {
// 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();
}
export const getMode = (row: IBid) => {
return (
row.metadata.find((item) => item.key_name === "mode_key")?.value || "live"
);
};
export const getEarlyTrackingSeconds = (row: IBid) => {
const mode = getMode(row);
return (
row.metadata.find(
(item) => item.key_name === `early_tracking_seconds_${mode}`
)?.value || row.web_bid.early_tracking_seconds
);
};
export const getResponseDemo = (data: IBid) => {
if (!data?.metadata) return null;
return (
(data?.metadata.find(
(item) => item.key_name === "demo_response"
) as IMetadata) || null
);
};