All files / Bandstand/src/app/api/availability/range route.ts

96.92% Statements 63/65
86.36% Branches 19/22
100% Functions 0/0
96.92% Lines 63/65

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 721x   1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x   1x 1x   1x     1x 7x 7x 7x 7x 7x 6x 6x 7x 5x 5x 7x 4x 4x 38x 4x 4x 4x 4x 4x 4x 4x 4x 3x 3x 4x 4x 4x 4x 4x 4x 3x 1x   1x   1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x  
export const dynamic = 'force-dynamic'
 
import {z} from 'zod'
import {prisma} from '@/lib/db'
import {requireScheduling, requireSession} from '@/lib/guard'
import {route} from '@/lib/route'
import {dayRange, formatDay} from '@/lib/availability'
import {scopeFor} from '@/lib/availability-server'
 
const Day = z.string().regex(/^\d{4}-\d{2}-\d{2}$/)
const Body = z.object({
  from: Day,
  to: Day,
  kind: z.enum(['OUT', 'PM_OUT', 'PREFER_NOT']).nullable(),
  // Only these weekdays (0 = Sunday); omitted = every day in the range
  weekdays: z.array(z.number().int().min(0).max(6)).min(1).optional(),
})
 
const MAX_DAYS = 400
 
/** Mark a whole stretch at once: "away Dec 18 – Jan 4", "out every Tuesday". */
export const PUT = route(async (req: Request) => {
  const {user, band} = await requireSession()
  requireScheduling(band)
  const scope = scopeFor(user, band.id)
  const parsed = Body.safeParse(await req.json())
  if (!parsed.success) return new Response('Bad Request', {status: 400})
  const {from, to, kind, weekdays} = parsed.data
  if (to < from)
    return Response.json({error: 'The end is before the start.'}, {status: 400})
  const count = Math.round((Date.parse(to) - Date.parse(from)) / 86_400_000) + 1
  if (count > MAX_DAYS)
    return Response.json({error: 'That’s more than a year.'}, {status: 400})
  const days = dayRange(from, count).filter(
    (d) =>
      !weekdays || weekdays.includes(new Date(d + 'T00:00:00Z').getUTCDay()),
  )
  const dates = days.map((d) => new Date(d + 'T00:00:00Z'))
  await prisma.$transaction(async (tx) => {
    await tx.unavailability.deleteMany({
      where: {userId: user.id, scope, date: {in: dates}},
    })
    if (kind)
      await tx.unavailability.createMany({
        data: dates.map((date) => ({userId: user.id, scope, date, kind})),
      })
    await tx.user.update({
      where: {id: user.id},
      data: {availabilityUpdatedAt: new Date()},
    })
    const what =
      kind === 'OUT'
        ? 'out'
        : kind === 'PM_OUT'
          ? 'out in the afternoons'
          : kind === 'PREFER_NOT'
            ? '“prefer not”'
            : 'free'
    await tx.activity.create({
      data: {
        bandId: scope ? band.id : null,
        userId: user.id,
        action: 'availability.range',
        targetType: 'availability',
        targetId: user.id,
        summary: `marked themselves ${what} ${formatDay(from)} – ${formatDay(to)}${weekdays ? ' (some weekdays)' : ''}`,
      },
    })
  })
  return Response.json({days})
})