All files / components/transcript utils.ts

93.69% Statements 104/111
88.28% Branches 98/111
100% Functions 31/31
96.07% Lines 98/102

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 2119x 9x       22x 143x     77x 77x     35x 35x     157x 190x     278x 392x     110x 118x         35x 35x 34x   35x     79x 108x 108x   97x     28x 28x     3x 3x       3x 3x 3x       3x       9x   3x     18x 18x 18x     18x         18x 54x       8x 8x     8x   8x         6x 6x     23x 23x 23x 23x 23x 23x 23x     6x 30x               4x 16x 4x     14x 14x 16x 16x 4x     12x       29x 29x 5x     24x 25x 25x   24x       25x     3x 9x   20x 20x 3x     17x 11x     6x 2x     4x 2x 2x   2x             2x     2x 4x 2x 1x 3x     1x       1x     16x 16x                 13x 13x 13x 13x   13x    
import { parseIsoDateUtc, formatDateWithYear, formatHumanDateRange } from "@/utils/date";
import { leaveTypeLabel } from "@/utils/leave";
 
export type UnknownRecord = Record<string, unknown>;
 
export function isRecord(value: unknown): value is UnknownRecord {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
 
export function asRecord(value: unknown): UnknownRecord | null {
  return isRecord(value) ? value : null;
}
 
export function asRecordArray(value: unknown): UnknownRecord[] {
  return Array.isArray(value) ? value.filter(isRecord) : [];
}
 
export function asOptionalString(value: unknown) {
  return typeof value === "string" ? value : undefined;
}
 
export function asString(value: unknown, fallback = "—") {
  return typeof value === "string" ? value : fallback;
}
 
export function asNumber(value: unknown, fallback = "—") {
  return typeof value === "number" && Number.isFinite(value)
    ? String(value)
    : fallback;
}
 
export function formatStatus(status: string) {
  if (!status) return "—";
  return status
    .replaceAll("_", " ")
    .replace(/\b\w/g, (char) => char.toUpperCase());
}
 
export function compactLeaveTypeLabel(label: string) {
  const normalized = label.trim();
  if (!normalized) return "—";
 
  return normalized.replace(/\s+leave$/i, "");
}
 
export function normalizeRequestStatus(status: unknown) {
  return asString(status, "").trim().toLowerCase();
}
 
export function buildRequestQueryText(request: UnknownRecord) {
  const leaveType = compactLeaveTypeLabel(
    asString(request.leaveTypeLabel, asString(request.leaveType, "")),
  );
 
  const startDate = asOptionalString(request.startDate)?.trim();
  const endDate = asOptionalString(request.endDate)?.trim();
  const datePart = startDate && endDate && startDate !== endDate
    ? `${startDate} ${endDate}`
    : startDate || endDate || null;
 
  const parts = [
    asOptionalString(request.employeeName)?.trim(),
    leaveType && leaveType !== "—" ? leaveType : null,
    datePart,
  ].filter((part): part is string => Boolean(part));
 
  return parts.join(" ").trim();
}
 
export function getRequestRowSummary(request: UnknownRecord) {
  const employeeName = asOptionalString(request.employeeName)?.trim();
  const leaveType = compactLeaveTypeLabel(
    asString(request.leaveTypeLabel, asString(request.leaveType, "")),
  );
  const dateRange = formatHumanDateRange(
    asString(request.startDate, ""),
    asString(request.endDate, ""),
  );
 
  return [employeeName, leaveType && leaveType !== "—" ? leaveType : null, dateRange]
    .filter((part): part is string => Boolean(part))
    .join(" • ");
}
 
export function getBalanceRowSummary(balance: UnknownRecord) {
  const leaveType = compactLeaveTypeLabel(
    leaveTypeLabel(asString(balance.leaveType, "annual")),
  );
  const remaining = asNumber(balance.remaining, "");
 
  return remaining
    ? `${leaveType} • Remaining ${remaining}`
    : leaveType;
}
 
export function getMemberRowSummary(member: UnknownRecord) {
  return asOptionalString(member.employeeName)?.trim() ?? "";
}
 
export function isFutureOrTodayDate(dateStr: unknown): boolean {
  Iif (typeof dateStr !== "string" || !dateStr.trim()) return false;
  const start = parseIsoDateUtc(dateStr.trim());
  Iif (!start) return false;
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  return start >= today;
}
 
export function isPrimitiveValue(value: unknown): value is string | number | boolean | null {
  return (
    value === null ||
    typeof value === "string" ||
    typeof value === "number" ||
    typeof value === "boolean"
  );
}
 
export function canRenderCellValue(value: unknown) {
  if (isPrimitiveValue(value)) return true;
  return Array.isArray(value) && value.every((item) => isPrimitiveValue(item));
}
 
export function hasRenderableColumnValue(rows: UnknownRecord[], key: string) {
  return rows.some((row) => {
    const value = row[key];
    if (value === undefined || value === null || value === "") {
      return false;
    }
 
    return canRenderCellValue(value);
  });
}
 
export function isNumericColumn(rows: UnknownRecord[], key: string) {
  if (GENERIC_NUMERIC_COLUMN_KEY_REGEX.test(key)) {
    return true;
  }
 
  const values = rows
    .map((row) => row[key])
    .filter((value) => value !== null && value !== undefined && value !== "");
 
  Iif (values.length === 0) {
    return false;
  }
 
  return values.every((value) => typeof value === "number" && Number.isFinite(value));
}
 
export const GENERIC_NUMERIC_COLUMN_KEY_REGEX =
  /(^|_)(count|total|days?|allowance|used|pending|remaining|hours?)$/i;
 
export function formatGenericCellValue(value: unknown, key: string) {
  if (value === null || value === undefined || value === "") {
    return "—";
  }
 
  if (typeof value === "number") {
    return Number.isFinite(value) ? String(value) : "—";
  }
 
  if (typeof value === "boolean") {
    return value ? "Yes" : "No";
  }
 
  if (typeof value === "string") {
    const normalizedValue = value.trim();
    Iif (!normalizedValue) return "—";
 
    Iif (key.toLowerCase().endsWith("date")) {
      const parsedDate = parseIsoDateUtc(normalizedValue);
      if (parsedDate) {
        return formatDateWithYear(parsedDate);
      }
    }
 
    return normalizedValue;
  }
 
  Eif (Array.isArray(value)) {
    const primitiveItems = value.filter((item) => isPrimitiveValue(item));
    if (primitiveItems.length === value.length) {
      const joinedValue = primitiveItems
        .map((item) => (item === null ? "—" : String(item)))
        .join(", ");
 
      return joinedValue.length > 0 ? joinedValue : "—";
    }
  }
 
  return "—";
}
 
export function isBalanceLikeRecord(row: UnknownRecord) {
  return (
    "leaveType" in row &&
    "allowance" in row &&
    "used" in row &&
    "pending" in row &&
    "remaining" in row
  );
}
 
export function isRequestLikeRecord(row: UnknownRecord) {
  const hasLeaveType = "leaveType" in row || "leaveTypeLabel" in row;
  const hasDateRange = "dateRange" in row || "startDate" in row || "endDate" in row;
  const hasStatus = "status" in row;
 
  return hasLeaveType && hasDateRange && hasStatus;
}