Skip to content

Commit

Permalink
Ticketing price integration (#659)
Browse files Browse the repository at this point in the history
* Integrate ticket price field into ticket creation/list views, as well into ticket creation frontend.

* Enforce non-negative ticket prices at creation

* Add frontend checks for fractional/negative ticket count and cost.

* Prevent users from entering negative/fractional ticket counts/price for now.

---------

Co-authored-by: aviupadhyayula <aupadhy@gmail.com>
  • Loading branch information
2 people authored and rm03 committed Apr 16, 2024
1 parent 4f9253c commit 5df2079
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 19 deletions.
14 changes: 13 additions & 1 deletion backend/clubs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
DurationField,
ExpressionWrapper,
F,
Max,
Prefetch,
Q,
Sum,
Expand Down Expand Up @@ -2544,6 +2545,8 @@ def tickets(self, request, *args, **kwargs):
type: string
count:
type: integer
price:
type: number
available:
type: array
items:
Expand All @@ -2553,15 +2556,24 @@ def tickets(self, request, *args, **kwargs):
type: string
count:
type: integer
price:
type: number
---
"""
event = self.get_object()
tickets = Ticket.objects.filter(event=event)

totals = tickets.values("type").annotate(count=Count("type")).order_by("type")
# Take price of first ticket of given type for now
totals = (
tickets.values("type")
.annotate(price=Max("price"))
.annotate(count=Count("type"))
.order_by("type")
)
available = (
tickets.filter(owner__isnull=True, holder__isnull=True)
.values("type")
.annotate(price=Max("price"))
.annotate(count=Count("type"))
.order_by("type")
)
Expand Down
68 changes: 50 additions & 18 deletions frontend/components/ClubEditPage/TicketsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,32 @@ const TicketItem = ({
ticket,
changeName,
changeCount,
changePrice,
deleteTicket,
deletable,
index,
}): ReactElement => {
const [name, setName] = useState(ticket.name)
const [count, setCount] = useState(ticket.count)
const [price, setPrice] = useState(ticket.price)

const handleNameChange = (e) => {
setName(e.target.value)
changeName(e.target.value, index)
}

const handleCountChange = (e) => {
setCount(e.target.value)
changeCount(e.target.value, index)
let rounded = Math.round(parseFloat(e.target.value))
rounded = rounded < 0 ? 0 : rounded
setCount(rounded.toString())
changeCount(rounded.toString(), index)
}

const handlePriceChange = (e) => {
let rounded = Math.round(parseFloat(e.target.value) * 100) / 100
rounded = rounded < 0 ? 0 : rounded
setPrice(rounded.toString())
changePrice(rounded.toString(), index)
}

return (
Expand All @@ -95,6 +106,13 @@ const TicketItem = ({
placeholder="Ticket Count"
onChange={handleCountChange}
/>
<Input
type="number"
className="input"
value={price}
placeholder="Ticket Price"
onChange={handlePriceChange}
/>
<button
className="button is-danger"
disabled={!deletable}
Expand All @@ -110,6 +128,7 @@ const TicketItem = ({
type Ticket = {
name: string
count: string | null
price: string | null // Free if null
}

const TicketsModal = (props: { event: ClubEvent }): ReactElement => {
Expand All @@ -119,7 +138,7 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => {
const [submitting, setSubmitting] = useState(false)

const [tickets, setTickets] = useState<Ticket[]>([
{ name: 'Regular Ticket', count: null },
{ name: 'Regular Ticket', count: null, price: null },
])

const handleNameChange = (name, i) => {
Expand All @@ -134,6 +153,12 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => {
setTickets(ticks)
}

const handlePriceChange = (price, i) => {
const ticks = [...tickets]
ticks[i].price = price
setTickets(ticks)
}

const deleteTicket = (i) => {
const ticks = [...tickets]
ticks.splice(i, 1)
Expand All @@ -142,7 +167,7 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => {

const addNewTicket = () => {
const ticks = [...tickets]
ticks.push({ name: '', count: null })
ticks.push({ name: '', count: null, price: null })
setTickets(ticks)
}

Expand All @@ -151,24 +176,38 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => {
const quantities = tickets
.filter((ticket) => ticket.count != null)
.map((ticket) => {
return { type: ticket.name, count: parseInt(ticket.count || '') }
return {
type: ticket.name,
count: parseInt(ticket.count || ''),
price: parseFloat(ticket.price || ''),
}
})
doApiRequest(`/events/${id}/tickets/?format=json`, {
method: 'PUT',
body: {
quantities: quantities,
quantities,
},
}).then((res) => {
if (res.ok) {
notify(<>Tickets Created!</>, 'success')
setSubmitting(false)
} else {
notify(<>Error creating tickets</>, 'error')
setSubmitting(false)
}
})
notify(<>Tickets Created!</>, 'success')
setSubmitting(false)
}
}

const disableSubmit = tickets.some(
(ticket) =>
typeof ticket.name !== 'string' ||
ticket.count === null ||
!Number.isInteger(parseInt(ticket.count || '0')),
!Number.isInteger(parseInt(ticket.count || '0')) ||
parseInt(ticket.count || '0') < 0 ||
ticket.price === null ||
!Number.isFinite(parseFloat(ticket.price || '0')) ||
parseFloat(ticket.price || '0') < 0,
)

return (
Expand All @@ -181,15 +220,7 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => {
/>
<ModalBody>
<Title>{name}</Title>
<Text>
Create new tickets for this event. To be filled with actual
instructions. Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur.
</Text>
<Text>Create new tickets for this event.</Text>
<Line />
<SectionContainer>
<h1>Tickets</h1>
Expand All @@ -202,6 +233,7 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => {
deletable={tickets.length !== 1}
changeName={handleNameChange}
changeCount={handleCountChange}
changePrice={handlePriceChange}
deleteTicket={deleteTicket}
/>
))}
Expand Down

0 comments on commit 5df2079

Please sign in to comment.