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 | export const dynamic = 'force-dynamic' import Link from 'next/link' import {prisma} from '@/lib/db' import {displayName} from '@/lib/songs' import {avatarUrl} from '@/lib/avatars' import Avatar from '@/components/Avatar' import {pageSession} from '@/lib/guard' export const metadata = {title: 'Recent changes'} const dayFmt = (timeZone: string) => new Intl.DateTimeFormat('en-US', { weekday: 'long', month: 'short', day: 'numeric', year: 'numeric', timeZone, }) const timeFmt = (timeZone: string) => new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit', timeZone, }) function hrefFor( a: { targetType: string targetId: string | null action: string }, scheduling: boolean, ) { if (!a.targetId) return null if (a.targetType === 'song') return a.action.startsWith('chart.') ? `/songs/${a.targetId}/history` : `/songs/${a.targetId}` if (a.targetType === 'setlist' && a.action !== 'setlist.delete') return `/setlists/${a.targetId}` if (a.targetType === 'rehearsal') return '/rehearsals' if (a.targetType === 'availability') return scheduling ? '/rehearsals' : null return null } /** Who changed what, newest first — every save in the app lands here. */ export default async function ActivityPage() { const {band} = await pageSession() const day = dayFmt(band.timezone) const time = timeFmt(band.timezone) const rows = await prisma.activity.findMany({ // This band's changes, and personal ones (photo, password, shared // availability) by people in it where: { OR: [ {bandId: band.id}, { bandId: null, user: {memberships: {some: {bandId: band.id}}}, }, ], }, orderBy: {createdAt: 'desc'}, take: 200, include: { user: { select: { id: true, name: true, displayName: true, email: true, avatarAt: true, }, }, }, }) const groups: {day: string; rows: typeof rows}[] = [] for (const r of rows) { const d = day.format(r.createdAt) if (groups[groups.length - 1]?.day !== d) groups.push({day: d, rows: []}) groups[groups.length - 1].rows.push(r) } return ( <main className="mx-auto max-w-3xl px-4 pb-10 pt-5"> <h1 className="text-3xl font-extrabold">Recent changes</h1> <p className="mt-1 text-muted"> Everything anyone has changed, newest first. </p> {!rows.length && <p className="py-8 text-muted">Nothing yet.</p>} {groups.map((g) => ( <section key={g.day} className="mt-6"> <h2 className="mb-1 text-xs font-bold uppercase tracking-widest text-muted"> {g.day} </h2> <ul> {g.rows.map((r) => { const href = hrefFor(r, band.scheduling) const body = ( <> <span className="w-16 shrink-0 font-mono text-xs text-faint"> {time.format(r.createdAt)} </span> <Avatar name={displayName(r.user)} src={r.user ? avatarUrl(r.user) : null} size={24} className="self-center" /> <span> <strong>{displayName(r.user)}</strong> {r.summary} </span> </> ) return ( <li key={r.id} className="border-t border-line"> {href ? ( <Link href={href} className="flex min-h-12 items-baseline gap-3 py-2.5 no-underline hover:bg-panel-2" > {body} </Link> ) : ( <div className="flex min-h-12 items-baseline gap-3 py-2.5"> {body} </div> )} </li> ) })} </ul> </section> ))} </main> ) } |