/* 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>( obj: T, excludeKeys: (keyof T)[] = [] ): Partial { 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); } 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 ); };