import { Rating } from "./db/schema/property"; import { KeyboardEvent } from "react"; export function handleNumericKeyDown(event: KeyboardEvent) { /** * Allowing: Integers | Backspace | Tab | Delete | Left & Right arrow keys **/ const regex = new RegExp( /(^\d*$)|(Backspace|Tab|Delete|ArrowLeft|ArrowRight|ArrowUp|ArrowDown)/ ); return !event.key.match(regex) && event.preventDefault(); } export function convertDaysToWorkingWeeks(days: number | null) { if (days === null) { return "-"; } const workingDaysPerWeek = 5; // Convert days to working weeks const workingWeeks = days / workingDaysPerWeek; // Determine the range let lowerBound = Math.floor(workingWeeks); let upperBound = Math.ceil(workingWeeks); // Adjust if the fraction is very small, you might not count it as a full extra week if (workingWeeks - lowerBound < 0.2) { upperBound = lowerBound; } if (lowerBound === 0 && upperBound === 1) { return "1 week"; } if (lowerBound === 1 && upperBound === 1) { return "1-2 weeks"; } // Format the output return lowerBound === upperBound ? `${lowerBound} weeks` : `${lowerBound}-${upperBound} weeks`; } export const getEpcColorClass = (letter: string) => { switch (letter.toUpperCase()) { case "A": return "bg-epc_a"; case "B": return "bg-epc_b"; case "C": return "bg-epc_c"; case "D": return "bg-epc_d"; case "E": return "bg-epc_e"; case "F": return "bg-epc_f"; case "G": return "bg-epc_g"; default: return "bg-gray-500"; } }; /** Border + text colour classes for a transparent-background EPC badge */ export const getEpcAccentClasses = (letter: string) => { switch (letter.toUpperCase()) { case "A": return "border-epc_a text-epc_a"; case "B": return "border-epc_b text-epc_b"; case "C": return "border-epc_c text-epc_c"; case "D": return "border-epc_d text-epc_d"; case "E": return "border-epc_e text-epc_e"; case "F": return "border-epc_f text-epc_f"; case "G": return "border-epc_g text-epc_g"; default: return "border-slate-300 text-slate-400"; } }; export const getRating = (rating: number | null): Rating => { if (rating == null) { return "N/A"; } if (![1, 2, 3, 4, 5].includes(rating)) { throw new Error("Rating should be between 1 and 5."); } const ratingMap: { [key in 1 | 2 | 3 | 4 | 5]: string } = { 1: "Very poor", 2: "Poor", 3: "Average", 4: "Good", 5: "Very good", }; return ratingMap[rating as 1 | 2 | 3 | 4 | 5] as Rating; // You can assert that rating is one of these values since you've already checked it }; export const serializeBigInt = (_: any, value: any): string | any => { // Simple utility function to convert BigInts to strings with JSON.stringify return typeof value === "bigint" ? value.toString() : value; }; export function sapToEpc(sapPoints: number | null): string { if (sapPoints === null) { return "Unknown"; } if (sapPoints < 0) { throw new Error("SAP points should be above 0."); } if (sapPoints >= 92) { return "A"; } else if (sapPoints >= 81) { return "B"; } else if (sapPoints >= 69) { return "C"; } else if (sapPoints >= 55) { return "D"; } else if (sapPoints >= 39) { return "E"; } else if (sapPoints >= 21) { return "F"; } else { return "G"; } } // Handles "D55" (new format) and "56" (legacy numeric-only format) export function parsePreSap(raw: string | null | undefined): { letter: string; display: string } | null { if (!raw) return null; const match = raw.trim().match(/^([A-Za-z]?)(\d+)$/); if (!match) return null; const num = parseInt(match[2], 10); const letter = match[1] ? match[1].toUpperCase() : sapToEpc(num); if (letter === "Unknown") return null; return { letter, display: `${letter}${num}` }; } export function formatDateTime(dateTimeString: string | Date): string { // Create a new Date object const dateTime = new Date(dateTimeString); // Get the various parts of the date const options: Intl.DateTimeFormatOptions = { year: "numeric", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit", }; return dateTime.toLocaleDateString("en-GB", options); } export function formatNumber(number: number): string { if (number === 0) return "0"; if (number < 1) { return number.toFixed(2); } const suffixes = ["", "k", "m", "b", "t"]; const suffixIndex = Math.floor(Math.log10(Math.abs(number)) / 3); if (suffixIndex >= suffixes.length) { return number.toString(); } const scaledNumber = number / Math.pow(1000, suffixIndex); // Format the number to one decimal place let formatted = scaledNumber.toFixed(1); // Remove trailing '.0' if present if (formatted.endsWith(".0")) { formatted = formatted.slice(0, -2); } return formatted + suffixes[suffixIndex]; } export function roundToDecimalPlaces( number: number, decimalPlaces: number ): number { const factor = 10 ** decimalPlaces; return Math.round(number * factor) / factor; }