Skip to content

Commit

Permalink
Feature/attendee-list (#182)
Browse files Browse the repository at this point in the history
* Date formatting safety and ordering
* Live Stats
  • Loading branch information
bardsley authored Sep 24, 2024
1 parent 10ffe10 commit f51afce
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 17 deletions.
11 changes: 2 additions & 9 deletions app/admin/ticketing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Layout from "@components/layout/layout";
import { Container } from "@components/layout/container";
import Navigation from "@components/admin/navigation";
import TicketList from "@components/admin/ticketList";
import StatBlock, {StatLine} from "@components/admin/statBlock";
import AttendeeStats from "@components/admin/attendeeStats";
import { admin_ticketing_url } from "@lib/urls";

export default async function AdminDashboardPage() {
Expand All @@ -13,13 +13,6 @@ export default async function AdminDashboardPage() {
{ name: 'Ticketing', href: admin_ticketing_url, current: true },
]

const stats = [
{ name: 'Total', value: '405', unit: 'tickets' },
{ name: 'Today', value: '10', unit: 'tickets' },
{ name: 'Meal prefs', value: '3', unit: 'set' },
{ name: 'Dinner Prefs', value: '2.5%', unit: 'complete' },
] as StatLine[]

return (<Layout>
<section className={`flex-1 relative transition duration-150 ease-out body-font overflow-hidden bg-none text-white`}>
{" "}
Expand All @@ -32,7 +25,7 @@ export default async function AdminDashboardPage() {
<p className="">See recent sales, mark as attended and give pass etc</p>
</Container>
<Container width="large" padding="tight" className={`flex-1 pb-2`} size="none">
<StatBlock stats={stats}></StatBlock>
<AttendeeStats/>
</Container>

<Container width="large" padding="tight" className={`flex-1 pb-2`} size="none">
Expand Down
6 changes: 3 additions & 3 deletions app/api/admin/attendees/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createClerkClient } from '@clerk/backend';
import { auth } from '@clerk/nextjs/server';
import { NextRequest } from 'next/server';
import { currentUser } from '@clerk/nextjs/server';
import { guaranteeISOstringFromDate } from '@lib/useful';

export async function GET() {
const {userId} = auth();
Expand Down Expand Up @@ -32,13 +33,12 @@ export async function GET() {
const name_changed = attendee.transferred && attendee.transferred.ticket_number == attendee.ticket_number
const transferred_in = !name_changed && attendee.history
// console.log(attendee.full_name,attendee.ticket_number,attendee.transferred?.ticket_number, attendee.transferred?.ticket_number != attendee.ticket_number)

return {
name: attendee.full_name,
email: attendee.email,
checkin_at: attendee.ticket_used,
passes: attendee.line_items.map((item) => item.description),
purchased_at: new Date(parseInt(attendee.purchase_date) * 1000).toISOString(),
purchased_date: guaranteeISOstringFromDate(attendee.purchase_date),
ticket_number: attendee.ticket_number,
active: attendee.active,
status: attendee.status,
Expand All @@ -48,7 +48,7 @@ export async function GET() {
name_changed: name_changed,
transferred: attendee.transferred,
history: attendee.history,

meal_preferences: attendee.meal_preferences,
};
})

Expand Down
36 changes: 36 additions & 0 deletions components/admin/attendeeStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client'
import useSWR from "swr";
import { fetcher } from "@lib/fetchers";
import StatBlock, { StatLine } from "@components/admin/statBlock";
import { subWeeks } from 'date-fns'
import { guaranteeTimestampFromDate } from '@lib/useful';


export default function AttendeeStats() {
const {data, error, isLoading} = useSWR("/api/admin/attendees", fetcher, { keepPreviousData: false });

const stats = isLoading ?
[
{ name: 'Total', value: 'Loading...', unit: '' },
{ name: 'Today', value: 'Loading...', unit: '' },
{ name: 'Meal prefs', value: 'Loading...', unit: '' },
{ name: 'Dinner Prefs', value: 'Loading...', unit: '' }
] as StatLine[]
: error ? [
{ name: 'Total', value: "Error", unit: '' },
{ name: 'Today', value: 'Error', unit: '' },
{ name: 'Meal prefs', value: 'Error', unit: '' },
{ name: 'Dinner Prefs', value: 'Error', unit: '' }
] as StatLine[]
: [
{ name: 'Total', value: data.attendees.length, unit: 'tickets' },
{ name: 'This week', value: data.attendees.filter((row)=>{
return guaranteeTimestampFromDate(row.purchased_date) > subWeeks(new Date(),1).getTime()
}).length, unit: 'tickets' },
{ name: 'Meal prefs', value: data.attendees.filter((row)=>{return row.meal_preferences}).length, unit: 'set' },
{ name: 'Dinner Prefs', value: 'Unknown', unit: '' }
] as StatLine[]

return <StatBlock stats={stats}></StatBlock>
// return <div>{JSON.stringify(stats,null,2)}</div>
}
3 changes: 2 additions & 1 deletion components/admin/lists/ticketRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const TicketRow = ({attendee,setActiveTicket, setNameChangeModalActive, s
<td className="w-full max-w-0 py-4 pl-4 pr-3 text-sm font-medium sm:w-auto sm:max-w-none sm:pl-2 vertical-align-top">
<Link href={`/admin/ticketing/ticket/${attendee.ticket_number}/${attendee.email}`} className={`${attendee.active ? 'text-chillired-600 hover:text-chillired-700' : "text-gray-600"}`}>
<span className="text-lg leading-6 sm:text-base md:text-base">{attendee.name}</span><br/>
<span className="text-xs leading-6 text-gray-300">#{attendee.ticket_number}</span>
<span className="text-xs leading-6 text-gray-300">#{attendee.ticket_number}</span><br/>
<span className="text-xs leading-6 text-gray-300">{format(Date.parse(attendee.purchased_date),'h:mmaaa EEE do LLL yyyy')}</span>
</Link>
<dl className="font-normal lg:hidden text-inherit">
<dt className="sr-only">Email</dt>
Expand Down
1 change: 0 additions & 1 deletion components/admin/statBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export type StatLine = {
export default function StatBlock({stats}: {stats: StatLine[]}) {
const [hidden,setHidden] = useState(false);


const toggelButton = <div className="w-full flex absolute -top-12 right-2 justify-end"><button className="border border-gray-600 text-gray-400 hover:text-gray-50 hover:border-gray-100 rounded-md px-3 pt-1 mt-3 text-xs" onClick={()=>{setHidden(!hidden)}}>{ hidden ? "Show" : "Hide"} Stats</button></div>
const statBlock = (<div className="mx-auto max-w-7xl bg-richblack-500 rounded-lg">
<div className="grid gap-px bg-red/5 grid-cols-2 md:grid-cols-4">
Expand Down
5 changes: 3 additions & 2 deletions components/admin/ticket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Link from "next/link";
import NameChangeModal from './modals/nameChangeModal';
import TicketTransferModal from './modals/ticketTransferModal';
import { fetcher } from "@lib/fetchers";
import { guaranteeTimestampFromDate } from "@lib/useful";

const accessToThings = (access:number[],) => {
let products = []
Expand Down Expand Up @@ -52,7 +53,7 @@ export default function TicketView({ticket_number, email}: {ticket_number: strin
if(ticket) {

const ticketUsage = ticket.active ? ticket.ticket_used ? `Used @ ${format(ticket.ticket_used,' eee')}` : "Active & Unused" : "Deactivated"
const purchaseData = fromUnixTime(ticket.purchase_date)
const purchaseDate = fromUnixTime(guaranteeTimestampFromDate(ticket.purchase_date) / 1000)
const purchasedThings = ticket.line_items ? ticket.line_items.map((item) => {return `${item.description} £${item.amount_total / 100}`}): []

return ( data && <div className="w-full md:flex justify-center items-start gap-3 max-w-full">
Expand Down Expand Up @@ -104,7 +105,7 @@ export default function TicketView({ticket_number, email}: {ticket_number: strin
<div className="rounded-lg shadow-lg bg-richblack-600 border-gray-500 border my-4">
<h3 className="font-bold uppercase border-b border-gray-500 py-2 px-4">Purchase</h3>
<div className="p-4">
<Info label="Purchased" info={format(purchaseData,'HH:mm do MMMM yyyy ')} options={{size: 'xl'}}/>
<Info label="Purchased" info={format(purchaseDate,'HH:mm do MMMM yyyy ')} options={{size: 'xl'}}/>
<Info label="Bought" info={purchasedThings} options={{size: 'lg'}} />
{ ticket.promo_code ? <Info label="Promo Code" info={ticket.promo_code} /> : null }
<Info label="Payment Method" info={ticket.status.replace('paid_','')} options={{size: '2xl'}} />
Expand Down
3 changes: 2 additions & 1 deletion components/admin/ticketList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default function TicketList() {
else {

const sortedAttendees = attendees.sort((a, b) => {
// console.log("Sorting",sortByField,a,b)
if (sortByDirection === 'desc') {
return (0 - (a[sortByField] > b[sortByField] ? 1 : -1))
} else {
Expand Down Expand Up @@ -120,7 +121,7 @@ export default function TicketList() {
<FilterLabel fieldname={"name"} addFilterFunction={addFilter}>
<span className={`${labelClassNames} sm:pl-2 `}>Name
<span className='sm:hidden'> & Details</span>
<span className='hidden sm:inline lg:hidden'>& Email</span>
<span className='sm:inline lg:hidden'>& Email</span>
</span>
</FilterLabel>

Expand Down
12 changes: 12 additions & 0 deletions lib/useful.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
export const deepCopy = (object: any) => {
return JSON.parse(JSON.stringify(object))
}

export const guaranteeISOstringFromDate = (date: string | number) => {
return isNaN(Date.parse(date as string))
? new Date(parseInt(date as string) * 1000).toISOString()
: new Date(Date.parse(date as string))
}

export const guaranteeTimestampFromDate = (date: string | number) => {
return isNaN(Date.parse(date as string))
? parseInt(date as string) * 1000
: Date.parse(date as string)
}

0 comments on commit f51afce

Please sign in to comment.