From 20f68ab191492345ab2de3ec201a543e2cd65c39 Mon Sep 17 00:00:00 2001 From: Joy Liu <34288846+joyliu-q@users.noreply.github.com> Date: Sun, 21 Apr 2024 02:19:53 -0400 Subject: [PATCH] Add Group Discount to Create Ticket Flow and Auto Scroll Down (#669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add to cart feature (styling is borked) * :bug: Broken code * 🐛 fixed * :art: Readd event preview * :broom: Less jank way of doing group discount visibility * :art: Address comments and actually type things * :art: Address nit --------- Co-authored-by: Julian Weng Co-authored-by: Eunsoo Shin --- .../components/ClubEditPage/EventsCard.tsx | 13 +- .../components/ClubEditPage/TicketsModal.tsx | 232 ++++++---- frontend/pages/events/[id].tsx | 140 +++++- frontend/types.ts | 1 + frontend/utils/branding.tsx | 418 +++++++++--------- 5 files changed, 516 insertions(+), 288 deletions(-) diff --git a/frontend/components/ClubEditPage/EventsCard.tsx b/frontend/components/ClubEditPage/EventsCard.tsx index 02c0f50c5..43eb55365 100644 --- a/frontend/components/ClubEditPage/EventsCard.tsx +++ b/frontend/components/ClubEditPage/EventsCard.tsx @@ -1,6 +1,6 @@ import { Field } from 'formik' import moment from 'moment' -import React, { ReactElement, useState } from 'react' +import React, { ReactElement, useRef, useState } from 'react' import TimeAgo from 'react-timeago' import styled from 'styled-components' @@ -431,7 +431,7 @@ const CreateTickets = ({ event }: { event: ClubEvent }) => { closeModal={hideModal} marginBottom={false} > - + )} @@ -440,6 +440,7 @@ const CreateTickets = ({ event }: { event: ClubEvent }) => { export default function EventsCard({ club }: EventsCardProps): ReactElement { const [deviceContents, setDeviceContents] = useState({}) + const eventDetailsRef = useRef(null) const event = { ...deviceContents, @@ -465,13 +466,19 @@ export default function EventsCard({ club }: EventsCardProps): ReactElement { noun="Event" currentTitle={(obj) => (obj != null ? obj.name : 'Deleted Event')} onChange={(obj) => { + eventDetailsRef.current?.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }) setDeviceContents(obj) }} /> - +
+ +
) } diff --git a/frontend/components/ClubEditPage/TicketsModal.tsx b/frontend/components/ClubEditPage/TicketsModal.tsx index 8e08ec825..e966fc38b 100644 --- a/frontend/components/ClubEditPage/TicketsModal.tsx +++ b/frontend/components/ClubEditPage/TicketsModal.tsx @@ -51,40 +51,40 @@ const notify = ( toast[type](msg) } -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) - } +type TicketItemProps = { + ticket: Ticket + onChange?: (ticket: Ticket) => void + onDelete?: () => void + deletable: boolean +} - const handleCountChange = (e) => { - let rounded = Math.round(parseFloat(e.target.value)) - rounded = rounded < 0 ? 0 : rounded - setCount(rounded.toString()) - changeCount(rounded.toString(), index) - } +const TicketItem: React.FC = ({ + ticket: propTicket, + onChange, + onDelete, + deletable, +}) => { + const [ticket, setTicket] = useState(propTicket) + const [openGroupDiscount, setOpenGroupDiscount] = useState(false) - 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) + const resetGroupDiscount = () => { + setTicket({ + ...ticket, + groupDiscount: null, + groupNumber: null, + }) + onChange?.({ + ...ticket, + groupDiscount: null, + groupNumber: null, + }) + setOpenGroupDiscount(!openGroupDiscount) } return ( -
+
{ + setTicket({ ...ticket, name: e.target.value }) + onChange?.({ ...ticket, name: e.target.value }) + }} /> { + const count = e.target.value + setTicket({ ...ticket, count }) + onChange?.({ ...ticket, count }) + }} /> { + const price = e.target.value + setTicket({ ...ticket, price }) + onChange?.({ ...ticket, price }) + }} /> +
+
+
+
+ {openGroupDiscount ? ( + <> +
+ { + const groupDiscount = e.target.value + setTicket({ ...ticket, groupDiscount }) + onChange?.({ ...ticket, groupDiscount }) + }} + /> +
+ % Discount for +
+ { + const groupNumber = e.target.value + setTicket({ ...ticket, groupNumber }) + onChange?.({ ...ticket, groupNumber }) + }} + /> +
+ + + ) : ( + <> + + + )} +
+
) } @@ -129,46 +203,43 @@ type Ticket = { name: string count: string | null price: string | null // Free if null + groupDiscount: string | null // If null, no group discount + groupNumber: string | null // If null, no group discount } -const TicketsModal = (props: { event: ClubEvent }): ReactElement => { - const { event } = props +const TicketsModal = ({ + event, + onSuccessfulSubmit, +}: { + event: ClubEvent + onSuccessfulSubmit: () => void +}): ReactElement => { const { large_image_url, image_url, club_name, name, id } = event const [submitting, setSubmitting] = useState(false) const [tickets, setTickets] = useState([ - { name: 'Regular Ticket', count: null, price: null }, + { + name: 'Regular Ticket', + count: null, + price: null, + groupDiscount: null, + groupNumber: null, + }, ]) - const handleNameChange = (name, i) => { - const ticks = [...tickets] - ticks[i].name = name - setTickets(ticks) - } - - const handleCountChange = (count, i) => { - const ticks = [...tickets] - ticks[i].count = count - 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) - setTickets(ticks) - } - const addNewTicket = () => { const ticks = [...tickets] - ticks.push({ name: '', count: null, price: null }) - setTickets(ticks) + setTickets([ + ...ticks, + { + name: '', + count: null, + price: null, + groupDiscount: null, + groupNumber: null, + }, + ]) } const submit = () => { @@ -176,10 +247,17 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => { const quantities = tickets .filter((ticket) => ticket.count != null) .map((ticket) => { + const usingGroupPricing = ticket.groupDiscount && ticket.groupNumber return { type: ticket.name, - count: parseInt(ticket.count || ''), - price: parseFloat(ticket.price || ''), + count: parseInt(ticket.count ?? '0'), + price: parseFloat(ticket.price ?? '0'), + groupDiscount: usingGroupPricing + ? parseFloat(ticket.groupDiscount!) + : null, + groupNumber: usingGroupPricing + ? parseFloat(ticket.groupNumber!) + : null, } }) doApiRequest(`/events/${id}/tickets/?format=json`, { @@ -191,6 +269,7 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => { if (res.ok) { notify(<>Tickets Created!, 'success') setSubmitting(false) + onSuccessfulSubmit() } else { notify(<>Error creating tickets, 'error') setSubmitting(false) @@ -207,7 +286,8 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => { parseInt(ticket.count || '0') < 0 || ticket.price === null || !Number.isFinite(parseFloat(ticket.price || '0')) || - parseFloat(ticket.price || '0') < 0, + parseFloat(ticket.price || '0') < 0 || + (ticket.groupNumber != null && parseFloat(ticket.price || '0') < 0), ) return ( @@ -228,13 +308,17 @@ const TicketsModal = (props: { event: ClubEvent }): ReactElement => { {tickets.map((ticket, index) => ( 1} + onChange={(newTicket) => { + setTickets((t) => + t.map((t, i) => (i === index ? newTicket : t)), + ) + }} + onDelete={() => { + setTickets((t) => t.filter((_, i) => i !== index)) + }} /> ))} diff --git a/frontend/pages/events/[id].tsx b/frontend/pages/events/[id].tsx index ce77c4f44..b0032b563 100644 --- a/frontend/pages/events/[id].tsx +++ b/frontend/pages/events/[id].tsx @@ -2,6 +2,7 @@ import { DateTime, Settings } from 'luxon' import { GetServerSideProps, InferGetServerSidePropsType } from 'next' import Link from 'next/link' import React, { useState } from 'react' +import { toast } from 'react-toastify' import styled from 'styled-components' import { BaseLayout } from '~/components/BaseLayout' @@ -14,12 +15,18 @@ import { Title, } from '~/components/common' import { + ALLBIRDS_GRAY, + BODY_FONT, BORDER, + BORDER_RADIUS, CLUBS_BLUE, + CLUBS_GREY, CLUBS_LIGHT_BLUE, + FOCUS_GRAY, mediaMaxWidth, mediaMinWidth, PHONE, + WHITE, } from '~/constants' import { Club, ClubEvent, TicketAvailability } from '~/types' import { doApiRequest } from '~/utils' @@ -118,6 +125,86 @@ const Divider = styled.hr` margin: 20px 0; ` +const Input = styled.input` + border: 1px solid ${ALLBIRDS_GRAY}; + outline: none; + color: ${CLUBS_GREY}; + flex: 0 0 auto; + font-size: 1em; + padding: 8px 10px; + margin: 0px 5px 0px 0px; + background: ${WHITE}; + border-radius: ${BORDER_RADIUS}; + min-width: 50px; + font-family: ${BODY_FONT}; + &:hover, + &:active, + &:focus { + background: ${FOCUS_GRAY}; + } +` + +type Ticket = { + type: string + price: string + max: string + count: number | null +} + +type TicketItemProps = { + ticket: Ticket + name: string + price: string + max: string + onCountChange: (newCount: number) => void +} + +const TicketItem: React.FC = ({ + ticket, + name, + price, + max, + onCountChange, +}) => { + const [count, setCount] = useState(ticket.count) + const handleCountChange = (e: React.ChangeEvent) => { + // Round to nearest integer and clamp to min/max + const value = Math.max( + 0, + Math.min(Math.round(parseFloat(e.target.value)), parseInt(max, 10)), + ) + setCount(value) + onCountChange(value) + } + + return ( +
+
+

+ {name} - ${price} +

+ +
+
+ ) +} + const EventPage: React.FC = ({ baseProps, club, @@ -136,10 +223,20 @@ const EventPage: React.FC = ({ total: cur.count, available: tickets.available.find((t) => t.type === cur.type)?.count ?? 0, + price: cur.price, }, }), {}, - ) as Record + ) as Record + + const [order, setOrder] = useState( + Object.entries(ticketMap).map(([type, counts]) => ({ + type, + price: counts.price.toString(), + max: counts.total.toString(), + count: 0, + })), + ) const totalAvailableTickets = Object.values(ticketMap) .map((k) => k.available) @@ -150,8 +247,47 @@ const EventPage: React.FC = ({ setShowTicketModal(false)} + marginBottom={false} > -

Get Tickets

+ Get Tickets + {order.map((ticket, index) => ( + { + const ticks = [...order] + ticks[index].count = count + setOrder(ticks) + }} + /> + ))} +
diff --git a/frontend/types.ts b/frontend/types.ts index e3efd4c27..d994a6836 100644 --- a/frontend/types.ts +++ b/frontend/types.ts @@ -406,6 +406,7 @@ export type ApplicationResponse = { export type TicketEntry = { type: string count: number + price: number } export type TicketAvailability = { diff --git a/frontend/utils/branding.tsx b/frontend/utils/branding.tsx index 06c9ed995..325688184 100644 --- a/frontend/utils/branding.tsx +++ b/frontend/utils/branding.tsx @@ -7,39 +7,39 @@ const { publicRuntimeConfig } = getConfig() const site = publicRuntimeConfig.NEXT_PUBLIC_SITE_NAME const sites = { - clubs: { - SITE_NAME: 'Penn Clubs', - SCHOOL_NAME: 'University of Pennsylvania', - SITE_SUBTITLE: 'Student Organizations at the University of Pennsylvania', - DOMAIN: 'pennclubs.com', - - OBJECT_NAME_PLURAL: 'clubs', - OBJECT_NAME_LONG_PLURAL: 'student organizations', - OBJECT_NAME_SINGULAR: 'club', - - OBJECT_NAME_TITLE: 'Clubs', - OBJECT_NAME_TITLE_SINGULAR: 'Club', - - SITE_LOGO: '/static/img/peoplelogo.png', - LOGO_BACKGROUND_IMAGE: null, - HEADER_BACKGROUND_IMAGE: null, - HEADER_OVERLAY: null, - SITE_FAVICON: '/static/favicon.ico', - SITE_TAGLINE: 'Find your people!', - OG_IMAGE: - 'https://pennlabs-assets.s3.amazonaws.com/metadata-images/penn-clubs.png', - - APPROVAL_AUTHORITY: 'Office of Student Affairs', - APPROVAL_AUTHORITY_URL: 'https://osa.vpul.upenn.edu/', - - FIELD_PARTICIPATION_LABEL: 'How to Get Involved', - - OBJECT_URL_SLUG: 'club', - OBJECT_TAB_MEMBERSHIP_LABEL: 'Membership', - OBJECT_TAB_RECRUITMENT_LABEL: 'Recruitment', - OBJECT_TAB_ADMISSION_LABEL: 'Admission', - OBJECT_TAB_FILES_DESCRIPTION: - 'You can upload club constitutions here. Please upload your club constitution in pdf or docx format.', + clubs: { + SITE_NAME: 'Penn Clubs', + SCHOOL_NAME: 'University of Pennsylvania', + SITE_SUBTITLE: 'Student Organizations at the University of Pennsylvania', + DOMAIN: 'pennclubs.com', + + OBJECT_NAME_PLURAL: 'clubs', + OBJECT_NAME_LONG_PLURAL: 'student organizations', + OBJECT_NAME_SINGULAR: 'club', + + OBJECT_NAME_TITLE: 'Clubs', + OBJECT_NAME_TITLE_SINGULAR: 'Club', + + SITE_LOGO: '/static/img/peoplelogo.png', + LOGO_BACKGROUND_IMAGE: null, + HEADER_BACKGROUND_IMAGE: null, + HEADER_OVERLAY: null, + SITE_FAVICON: '/static/favicon.ico', + SITE_TAGLINE: 'Find your people!', + OG_IMAGE: + 'https://pennlabs-assets.s3.amazonaws.com/metadata-images/penn-clubs.png', + + APPROVAL_AUTHORITY: 'Office of Student Affairs', + APPROVAL_AUTHORITY_URL: 'https://osa.vpul.upenn.edu/', + + FIELD_PARTICIPATION_LABEL: 'How to Get Involved', + + OBJECT_URL_SLUG: 'club', + OBJECT_TAB_MEMBERSHIP_LABEL: 'Membership', + OBJECT_TAB_RECRUITMENT_LABEL: 'Recruitment', + OBJECT_TAB_ADMISSION_LABEL: 'Admission', + OBJECT_TAB_FILES_DESCRIPTION: + 'You can upload club constitutions here. Please upload your club constitution in pdf or docx format.', CONTACT_EMAIL: 'contact@pennclubs.com', SUPPORT_EMAIL: 'vpul-orgs@pobox.upenn.edu', @@ -80,175 +80,175 @@ const sites = { // show feedback icon on bottom right SHOW_FEEDBACK: true, - MEMBERSHIP_ROLE_NAMES: { 0: 'Owner', 10: 'Officer', 20: 'Member' }, - OBJECT_MEMBERSHIP_LABEL: 'Members', - OBJECT_MEMBERSHIP_LABEL_LOWERCASE: "member's", - OBJECT_INVITE_LABEL: 'Members', - OBJECT_EVENT_TYPES: [ - ClubEventType.RECRUITMENT, - ClubEventType.GBM, - ClubEventType.SPEAKER, - ClubEventType.FAIR, - ClubEventType.OTHER, - ], - - FORM_DESCRIPTION_EXAMPLES: 'Penn Labs', - FORM_TAG_DESCRIPTION: - 'You will need to at least specify either the Undergraduate or Graduate tag.', - FORM_LOGO_DESCRIPTION: - 'Changing this field will require reapproval from the Office of Student Affairs.', - FORM_TARGET_DESCRIPTION: ( - <> - Does your club restrict membership to certain student groups? If - you are only looking for certain student groups during your recruitment - process, please specify those groups here. Otherwise, we will assume - that you are targeting the general student population. - - ), - OBJECT_MEMBERSHIP_DEFAULT_TITLE: 'Member', - CLUB_EMPTY_STATE: ( - <> - Looking for university resources? Check out{' '} - Hub@Penn! - - ), - - PARTNER_LOGOS: [ - { - name: 'Student Activities Council', - image: '/static/img/collaborators/sac.png', - url: 'https://sacfunded.net/', - }, - { - name: 'Undergraduate Assembly', - image: '/static/img/collaborators/ua.png', - url: 'https://pennua.org/', - height: 80, - }, - { - name: 'Office of Student Affairs', - image: '/static/img/collaborators/osa.png', - url: 'https://www.vpul.upenn.edu/osa/', - className: 'mr-4', - }, - { - name: 'Engineering Student Activities Council', - image: '/static/img/collaborators/esac.png', - url: 'https://esac.squarespace.com/', - height: 80, - }, - ], - GA_TRACKING_CODE: 'UA-21029575-14', - FAIR_NAME: 'activities', - FAIR_NAME_CAPITALIZED: 'Activities', - }, - fyh: { - SITE_NAME: 'Hub@Penn', - SCHOOL_NAME: 'University of Pennsylvania', - SITE_SUBTITLE: 'Student Resources at the University of Pennsylvania', - DOMAIN: 'hub.provost.upenn.edu', - - OBJECT_NAME_PLURAL: 'resources', - OBJECT_NAME_LONG_PLURAL: 'university resources', - OBJECT_NAME_SINGULAR: 'resource', - - OBJECT_NAME_TITLE: 'Resources', - OBJECT_NAME_TITLE_SINGULAR: 'Resource', - - SITE_LOGO: '/static/img/penn_shield.png', - LOGO_BACKGROUND_IMAGE: '/static/img/penn_header_fade.png', - HEADER_BACKGROUND_IMAGE: '/static/img/hub_banner.png', - HEADER_OVERLAY: '/static/img/platform-start-point.png', - SITE_FAVICON: '/static/penn_favicon.ico', - SITE_TAGLINE: - "Find the support resources you need on and around Penn's campus!", - OG_IMAGE: - 'https://pennlabs-assets.s3.amazonaws.com/metadata-images/hub-at-penn.png', - - APPROVAL_AUTHORITY: 'Hub@Penn administrators', - APPROVAL_AUTHORITY_URL: '/faq', - - FIELD_PARTICIPATION_LABEL: 'Services Offered', - - OBJECT_URL_SLUG: 'org', - OBJECT_TAB_MEMBERSHIP_LABEL: 'Admins', - OBJECT_TAB_RECRUITMENT_LABEL: 'Mailing List', - OBJECT_TAB_ADMISSION_LABEL: 'Usage', - OBJECT_TAB_FILES_DESCRIPTION: null, - OBJECT_EVENT_TYPES: [ - ClubEventType.SOCIAL, - ClubEventType.CAREER, - ClubEventType.SPEAKER, - ClubEventType.FAIR, - ClubEventType.OTHER, - ], + MEMBERSHIP_ROLE_NAMES: { 0: 'Owner', 10: 'Officer', 20: 'Member' }, + OBJECT_MEMBERSHIP_LABEL: 'Members', + OBJECT_MEMBERSHIP_LABEL_LOWERCASE: "member's", + OBJECT_INVITE_LABEL: 'Members', + OBJECT_EVENT_TYPES: [ + ClubEventType.RECRUITMENT, + ClubEventType.GBM, + ClubEventType.SPEAKER, + ClubEventType.FAIR, + ClubEventType.OTHER, + ], + + FORM_DESCRIPTION_EXAMPLES: 'Penn Labs', + FORM_TAG_DESCRIPTION: + 'You will need to at least specify either the Undergraduate or Graduate tag.', + FORM_LOGO_DESCRIPTION: + 'Changing this field will require reapproval from the Office of Student Affairs.', + FORM_TARGET_DESCRIPTION: ( + <> + Does your club restrict membership to certain student groups? If + you are only looking for certain student groups during your recruitment + process, please specify those groups here. Otherwise, we will assume + that you are targeting the general student population. + + ), + OBJECT_MEMBERSHIP_DEFAULT_TITLE: 'Member', + CLUB_EMPTY_STATE: ( + <> + Looking for university resources? Check out{' '} + Hub@Penn! + + ), + + PARTNER_LOGOS: [ + { + name: 'Student Activities Council', + image: '/static/img/collaborators/sac.png', + url: 'https://sacfunded.net/', + }, + { + name: 'Undergraduate Assembly', + image: '/static/img/collaborators/ua.png', + url: 'https://pennua.org/', + height: 80, + }, + { + name: 'Office of Student Affairs', + image: '/static/img/collaborators/osa.png', + url: 'https://www.vpul.upenn.edu/osa/', + className: 'mr-4', + }, + { + name: 'Engineering Student Activities Council', + image: '/static/img/collaborators/esac.png', + url: 'https://esac.squarespace.com/', + height: 80, + }, + ], + GA_TRACKING_CODE: 'UA-21029575-14', + FAIR_NAME: 'activities', + FAIR_NAME_CAPITALIZED: 'Activities', + }, + fyh: { + SITE_NAME: 'Hub@Penn', + SCHOOL_NAME: 'University of Pennsylvania', + SITE_SUBTITLE: 'Student Resources at the University of Pennsylvania', + DOMAIN: 'hub.provost.upenn.edu', + + OBJECT_NAME_PLURAL: 'resources', + OBJECT_NAME_LONG_PLURAL: 'university resources', + OBJECT_NAME_SINGULAR: 'resource', + + OBJECT_NAME_TITLE: 'Resources', + OBJECT_NAME_TITLE_SINGULAR: 'Resource', + + SITE_LOGO: '/static/img/penn_shield.png', + LOGO_BACKGROUND_IMAGE: '/static/img/penn_header_fade.png', + HEADER_BACKGROUND_IMAGE: '/static/img/hub_banner.png', + HEADER_OVERLAY: '/static/img/platform-start-point.png', + SITE_FAVICON: '/static/penn_favicon.ico', + SITE_TAGLINE: + "Find the support resources you need on and around Penn's campus!", + OG_IMAGE: + 'https://pennlabs-assets.s3.amazonaws.com/metadata-images/hub-at-penn.png', + + APPROVAL_AUTHORITY: 'Hub@Penn administrators', + APPROVAL_AUTHORITY_URL: '/faq', + + FIELD_PARTICIPATION_LABEL: 'Services Offered', + + OBJECT_URL_SLUG: 'org', + OBJECT_TAB_MEMBERSHIP_LABEL: 'Admins', + OBJECT_TAB_RECRUITMENT_LABEL: 'Mailing List', + OBJECT_TAB_ADMISSION_LABEL: 'Usage', + OBJECT_TAB_FILES_DESCRIPTION: null, + OBJECT_EVENT_TYPES: [ + ClubEventType.SOCIAL, + ClubEventType.CAREER, + ClubEventType.SPEAKER, + ClubEventType.FAIR, + ClubEventType.OTHER, + ], CONTACT_EMAIL: 'hub.provost@upenn.edu', SUPPORT_EMAIL: 'hubcommunications@lists.upenn.edu', FEEDBACK_URL: 'https://airtable.com/shrv4RfYIddU1i9o6', - CLUB_FIELDS: [ - 'appointment_needed', - 'available_virtually', - 'signature_events', - 'student_types', - 'target_schools', - ], - SHOW_MEMBERS: false, - SHOW_MEMBERSHIP_REQUEST: false, - SHOW_RANK_ALGORITHM: false, - SHOW_ACCESSIBILITY: true, - SHOW_ADDITIONAL_LINKS: false, - SHOW_LEAVE_CONFIRMATION: false, - SHOW_SEARCHBAR_TOP: true, - SHOW_APPLICATIONS: false, - SHOW_ORG_MANAGEMENT: false, - SHOW_FEEDBACK: true, - - MEMBERSHIP_ROLE_NAMES: { 0: 'Owner', 10: 'Editor' }, - OBJECT_MEMBERSHIP_LABEL: 'Staff', - OBJECT_MEMBERSHIP_LABEL_LOWERCASE: 'staff', - OBJECT_INVITE_LABEL: 'Editor', - - FORM_DESCRIPTION_EXAMPLES: - 'Office of New Student Orientation & Academic Initiative - NSOAI', - FORM_TAG_DESCRIPTION: - 'Tags will allow students to find your resource while filtering Hub@Penn. Select as many as apply.', - FORM_LOGO_DESCRIPTION: 'Upload your approved Penn logo.', - FORM_TARGET_DESCRIPTION: ( - <> - - Does your resource apply to all undergraduate, graduate, and - professional Penn students? - {' '} - - ), - OBJECT_MEMBERSHIP_DEFAULT_TITLE: '', - CLUB_EMPTY_STATE: ( - <> - Looking for student organizations? Check out{' '} - Penn Clubs! - - ), - - PARTNER_LOGOS: [ - { - name: 'University Life', - image: '/static/img/collaborators/vpul.png', - url: 'https://home.vpul.upenn.edu/', - className: 'mr-4 mb-4', - }, - { - name: 'New Student Orientation and Academic Initatives', - image: '/static/img/collaborators/nsoai.png', - url: 'https://www.nso.upenn.edu/', - className: 'mr-4 mb-4', - }, - ], - GA_TRACKING_CODE: 'UA-21029575-19', - FAIR_NAME: 'resource', - FAIR_NAME_CAPITALIZED: 'Resource', - }, + CLUB_FIELDS: [ + 'appointment_needed', + 'available_virtually', + 'signature_events', + 'student_types', + 'target_schools', + ], + SHOW_MEMBERS: false, + SHOW_MEMBERSHIP_REQUEST: false, + SHOW_RANK_ALGORITHM: false, + SHOW_ACCESSIBILITY: true, + SHOW_ADDITIONAL_LINKS: false, + SHOW_LEAVE_CONFIRMATION: false, + SHOW_SEARCHBAR_TOP: true, + SHOW_APPLICATIONS: false, + SHOW_ORG_MANAGEMENT: false, + SHOW_FEEDBACK: true, + + MEMBERSHIP_ROLE_NAMES: { 0: 'Owner', 10: 'Editor' }, + OBJECT_MEMBERSHIP_LABEL: 'Staff', + OBJECT_MEMBERSHIP_LABEL_LOWERCASE: 'staff', + OBJECT_INVITE_LABEL: 'Editor', + + FORM_DESCRIPTION_EXAMPLES: + 'Office of New Student Orientation & Academic Initiative - NSOAI', + FORM_TAG_DESCRIPTION: + 'Tags will allow students to find your resource while filtering Hub@Penn. Select as many as apply.', + FORM_LOGO_DESCRIPTION: 'Upload your approved Penn logo.', + FORM_TARGET_DESCRIPTION: ( + <> + + Does your resource apply to all undergraduate, graduate, and + professional Penn students? + {' '} + + ), + OBJECT_MEMBERSHIP_DEFAULT_TITLE: '', + CLUB_EMPTY_STATE: ( + <> + Looking for student organizations? Check out{' '} + Penn Clubs! + + ), + + PARTNER_LOGOS: [ + { + name: 'University Life', + image: '/static/img/collaborators/vpul.png', + url: 'https://home.vpul.upenn.edu/', + className: 'mr-4 mb-4', + }, + { + name: 'New Student Orientation and Academic Initatives', + image: '/static/img/collaborators/nsoai.png', + url: 'https://www.nso.upenn.edu/', + className: 'mr-4 mb-4', + }, + ], + GA_TRACKING_CODE: 'UA-21029575-19', + FAIR_NAME: 'resource', + FAIR_NAME_CAPITALIZED: 'Resource', + }, } export const SITE_ID = site @@ -281,18 +281,18 @@ export const HEADER_OVERLAY = sites[site].HEADER_OVERLAY export const FIELD_PARTICIPATION_LABEL = sites[site].FIELD_PARTICIPATION_LABEL export const OBJECT_URL_SLUG = sites[site].OBJECT_URL_SLUG export const OBJECT_TAB_MEMBERSHIP_LABEL = - sites[site].OBJECT_TAB_MEMBERSHIP_LABEL + sites[site].OBJECT_TAB_MEMBERSHIP_LABEL export const OBJECT_TAB_RECRUITMENT_LABEL = - sites[site].OBJECT_TAB_RECRUITMENT_LABEL + sites[site].OBJECT_TAB_RECRUITMENT_LABEL export const OBJECT_TAB_ADMISSION_LABEL = sites[site].OBJECT_TAB_ADMISSION_LABEL export const OBJECT_TAB_FILES_DESCRIPTION = - sites[site].OBJECT_TAB_FILES_DESCRIPTION + sites[site].OBJECT_TAB_FILES_DESCRIPTION export const SHOW_MEMBERS = sites[site].SHOW_MEMBERS export const SHOW_MEMBERSHIP_REQUEST = sites[site].SHOW_MEMBERSHIP_REQUEST export const SHOW_RANK_ALGORITHM = sites[site].SHOW_RANK_ALGORITHM export const MEMBERSHIP_ROLE_NAMES: { [key: number]: string } = - sites[site].MEMBERSHIP_ROLE_NAMES + sites[site].MEMBERSHIP_ROLE_NAMES export const SHOW_ACCESSIBILITY = sites[site].SHOW_ACCESSIBILITY export const SHOW_ADDITIONAL_LINKS = sites[site].SHOW_ADDITIONAL_LINKS export const SHOW_LEAVE_CONFIRMATION = sites[site].SHOW_LEAVE_CONFIRMATION @@ -303,9 +303,9 @@ export const SHOW_FEEDBACK = sites[site].SHOW_FEEDBACK export const OBJECT_MEMBERSHIP_LABEL = sites[site].OBJECT_MEMBERSHIP_LABEL export const OBJECT_MEMBERSHIP_LABEL_LOWERCASE = - sites[site].OBJECT_MEMBERSHIP_LABEL_LOWERCASE + sites[site].OBJECT_MEMBERSHIP_LABEL_LOWERCASE export const OBJECT_MEMBERSHIP_DEFAULT_TITLE = - sites[site].OBJECT_MEMBERSHIP_DEFAULT_TITLE + sites[site].OBJECT_MEMBERSHIP_DEFAULT_TITLE export const OBJECT_EVENT_TYPES = new Set(sites[site].OBJECT_EVENT_TYPES) export const CLUB_EMPTY_STATE = sites[site].CLUB_EMPTY_STATE @@ -313,9 +313,9 @@ export const PARTNER_LOGOS = sites[site].PARTNER_LOGOS export const CLUB_FIELDS = new Set(sites[site].CLUB_FIELDS) export const ALL_CLUB_FIELDS = new Set( - Object.values(sites) - .map(({ CLUB_FIELDS }) => CLUB_FIELDS) - .flat(), + Object.values(sites) + .map(({ CLUB_FIELDS }) => CLUB_FIELDS) + .flat(), ) export const GA_TRACKING_CODE = sites[site].GA_TRACKING_CODE @@ -323,11 +323,11 @@ export const FAIR_NAME = sites[site].FAIR_NAME export const FAIR_NAME_CAPITALIZED = sites[site].FAIR_NAME_CAPITALIZED export const FORM_DESCRIPTION_EXAMPLES: ReactNode = - sites[site].FORM_DESCRIPTION_EXAMPLES + sites[site].FORM_DESCRIPTION_EXAMPLES export const FORM_TAG_DESCRIPTION: ReactNode = sites[site].FORM_TAG_DESCRIPTION export const FORM_LOGO_DESCRIPTION: ReactNode = - sites[site].FORM_LOGO_DESCRIPTION + sites[site].FORM_LOGO_DESCRIPTION export const FORM_TARGET_DESCRIPTION: ReactNode = - sites[site].FORM_TARGET_DESCRIPTION + sites[site].FORM_TARGET_DESCRIPTION export const OBJECT_INVITE_LABEL = sites[site].OBJECT_INVITE_LABEL