204 lines
6.0 KiB
TypeScript
204 lines
6.0 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 { IWebBid } from "../system/type";
|
|
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;
|
|
}
|
|
}
|