ui fixers in 10/16

This commit is contained in:
2026-10-16 14:34:28 +05:30
parent 379ad204bd
commit e289b8430f
47 changed files with 1904 additions and 1046 deletions
+59
View File
@@ -0,0 +1,59 @@
/**
* Collapses serial / warranty numbers into consecutive runs for display:
* SN-001, SN-002 … SN-050, SN-077 → "SN-001 – SN-050" (50) and "SN-077" (1).
*
* Uses the same "prefix + trailing number" reading as the GRN form's range builder, so a run
* entered there as From/adding comes back out as exactly one range here.
*/
export type SerialRange = { from: string; to: string; count: number }
function parseTrailingNumber(s: string): { prefix: string; num: number; width: number } | null {
const m = s.match(/^(.*?)(\d+)$/)
if (!m) return null
return { prefix: m[1], num: parseInt(m[2], 10), width: m[2].length }
}
/** Natural order: by prefix, then by the trailing number (so SN-9 sorts before SN-10). */
function compareSerials(a: string, b: string): number {
const pa = parseTrailingNumber(a)
const pb = parseTrailingNumber(b)
if (pa && pb && pa.prefix === pb.prefix) return pa.num - pb.num
return a.localeCompare(b, undefined, { numeric: true })
}
export function toSerialRanges(values: string[]): SerialRange[] {
const sorted = [...new Set(values.map((v) => v.trim()).filter(Boolean))].sort(compareSerials)
const ranges: SerialRange[] = []
let run: { start: string; prefix: string; width: number; last: number; lastText: string; count: number } | null = null
for (const value of sorted) {
const parsed = parseTrailingNumber(value)
// Consecutive = same prefix, next number, written with the run's zero-padding.
const continues =
run !== null &&
parsed !== null &&
parsed.prefix === run.prefix &&
parsed.num === run.last + 1 &&
value === `${run.prefix}${String(parsed.num).padStart(run.width, "0")}`
if (continues && run && parsed) {
run.last = parsed.num
run.lastText = value
run.count += 1
continue
}
if (run) ranges.push({ from: run.start, to: run.lastText, count: run.count })
run = parsed
? { start: value, prefix: parsed.prefix, width: parsed.width, last: parsed.num, lastText: value, count: 1 }
: { start: value, prefix: `\u0000${value}`, width: 0, last: Number.NaN, lastText: value, count: 1 }
}
if (run) ranges.push({ from: run.start, to: run.lastText, count: run.count })
return ranges
}
/** One-line summary, e.g. "SN-001 – SN-050" or "SN-001 – SN-050, SN-077 +2 more". */
export function formatSerialRanges(ranges: SerialRange[], max = 2): string {
const label = (r: SerialRange) => (r.count === 1 ? r.from : `${r.from} – ${r.to}`)
const shown = ranges.slice(0, max).map(label).join(", ")
return ranges.length > max ? `${shown} +${ranges.length - max} more` : shown
}