|
1 | 1 | const UUID_PATTERN = |
2 | | - /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi |
| 2 | + /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi; |
| 3 | + |
| 4 | +const INC_PATTERN = /INC\d{12,15}/gi; |
3 | 5 |
|
4 | 6 | export function formatDateTime(value) { |
5 | | - if (!value) return '—' |
6 | | - const parsed = new Date(value) |
7 | | - if (Number.isNaN(parsed.getTime())) return value |
8 | | - return parsed.toLocaleString() |
| 7 | + if (!value) return "—"; |
| 8 | + const parsed = new Date(value); |
| 9 | + if (Number.isNaN(parsed.getTime())) return value; |
| 10 | + return parsed.toLocaleString(); |
9 | 11 | } |
10 | 12 |
|
11 | 13 | export function upsertRun(runs, updatedRun, maxSize = 25) { |
12 | | - const index = runs.findIndex((item) => item.id === updatedRun.id) |
| 14 | + const index = runs.findIndex((item) => item.id === updatedRun.id); |
13 | 15 | if (index === -1) { |
14 | | - return [updatedRun, ...runs].slice(0, maxSize) |
| 16 | + return [updatedRun, ...runs].slice(0, maxSize); |
15 | 17 | } |
16 | 18 |
|
17 | | - const next = [...runs] |
18 | | - next[index] = updatedRun |
19 | | - next.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)) |
20 | | - return next |
| 19 | + const next = [...runs]; |
| 20 | + next[index] = updatedRun; |
| 21 | + next.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); |
| 22 | + return next; |
21 | 23 | } |
22 | 24 |
|
23 | 25 | export function parseTicketIds(rawValue) { |
24 | | - if (rawValue == null) return [] |
| 26 | + if (rawValue == null) return []; |
25 | 27 |
|
26 | 28 | const asText = Array.isArray(rawValue) |
27 | | - ? rawValue.join(',') |
28 | | - : String(rawValue) |
| 29 | + ? rawValue.join(",") |
| 30 | + : String(rawValue); |
| 31 | + |
| 32 | + // Try INC numbers first (primary identifier) |
| 33 | + const incMatches = asText.match(INC_PATTERN); |
| 34 | + if (incMatches?.length) { |
| 35 | + return incMatches.map((value) => value.toUpperCase()); |
| 36 | + } |
29 | 37 |
|
30 | | - const uuidMatches = asText.match(UUID_PATTERN) |
| 38 | + const uuidMatches = asText.match(UUID_PATTERN); |
31 | 39 | if (uuidMatches?.length) { |
32 | | - return uuidMatches.map((value) => value.toLowerCase()) |
| 40 | + return uuidMatches.map((value) => value.toLowerCase()); |
33 | 41 | } |
34 | 42 |
|
35 | 43 | return asText |
36 | 44 | .split(/[\n,;\s]+/) |
37 | 45 | .map((value) => value.trim()) |
38 | | - .filter(Boolean) |
| 46 | + .filter(Boolean); |
39 | 47 | } |
40 | 48 |
|
41 | | -export function extractTicketIdsFromRows(rows, ticketIdFields = ['ticket_ids', 'ticket_id', 'ticketIds']) { |
42 | | - const ids = new Set() |
| 49 | +export function extractTicketIdsFromRows( |
| 50 | + rows, |
| 51 | + ticketIdFields = ["ticket_ids", "ticket_id", "ticketIds"], |
| 52 | +) { |
| 53 | + const ids = new Set(); |
43 | 54 | for (const row of rows || []) { |
44 | | - if (!row || typeof row !== 'object') continue |
| 55 | + if (!row || typeof row !== "object") continue; |
45 | 56 | for (const field of ticketIdFields) { |
46 | | - const fieldValue = row[field] |
| 57 | + const fieldValue = row[field]; |
47 | 58 | for (const parsedId of parseTicketIds(fieldValue)) { |
48 | | - ids.add(parsedId) |
| 59 | + ids.add(parsedId); |
49 | 60 | } |
50 | 61 | } |
51 | 62 | } |
52 | | - return Array.from(ids) |
| 63 | + return Array.from(ids); |
53 | 64 | } |
54 | 65 |
|
55 | 66 | export function sanitizeMarkdownForDisplay(markdown) { |
56 | | - if (!markdown) return '' |
57 | | - return markdown.replace(/```json[\s\S]*?```/gi, '').trim() |
| 67 | + if (!markdown) return ""; |
| 68 | + return markdown.replace(/```json[\s\S]*?```/gi, "").trim(); |
58 | 69 | } |
0 commit comments