All files / Bandstand/src/lib song-fields.ts

100% Statements 57/57
71.42% Branches 10/14
100% Functions 1/1
100% Lines 57/57

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 681x 1x   1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x 1x       1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 3x 3x 3x 2x 3x 1x 3x 1x 1x  
import {z} from 'zod'
import {isHttpUrl} from './url'
 
const optText = z
  .string()
  .trim()
  .max(200)
  .transform((v) => v || null)
  .nullable()
  .optional()
 
const optUrl = z
  .string()
  .trim()
  .transform((v) => v || null)
  .refine((v) => v === null || isHttpUrl(v), 'Must be an http(s) URL')
  .nullable()
  .optional()
 
/** Editable song details (everything except the chart itself). */
export const SongFields = z.object({
  title: z.string().trim().min(1).max(200).optional(),
  writer: optText,
  leadSinger: optText,
  guitars: z.number().int().min(0).max(9).nullable().optional(),
  // How long the band plays it, in seconds
  seconds: z.number().int().min(1).max(3600).nullable().optional(),
  keys: optText,
  percussion: optText,
  youtubeUrl: optUrl,
  lyricsUrl: optUrl,
  status: z.enum(['READY', 'LEARNING']).optional(),
  notes: z.string().max(5000).nullable().optional(),
})
 
export type SongFieldValues = z.infer<typeof SongFields>
 
const LABELS: Record<keyof SongFieldValues, string> = {
  title: 'title',
  writer: 'writer',
  leadSinger: 'lead singer',
  guitars: 'guitars',
  seconds: 'length',
  keys: 'keys',
  percussion: 'percussion',
  youtubeUrl: 'YouTube link',
  lyricsUrl: 'lyrics link',
  status: 'status',
  notes: 'notes',
}
 
/** Human-readable list of the fields that actually change. */
export function describeChanges(
  before: Record<string, unknown>,
  after: SongFieldValues,
): string[] {
  const out: string[] = []
  for (const k of Object.keys(after) as (keyof SongFieldValues)[]) {
    const next = after[k]
    if (next === undefined) continue
    if ((before[k] ?? null) === (next ?? null)) continue
    if (k === 'status')
      out.push(next === 'READY' ? 'status to gig-ready' : 'status to learning')
    else out.push(LABELS[k])
  }
  return out
}