├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── discord.xml ├── evental.iml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml ├── prettier.xml └── vcs.xml ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── mobile ├── .env.local.example ├── .gitignore ├── Main.tsx ├── README.md ├── app.json ├── babel.config.js ├── components │ ├── form │ │ └── NotificationPreferenceForm.tsx │ └── navigation │ │ ├── EventStackNavigation.tsx │ │ ├── SettingsStackNavigation.tsx │ │ └── index.tsx ├── hooks │ └── useRefreshOnFocus.ts ├── metro.config.js ├── package.json ├── screens │ ├── SignIn.tsx │ ├── events │ │ ├── UpcomingEvents.tsx │ │ ├── ViewEvent.tsx │ │ ├── attendees │ │ │ └── ViewAttendee.tsx │ │ └── sessions │ │ │ └── ViewSession.tsx │ └── settings │ │ ├── NotificationPreferences.tsx │ │ └── Settings.tsx └── tsconfig.json ├── package.json ├── shared ├── .env.direct.local.example ├── .env.local.example ├── api │ └── index.ts ├── barrel.config.json ├── db │ ├── client.ts │ ├── index.ts │ └── prisma │ │ ├── fake.ts │ │ ├── schema.prisma │ │ └── seed.ts ├── hooks │ ├── index.ts │ ├── mutations │ │ ├── useAcceptOrganizerInvite.ts │ │ ├── useAcceptRoleInvite.ts │ │ ├── useEditNotificationPreferences.ts │ │ ├── useEditUserSettings.ts │ │ ├── useEventRegister.ts │ │ ├── useLeaveEvent.ts │ │ ├── useLeaveSession.ts │ │ ├── useRequestPasswordReset.ts │ │ ├── useRequestVerification.ts │ │ ├── useResetPassword.ts │ │ ├── useSignIn.ts │ │ ├── useSignOut.ts │ │ ├── useSignUp.ts │ │ └── useVerifyEmail.ts │ └── queries │ │ ├── useAttendee.ts │ │ ├── useAttendees.ts │ │ ├── useAttendeesByName.ts │ │ ├── useAttendeesByRole.ts │ │ ├── useAttendingEvents.ts │ │ ├── useEvent.ts │ │ ├── useEventMessage.ts │ │ ├── useEventMessages.ts │ │ ├── useIsAttendee.ts │ │ ├── useIsFounder.ts │ │ ├── useIsOrganizer.ts │ │ ├── useIsSessionAttendee.ts │ │ ├── useNotificationPreferences.ts │ │ ├── useOrganizers.ts │ │ ├── useOrganizingEvents.ts │ │ ├── usePage.ts │ │ ├── usePages.ts │ │ ├── useRole.ts │ │ ├── useRoles.ts │ │ ├── useSession.ts │ │ ├── useSessionAttendee.ts │ │ ├── useSessionAttendees.ts │ │ ├── useSessionCategories.ts │ │ ├── useSessionCategory.ts │ │ ├── useSessionRoleAttendees.ts │ │ ├── useSessions.ts │ │ ├── useSessionsByCategory.ts │ │ ├── useSessionsByDate.ts │ │ ├── useSessionsByVenue.ts │ │ ├── useUnclaimedUser.ts │ │ ├── useUpcomingEvents.ts │ │ ├── useUser.ts │ │ ├── useUserById.ts │ │ ├── useUserByIdSchedule.ts │ │ ├── useVenue.ts │ │ └── useVenues.ts ├── package.json ├── tsconfig.json └── utils │ ├── color.ts │ ├── config.ts │ ├── const.ts │ ├── date.ts │ ├── form.ts │ ├── icons.ts │ ├── index.ts │ ├── isBrowser.ts │ ├── schema.ts │ ├── session.ts │ ├── sharing │ ├── email.ts │ ├── facebook.ts │ ├── linkedin.ts │ ├── objectToGetParams.ts │ └── twitter.ts │ ├── string.ts │ └── user.ts ├── tsconfig.json ├── web ├── .env.local.example ├── .gitignore ├── README.md ├── babel.config.js ├── components │ ├── attendees │ │ ├── AdminCreateAttendeeForm.tsx │ │ ├── AdminEditAttendeeForm.tsx │ │ ├── AttendeeList.tsx │ │ ├── CreateAttendeeDialog.tsx │ │ ├── CreateAttendeeForm.tsx │ │ ├── DeleteAttendeeDialog.tsx │ │ ├── EventRegistrationDialog.tsx │ │ └── ViewAttendee.tsx │ ├── authentication │ │ ├── ClaimProfileForm.tsx │ │ ├── EditUserForm.tsx │ │ ├── PasswordResetForm.tsx │ │ ├── ProfileDropdown.tsx │ │ ├── RequestPasswordResetForm.tsx │ │ ├── SignInForm.tsx │ │ ├── SignUpForm.tsx │ │ ├── UserNotificationPreferencesForm.tsx │ │ └── UserSettingsForm.tsx │ ├── billing │ │ ├── EventalPro.tsx │ │ ├── EventalProCard.tsx │ │ ├── FaqAccordion.tsx │ │ ├── FeatureList.tsx │ │ ├── PromotionalOffer.tsx │ │ └── PurchaseProForm.tsx │ ├── categories │ │ ├── CreateCategoryDialog.tsx │ │ ├── CreateSessionCategoryForm.tsx │ │ ├── DeleteSessionCategoryDialog.tsx │ │ ├── EditSessionCategoryForm.tsx │ │ ├── SessionCategoryList.tsx │ │ └── ViewSessionCategory.tsx │ ├── contact │ │ ├── SubmitDemoRequestForm.tsx │ │ └── SubmitSupportTicketForm.tsx │ ├── error │ │ ├── AlreadySignedInPage.tsx │ │ ├── Loading.tsx │ │ ├── LoadingInner.tsx │ │ ├── LoadingPage.tsx │ │ ├── LoadingSpinner.tsx │ │ ├── NoAccess.tsx │ │ ├── NoAccessPage.tsx │ │ ├── NotFound.tsx │ │ ├── NotFoundPage.tsx │ │ ├── PrivatePage.tsx │ │ ├── Unauthorized.tsx │ │ ├── UnauthorizedPage.tsx │ │ └── ViewErrorPage.tsx │ ├── events │ │ ├── CreateEventForm.tsx │ │ ├── DeleteEventDialog.tsx │ │ ├── EditEventForm.tsx │ │ ├── EventHeader.tsx │ │ ├── EventList.tsx │ │ ├── EventListItem.tsx │ │ ├── EventsPageNavigation.tsx │ │ ├── LeaveEventDialog.tsx │ │ ├── Navigation.tsx │ │ ├── SessionDatePicker.tsx │ │ ├── SessionDatePickerButton.tsx │ │ └── ShareEventDropdown.tsx │ ├── form │ │ ├── AvatarUpload.tsx │ │ ├── DatePicker.tsx │ │ ├── DatePickerButton.tsx │ │ ├── Editor.tsx │ │ ├── ErrorMessage.tsx │ │ ├── ImageUpload.tsx │ │ ├── ImageUploadDialog.tsx │ │ └── TimeZoneNotice.tsx │ ├── guides │ │ ├── AspectImage.tsx │ │ ├── CopyToClipboard.tsx │ │ ├── GuideCategoryCard.tsx │ │ ├── GuideCategoryCardWrapper.tsx │ │ ├── GuideSection.tsx │ │ ├── GuideSectionHeader.tsx │ │ ├── StillNeedHelp.tsx │ │ ├── TableOfContents.tsx │ │ └── UnorderedIconLinkList.tsx │ ├── layout │ │ ├── AdminPageWrapper.tsx │ │ ├── AdminSidebar.tsx │ │ ├── AdminSidebarWrapper.tsx │ │ ├── Card.tsx │ │ ├── CardGrid.tsx │ │ ├── Column.tsx │ │ ├── FlexRowBetween.tsx │ │ ├── Footer.tsx │ │ ├── PageWrapper.tsx │ │ ├── SettingsPageWrapper.tsx │ │ ├── SettingsSidebar.tsx │ │ ├── SettingsSidebarWrapper.tsx │ │ └── SidebarLink.tsx │ ├── messages │ │ ├── DeleteMessageDialog.tsx │ │ ├── EditMessageForm.tsx │ │ ├── MessageList.tsx │ │ ├── SendMessageForm.tsx │ │ └── ViewMessage.tsx │ ├── navigation │ │ ├── AuthContainer.tsx │ │ ├── FullscreenLinkItem.tsx │ │ ├── HamburgerContainer.tsx │ │ ├── LinkContainer.tsx │ │ ├── LinkItem.tsx │ │ ├── LogoLinkItem.tsx │ │ ├── NavigationWrapper.tsx │ │ └── index.tsx │ ├── organizer │ │ └── InviteOrganizerDialog.tsx │ ├── pages │ │ ├── CreatePageForm.tsx │ │ ├── DeletePageDialog.tsx │ │ ├── EditPageForm.tsx │ │ ├── PageList.tsx │ │ └── ViewPage.tsx │ ├── primitives │ │ ├── Accordion.tsx │ │ ├── AddToCalendar.tsx │ │ ├── AddToCalendarDropdown.tsx │ │ ├── BlankLink.tsx │ │ ├── Button.tsx │ │ ├── DialogContent.tsx │ │ ├── Heading.tsx │ │ ├── HelpTooltip.tsx │ │ ├── IconButtonTooltip.tsx │ │ ├── IconLinkTooltip.tsx │ │ ├── Input.tsx │ │ ├── Label.tsx │ │ ├── LinkButton.tsx │ │ ├── LinkDialog.tsx │ │ ├── Paragraph.tsx │ │ ├── Select.tsx │ │ ├── Slider.tsx │ │ ├── Switch.tsx │ │ ├── Textarea.tsx │ │ ├── Tooltip.tsx │ │ └── TooltipIcon.tsx │ ├── roles │ │ ├── CreateRoleDialog.tsx │ │ ├── CreateRoleForm.tsx │ │ ├── DeleteRoleDialog.tsx │ │ ├── EditRoleForm.tsx │ │ ├── InviteRoleMemberDialog.tsx │ │ ├── RoleList.tsx │ │ └── ViewRole.tsx │ ├── sessions │ │ ├── AttachPeopleDialog.tsx │ │ ├── CreateSessionAttendeeForm.tsx │ │ ├── CreateSessionForm.tsx │ │ ├── DeleteSessionDialog.tsx │ │ ├── EditSessionForm.tsx │ │ ├── LeaveSessionDialog.tsx │ │ ├── RoleMemberListItem.tsx │ │ ├── SessionHoverCard.tsx │ │ ├── SessionList.tsx │ │ ├── SessionListDateItem.tsx │ │ ├── SessionListHourItem.tsx │ │ ├── SessionWithEventList.tsx │ │ ├── ShareSessionDropdown.tsx │ │ └── ViewSession.tsx │ └── venues │ │ ├── CreateVenueDialog.tsx │ │ ├── CreateVenueForm.tsx │ │ ├── DeleteVenueDialog.tsx │ │ ├── EditVenueForm.tsx │ │ ├── VenueList.tsx │ │ └── ViewVenue.tsx ├── email │ ├── generateTemplates.ts │ ├── output │ │ ├── ClaimProfile │ │ │ └── template.json │ │ ├── EventMessage │ │ │ └── template.json │ │ ├── OrganizerInvite │ │ │ └── template.json │ │ ├── ResetPassword │ │ │ └── template.json │ │ ├── RoleInvite │ │ │ └── template.json │ │ ├── Sales │ │ │ └── template.json │ │ ├── VerifyEmail │ │ │ └── template.json │ │ └── Welcome │ │ │ └── template.json │ ├── sendSales.ts │ └── templates │ │ ├── claimProfile.ts │ │ ├── demoRequest.ts │ │ ├── eventMessage.ts │ │ ├── inviteOrganizer.ts │ │ ├── inviteRole.ts │ │ ├── resetPassword.ts │ │ ├── sales.ts │ │ ├── supportTicket.ts │ │ ├── verifyEmail.ts │ │ └── welcome.ts ├── hooks │ └── mutations │ │ ├── useAddAttendeeToSession.ts │ │ ├── useAdminCreateAttendee.ts │ │ ├── useAdminDeleteAttendee.ts │ │ ├── useClaimProfile.ts │ │ ├── useCreateEvent.ts │ │ ├── useCreatePage.ts │ │ ├── useCreateRole.ts │ │ ├── useCreateSession.ts │ │ ├── useCreateSessionAttendee.ts │ │ ├── useCreateSessionCategory.ts │ │ ├── useCreateVenue.ts │ │ ├── useDeleteEvent.ts │ │ ├── useDeleteMessage.ts │ │ ├── useDeletePage.ts │ │ ├── useDeleteRole.ts │ │ ├── useDeleteSession.ts │ │ ├── useDeleteSessionAttendee.ts │ │ ├── useDeleteSessionCategory.ts │ │ ├── useDeleteVenue.ts │ │ ├── useEditAttendee.ts │ │ ├── useEditEvent.ts │ │ ├── useEditMessage.ts │ │ ├── useEditPage.ts │ │ ├── useEditRole.ts │ │ ├── useEditSession.ts │ │ ├── useEditSessionCategory.ts │ │ ├── useEditUser.ts │ │ ├── useEditVenue.ts │ │ ├── useImageUpload.ts │ │ ├── useInviteOrganizer.ts │ │ ├── useInviteRole.ts │ │ ├── useSendEvent.ts │ │ ├── useSubmitDemoRequest.ts │ │ ├── useSubmitSupportTicket.ts │ │ └── useUpgradeEvent.ts ├── next-env.d.ts ├── next-sitemap.config.js ├── next.config.js ├── package.json ├── pages │ ├── 404.tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── api │ │ ├── auth │ │ │ ├── claim │ │ │ │ └── index.ts │ │ │ ├── password │ │ │ │ ├── request.ts │ │ │ │ └── reset.ts │ │ │ ├── signin.ts │ │ │ ├── signout.ts │ │ │ ├── signup.ts │ │ │ └── verify │ │ │ │ ├── index.ts │ │ │ │ └── request.ts │ │ ├── demo │ │ │ └── index.ts │ │ ├── events │ │ │ ├── [eid] │ │ │ │ ├── admin │ │ │ │ │ ├── attendees │ │ │ │ │ │ ├── [uid] │ │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── create.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── messages │ │ │ │ │ │ ├── [mid].ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── organizers │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── invite.ts │ │ │ │ │ ├── pages │ │ │ │ │ │ ├── [pid] │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── create.ts │ │ │ │ │ ├── roles │ │ │ │ │ │ ├── [rid] │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── invite.ts │ │ │ │ │ │ └── create.ts │ │ │ │ │ ├── sessions │ │ │ │ │ │ ├── [sid] │ │ │ │ │ │ │ ├── attendees │ │ │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ │ │ └── remove.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── categories │ │ │ │ │ │ │ ├── [cid] │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ └── create.ts │ │ │ │ │ │ └── create.ts │ │ │ │ │ └── venues │ │ │ │ │ │ ├── [vid] │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── create.ts │ │ │ │ ├── attendee.ts │ │ │ │ ├── attendees │ │ │ │ │ ├── [uid].ts │ │ │ │ │ └── index.ts │ │ │ │ ├── founder.ts │ │ │ │ ├── index.ts │ │ │ │ ├── invites │ │ │ │ │ ├── organizer.ts │ │ │ │ │ └── roles │ │ │ │ │ │ └── [rid].ts │ │ │ │ ├── leave.ts │ │ │ │ ├── messages │ │ │ │ │ ├── [mid].ts │ │ │ │ │ └── index.ts │ │ │ │ ├── organizer.ts │ │ │ │ ├── pages │ │ │ │ │ ├── [pid].ts │ │ │ │ │ └── index.ts │ │ │ │ ├── register.ts │ │ │ │ ├── roles │ │ │ │ │ ├── [rid].ts │ │ │ │ │ └── index.ts │ │ │ │ ├── sessions │ │ │ │ │ ├── [sid] │ │ │ │ │ │ ├── attendee.ts │ │ │ │ │ │ ├── attendees │ │ │ │ │ │ │ ├── [uid].ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── leave.ts │ │ │ │ │ │ └── register.ts │ │ │ │ │ ├── categories │ │ │ │ │ │ ├── [cid].ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── venues │ │ │ │ │ ├── [vid].ts │ │ │ │ │ └── index.ts │ │ │ ├── attending.ts │ │ │ ├── create.ts │ │ │ ├── index.ts │ │ │ └── organizing.ts │ │ ├── payment │ │ │ ├── intents │ │ │ │ └── index.ts │ │ │ ├── sessions │ │ │ │ ├── [id].ts │ │ │ │ └── index.ts │ │ │ └── webhooks │ │ │ │ └── index.ts │ │ ├── support │ │ │ └── index.ts │ │ ├── upload │ │ │ ├── avatar.ts │ │ │ └── image.ts │ │ └── user │ │ │ ├── [uid] │ │ │ ├── index.ts │ │ │ ├── schedule │ │ │ │ └── generate.ts │ │ │ └── sessions.ts │ │ │ ├── index.ts │ │ │ └── notifications.ts │ ├── auth │ │ ├── claim │ │ │ └── index.tsx │ │ ├── password │ │ │ ├── request.tsx │ │ │ └── reset.tsx │ │ ├── signin.tsx │ │ ├── signup.tsx │ │ └── verify │ │ │ └── index.tsx │ ├── conference.tsx │ ├── contact.tsx │ ├── convention.tsx │ ├── demo.tsx │ ├── education.tsx │ ├── events │ │ ├── [eid] │ │ │ ├── admin │ │ │ │ ├── attendees │ │ │ │ │ ├── [uid] │ │ │ │ │ │ ├── edit.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── billing │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── messages │ │ │ │ │ ├── [mid] │ │ │ │ │ │ ├── edit.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── organizers │ │ │ │ │ └── index.tsx │ │ │ │ ├── pages │ │ │ │ │ ├── [pid] │ │ │ │ │ │ ├── edit.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── roles │ │ │ │ │ ├── [rid] │ │ │ │ │ │ ├── edit.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── sessions │ │ │ │ │ ├── [sid] │ │ │ │ │ │ ├── edit.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── categories │ │ │ │ │ │ ├── [cid] │ │ │ │ │ │ │ ├── edit.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── create.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── venues │ │ │ │ │ ├── [vid] │ │ │ │ │ ├── edit.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ ├── attendees │ │ │ │ ├── [uid].tsx │ │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ ├── invites │ │ │ │ ├── organizer.tsx │ │ │ │ └── roles │ │ │ │ │ └── [rid].tsx │ │ │ ├── messages │ │ │ │ ├── [mid].tsx │ │ │ │ └── index.tsx │ │ │ ├── pages │ │ │ │ ├── [pid].tsx │ │ │ │ └── index.tsx │ │ │ ├── register.tsx │ │ │ ├── roles │ │ │ │ ├── [rid].tsx │ │ │ │ └── index.tsx │ │ │ ├── sessions │ │ │ │ ├── [sid] │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── register.tsx │ │ │ │ ├── categories │ │ │ │ │ ├── [cid].tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── dates │ │ │ │ │ └── [date].tsx │ │ │ │ └── index.tsx │ │ │ └── venues │ │ │ │ ├── [vid].tsx │ │ │ │ └── index.tsx │ │ ├── attending.tsx │ │ ├── create.tsx │ │ ├── index.tsx │ │ └── organizing.tsx │ ├── festival.tsx │ ├── guides │ │ ├── attendee │ │ │ ├── attending-a-session.tsx │ │ │ ├── attending-an-event.tsx │ │ │ └── downloading-your-schedule.tsx │ │ ├── index.tsx │ │ ├── organizer │ │ │ ├── granting-the-organizer-role.tsx │ │ │ └── inviting-organizers.tsx │ │ ├── page │ │ │ ├── creating-a-page.tsx │ │ │ └── editing-a-page.tsx │ │ ├── role │ │ │ ├── creating-a-role-member.tsx │ │ │ ├── creating-a-role.tsx │ │ │ ├── editing-a-role.tsx │ │ │ └── inviting-a-role-member.tsx │ │ ├── session │ │ │ ├── attaching-a-role-member.tsx │ │ │ ├── creating-a-session-category.tsx │ │ │ ├── creating-a-session.tsx │ │ │ ├── editing-a-session-category.tsx │ │ │ └── editing-a-session.tsx │ │ ├── user │ │ │ └── creating-an-account.tsx │ │ └── venue │ │ │ ├── creating-a-venue.tsx │ │ │ └── editing-a-venue.tsx │ ├── hybrid.tsx │ ├── index.tsx │ ├── pricing.tsx │ ├── settings │ │ ├── index.tsx │ │ └── notifications.tsx │ ├── support.tsx │ ├── users │ │ └── [uid] │ │ │ ├── index.tsx │ │ │ └── schedule.tsx │ └── virtual.tsx ├── postcss.config.js ├── public │ ├── browserconfig.xml │ ├── images │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── app-icon.svg │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── favicon.svg │ │ ├── logo.svg │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg │ ├── manifest.json │ ├── robots.txt │ ├── sitemap-0.xml │ └── sitemap.xml ├── styles │ └── global.css ├── tailwind.config.js ├── tsconfig.json ├── typings │ └── next.d.ts └── utils │ ├── analytics.ts │ ├── api.ts │ ├── attendee.ts │ ├── client.ts │ ├── email.ts │ ├── file.ts │ ├── form.ts │ ├── image.ts │ ├── price.ts │ └── stripe.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules/ 3 | .pnp/ 4 | .pnp.js 5 | 6 | # spam 7 | .DS_Store 8 | *.pem 9 | *.tsbuildinfo 10 | .eslintcache 11 | 12 | # output 13 | *.log 14 | 15 | # local env 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .env.direct 21 | .env 22 | 23 | # npm 24 | package-lock.json 25 | 26 | #.idea 27 | dataSources.xml 28 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/evental.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.13 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | .idea 4 | .vscode 5 | .expo 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require.resolve('@trivago/prettier-plugin-sort-imports')], 3 | useTabs: true, 4 | printWidth: 100, 5 | singleQuote: true, 6 | trailingComma: 'none', 7 | semi: true, 8 | quoteProps: 'consistent', 9 | endOfLine: 'lf', 10 | bracketSpacing: true, 11 | arrowParens: 'always', 12 | importOrder: ['@eventalapp', '^[./]'], 13 | importOrderSeparation: true, 14 | importOrderSortSpecifiers: true 15 | }; 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "javascript.preferences.importModuleSpecifier": "non-relative" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Evental 2 | 3 | Evental is an open source event management platform. 4 | 5 | ### Technologies: 6 | 7 | Next.js, TypeScript, React, React Native, Prisma, Tailwind CSS, Radix UI, react-query, react-hook-form, mjml, Yarn 8 | 9 | ### Hosting/Services: 10 | - [AWS SES](https://aws.amazon.com/ses/), [AWS CloudFront](https://aws.amazon.com/cloudfront/), [AWS S3](https://aws.amazon.com/s3/), [AWS Route53](https://aws.amazon.com/route53/), [AWS ACM](https://aws.amazon.com/certificate-manager/) 11 | - [PlanetScale MySQL](https://planetscale.com/) 12 | - [Vercel](https://vercel.com/) 13 | - [Prisma Data Platform](https://www.prisma.io/data-platform) 14 | - [Upstash Redis](https://docs.upstash.com/redis) 15 | - [Google Analytics](https://analytics.google.com/analytics/web/) 16 | - [Stripe](https://stripe.com/) 17 | 18 | 19 | # Development Setup 20 | 21 | Prerequisites: 22 | 23 | - [Node 16.13](https://nodejs.org/ko/blog/release/v16.13.0/) 24 | - [Yarn](https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable) 25 | 26 | Clone the repository: 27 | 28 | ```bash 29 | git clone https://github.com/eventalapp/evental 30 | ``` 31 | 32 | Duplicate `.env.local.example` in each application (web, mobile & shared) and rename it to `.env` 33 | 34 | Install dependencies and generate the prisma client: 35 | 36 | ```bash 37 | yarn 38 | ``` 39 | 40 | Start the web development server: 41 | 42 | ```bash 43 | yarn web dev 44 | ``` 45 | 46 | Start the mobile development server: 47 | 48 | ```bash 49 | yarn mobile dev 50 | ``` 51 | -------------------------------------------------------------------------------- /mobile/.env.local.example: -------------------------------------------------------------------------------- 1 | # The URL to your local API. Ensure you are on the same network. 2 | API_HOST="http://YOUR_COMPUTER_NAME.local:5555/api" -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | project.xcworkspace 20 | 21 | # Android/IntelliJ 22 | # 23 | build/ 24 | .idea 25 | .gradle 26 | local.properties 27 | *.iml 28 | *.hprof 29 | 30 | # BUCK 31 | buck-out/ 32 | \.buckd/ 33 | *.keystore 34 | 35 | # fastlane 36 | # 37 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 38 | # screenshots whenever they are needed. 39 | # For more information about the recommended setup visit: 40 | # https://docs.fastlane.tools/best-practices/source-control/ 41 | 42 | */fastlane/report.xml 43 | */fastlane/Preview.html 44 | */fastlane/screenshots 45 | 46 | # Bundle artifacts 47 | *.jsbundle 48 | 49 | # CocoaPods 50 | /ios/Pods/ 51 | 52 | # Expo 53 | .expo 54 | web-build/ 55 | -------------------------------------------------------------------------------- /mobile/Main.tsx: -------------------------------------------------------------------------------- 1 | import NetInfo from '@react-native-community/netinfo'; 2 | import { registerRootComponent } from 'expo'; 3 | import * as React from 'react'; 4 | import { AppState, AppStateStatus, Platform, StatusBar } from 'react-native'; 5 | import { QueryClient, QueryClientProvider, focusManager, onlineManager } from 'react-query'; 6 | 7 | import { Navigation } from './components/navigation'; 8 | 9 | function onAppStateChange(status: AppStateStatus) { 10 | if (Platform.OS !== 'web') { 11 | focusManager.setFocused(status === 'active'); 12 | } 13 | } 14 | 15 | onlineManager.setEventListener((setOnline) => { 16 | return NetInfo.addEventListener((state) => { 17 | setOnline(!!state.isConnected); 18 | }); 19 | }); 20 | 21 | export function Main() { 22 | const queryClient = new QueryClient(); 23 | 24 | React.useEffect(() => { 25 | const subscription = AppState.addEventListener('change', onAppStateChange); 26 | 27 | return () => subscription.remove(); 28 | }, []); 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | } 38 | 39 | registerRootComponent(Main); 40 | -------------------------------------------------------------------------------- /mobile/README.md: -------------------------------------------------------------------------------- 1 | ## Todo List 2 | 3 | - deep link configuration 4 | -------------------------------------------------------------------------------- /mobile/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "Evental", 4 | "slug": "evental", 5 | "scheme": "evental" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /mobile/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | plugins: ['inline-dotenv'] 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /mobile/components/navigation/EventStackNavigation.tsx: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from '@react-navigation/stack'; 2 | import * as React from 'react'; 3 | 4 | import { UpcomingEventsScreen } from '../../screens/events/UpcomingEvents'; 5 | import { ViewEventScreen } from '../../screens/events/ViewEvent'; 6 | import { ViewAttendeeScreen } from '../../screens/events/attendees/ViewAttendee'; 7 | import { ViewSessionScreen } from '../../screens/events/sessions/ViewSession'; 8 | 9 | const Stack = createStackNavigator(); 10 | 11 | export type EventsStackParamList = { 12 | Events: undefined; 13 | ViewEvent: { eid: string }; 14 | ViewAttendee: { eid: string; uid: string }; 15 | ViewSession: { eid: string; sid: string }; 16 | }; 17 | 18 | export const EventStackNavigation = () => { 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /mobile/components/navigation/SettingsStackNavigation.tsx: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from '@react-navigation/stack'; 2 | import * as React from 'react'; 3 | 4 | import { NotificationPreferencesScreen } from '../../screens/settings/NotificationPreferences'; 5 | import { SettingsScreen } from '../../screens/settings/Settings'; 6 | 7 | const Stack = createStackNavigator(); 8 | 9 | export type SettingsStackParamList = { 10 | Settings: undefined; 11 | NotificationPreferences: undefined; 12 | }; 13 | 14 | export const SettingStackNavigation = () => { 15 | return ( 16 | 17 | 18 | 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /mobile/hooks/useRefreshOnFocus.ts: -------------------------------------------------------------------------------- 1 | import { useFocusEffect } from '@react-navigation/native'; 2 | import React from 'react'; 3 | 4 | export function useRefreshOnFocus(refetch: () => Promise) { 5 | const firstTimeRef = React.useRef(true); 6 | 7 | useFocusEffect( 8 | React.useCallback(() => { 9 | if (firstTimeRef.current) { 10 | firstTimeRef.current = false; 11 | return; 12 | } 13 | 14 | void refetch(); 15 | }, [refetch]) 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /mobile/metro.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolver: { 3 | sourceExts: ['jsx', 'js', 'ts', 'tsx', 'cjs'] 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /mobile/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "moduleResolution": "node" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "evental-app", 3 | "private": true, 4 | "workspaces": [ 5 | "mobile", 6 | "web", 7 | "shared" 8 | ], 9 | "scripts": { 10 | "prepare": "yarn shared generate", 11 | "web": "dotenv -e ./shared/.env -- yarn workspace @eventalapp/web", 12 | "mobile": "yarn workspace @eventalapp/mobile", 13 | "shared": "yarn workspace @eventalapp/shared", 14 | "format": "prettier --write \"./**/*.{js,jsx,md,ts,tsx,json,yml}\" && yarn shared format && yarn shared barrels", 15 | "tsc": "yarn web tsc && yarn shared tsc && yarn mobile tsc", 16 | "clean": "yarn global add rimraf && rimraf \"./**/**/{node_modules,.next,.expo}\"" 17 | }, 18 | "devDependencies": { 19 | "@trivago/prettier-plugin-sort-imports": "^3.2.0", 20 | "dotenv-cli": "^6.0.0", 21 | "prettier": "^2.7.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shared/.env.direct.local.example: -------------------------------------------------------------------------------- 1 | # Since Prisma Data Platform doesn't support db push via connection URLs starting with protocol prisma://. 2 | 3 | # Direct DB Conection URI 4 | DATABASE_URL='mysql://...' 5 | 6 | -------------------------------------------------------------------------------- /shared/.env.local.example: -------------------------------------------------------------------------------- 1 | # The connection string to the prisma data platform (https://cloud.prisma.io) 2 | 3 | # Prisma Data Platform URI 4 | DATABASE_URL='prisma://...' 5 | -------------------------------------------------------------------------------- /shared/api/index.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | let baseURL; 4 | 5 | if (process.env.API_HOST) { 6 | baseURL = process.env.API_HOST; 7 | } else if (process.env.NEXT_PUBLIC_VERCEL_URL) { 8 | baseURL = `https://${process.env.NEXT_PUBLIC_VERCEL_URL}/api`; 9 | } else { 10 | baseURL = 'http://localhost:5555/api'; 11 | } 12 | 13 | console.log({ baseURL }); 14 | 15 | export const api = axios.create({ 16 | baseURL 17 | }); 18 | -------------------------------------------------------------------------------- /shared/barrel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "directory": ["./utils", "./hooks", "./db"] 3 | } 4 | -------------------------------------------------------------------------------- /shared/db/client.ts: -------------------------------------------------------------------------------- 1 | // noinspection ES6ConvertVarToLetConst 2 | import { PrismaClient } from '@prisma/client/edge'; 3 | 4 | declare global { 5 | // allow global `var` declarations 6 | 7 | // eslint-disable-next-line no-var 8 | var prisma: PrismaClient | undefined; 9 | } 10 | 11 | export const prisma = global.prisma ?? new PrismaClient(); 12 | 13 | if (process.env.NODE_ENV !== 'production') global.prisma = prisma; 14 | -------------------------------------------------------------------------------- /shared/db/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Automatically generated by barrelsby. 3 | */ 4 | 5 | export * from './client'; 6 | export * from './prisma/fake'; 7 | export * from './prisma/seed'; 8 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useAcceptOrganizerInvite.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AcceptOrganizerInvitePayload } from '../../utils'; 7 | 8 | interface UseAcceptOrganizerInviteArgs { 9 | eid?: string; 10 | onError?: ( 11 | error: ErroredAPIResponse | undefined, 12 | variables: AcceptOrganizerInvitePayload, 13 | context: unknown 14 | ) => void; 15 | onSuccess?: (data: void, variables: AcceptOrganizerInvitePayload, context: unknown) => void; 16 | } 17 | 18 | export const useAcceptOrganizerInvite = (args: UseAcceptOrganizerInviteArgs = {}) => { 19 | const { onError, onSuccess, eid } = args; 20 | 21 | return useMutation( 22 | async (data) => { 23 | return await api 24 | .post>(`/events/${eid}/invites/organizer`, data) 25 | .then((res) => res.data.data) 26 | .catch((err: AxiosError) => { 27 | throw err.response?.data; 28 | }); 29 | }, 30 | { 31 | onSuccess, 32 | onError 33 | } 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useAcceptRoleInvite.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AcceptRoleInvitePayload } from '../../utils'; 7 | 8 | interface UseAcceptRoleInviteArgs { 9 | eid?: string; 10 | rid?: string; 11 | onError?: ( 12 | error: ErroredAPIResponse | undefined, 13 | variables: AcceptRoleInvitePayload, 14 | context: unknown 15 | ) => void; 16 | onSuccess?: (data: void, variables: AcceptRoleInvitePayload, context: unknown) => void; 17 | } 18 | 19 | export const useAcceptRoleInvite = (args: UseAcceptRoleInviteArgs = {}) => { 20 | const { eid, rid, onError, onSuccess } = args; 21 | 22 | return useMutation( 23 | async (data) => { 24 | return await api 25 | .post>(`/events/${eid}/invites/roles/${rid}`, data) 26 | .then((res) => res.data.data) 27 | .catch((err: AxiosError) => { 28 | throw err.response?.data; 29 | }); 30 | }, 31 | { 32 | onSuccess, 33 | onError 34 | } 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useEditNotificationPreferences.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useMutation, useQueryClient } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | import { NotificationPreferencePayload } from '../../utils'; 8 | 9 | interface UseUserSettingsArgs { 10 | onError?: ( 11 | error: ErroredAPIResponse | undefined, 12 | variables: NotificationPreferencePayload, 13 | context: unknown 14 | ) => void; 15 | onSuccess?: ( 16 | data: Prisma.NotificationPreference, 17 | variables: NotificationPreferencePayload, 18 | context: unknown 19 | ) => void; 20 | } 21 | 22 | export const useEditNotificationPreferences = (args: UseUserSettingsArgs = {}) => { 23 | const { onError, onSuccess } = args; 24 | 25 | const queryClient = useQueryClient(); 26 | 27 | return useMutation< 28 | Prisma.NotificationPreference, 29 | ErroredAPIResponse, 30 | NotificationPreferencePayload 31 | >( 32 | async (data) => { 33 | return await api 34 | .put>(`/user/notifications`, data) 35 | .then((res) => res.data.data) 36 | .catch((err: AxiosError) => { 37 | throw err.response?.data; 38 | }); 39 | }, 40 | { 41 | onSuccess: (...rest) => { 42 | void queryClient.refetchQueries(['notification-preference']); 43 | 44 | onSuccess?.(...rest); 45 | }, 46 | onError 47 | } 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useEditUserSettings.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useMutation, useQueryClient } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | import { StrippedUser, UserSettingsPayload, populateFormData } from '../../utils'; 8 | 9 | interface UseUserSettingsArgs { 10 | onError?: ( 11 | error: ErroredAPIResponse | undefined, 12 | variables: UserSettingsPayload, 13 | context: unknown 14 | ) => void; 15 | onSuccess?: (data: StrippedUser, variables: UserSettingsPayload, context: unknown) => void; 16 | } 17 | 18 | export const useEditUserSettings = (args: UseUserSettingsArgs = {}) => { 19 | const { onError, onSuccess } = args; 20 | 21 | const queryClient = useQueryClient(); 22 | 23 | return useMutation( 24 | async (data) => { 25 | const formData = populateFormData(data); 26 | 27 | return await api 28 | .put>(`/user/`, formData) 29 | .then((res) => res.data.data) 30 | .catch((err: AxiosError) => { 31 | throw err.response?.data; 32 | }); 33 | }, 34 | { 35 | onSuccess: (data, ...rest) => { 36 | void queryClient.refetchQueries(['user', data.slug]); 37 | void queryClient.refetchQueries(['user']); 38 | 39 | onSuccess?.(data, ...rest); 40 | }, 41 | onError 42 | } 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useEventRegister.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useMutation, useQueryClient } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | interface UseEventRegisterArgs { 9 | eid?: string; 10 | onError?: (error: ErroredAPIResponse | undefined, variables: void, context: unknown) => void; 11 | onSuccess?: (data: Prisma.EventAttendee, variables: void, context: unknown) => void; 12 | } 13 | 14 | export const useEventRegister = (args: UseEventRegisterArgs = {}) => { 15 | const { eid, onError, onSuccess } = args; 16 | 17 | const queryClient = useQueryClient(); 18 | 19 | return useMutation( 20 | async (data) => { 21 | return await api 22 | .post>(`/events/${eid}/register`, data) 23 | .then((res) => res.data.data) 24 | .catch((err: AxiosError) => { 25 | throw err.response?.data; 26 | }); 27 | }, 28 | { 29 | onSuccess: (...rest) => { 30 | void queryClient.invalidateQueries(['attendees', eid]); 31 | 32 | onSuccess?.(...rest); 33 | }, 34 | onError 35 | } 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useLeaveEvent.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation, useQueryClient } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { StrippedUser } from '../../utils'; 7 | 8 | interface UseLeaveEventArgs { 9 | eid?: string; 10 | onError?: (error: ErroredAPIResponse | undefined, variables: void, context: unknown) => void; 11 | onSuccess?: (data: StrippedUser, variables: void, context: unknown) => void; 12 | } 13 | 14 | export const useLeaveEvent = (args: UseLeaveEventArgs = {}) => { 15 | const { eid, onSuccess, onError } = args; 16 | 17 | const queryClient = useQueryClient(); 18 | 19 | return useMutation( 20 | async () => { 21 | return await api 22 | .delete>(`/events/${eid}/leave`) 23 | .then((res) => res.data.data) 24 | .catch((err: AxiosError) => { 25 | throw err.response?.data; 26 | }); 27 | }, 28 | { 29 | onSuccess: (data, ...rest) => { 30 | void queryClient.invalidateQueries(['attendee', eid, data.slug]); 31 | void queryClient.invalidateQueries(['attendees', eid]); 32 | 33 | onSuccess?.(data, ...rest); 34 | }, 35 | onError 36 | } 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useRequestPasswordReset.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { ChangePasswordRequestPayload } from '../../utils'; 7 | 8 | interface UseRequestPasswordResetArgs { 9 | onError?: ( 10 | error: ErroredAPIResponse | undefined, 11 | variables: ChangePasswordRequestPayload, 12 | context: unknown 13 | ) => void; 14 | onSuccess?: (data: void, variables: ChangePasswordRequestPayload, context: unknown) => void; 15 | } 16 | 17 | export const useRequestPasswordReset = (args: UseRequestPasswordResetArgs = {}) => { 18 | const { onError, onSuccess } = args; 19 | 20 | return useMutation( 21 | async (data) => { 22 | return await api 23 | .post>(`/auth/password/request`, data) 24 | .then((res) => res.data.data) 25 | .catch((err: AxiosError) => { 26 | throw err.response?.data; 27 | }); 28 | }, 29 | { 30 | onSuccess, 31 | onError 32 | } 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useRequestVerification.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | 7 | interface UseRequestVerificationArgs { 8 | onError?: (error: ErroredAPIResponse | undefined, variables: void, context: unknown) => void; 9 | onSuccess?: (data: void, variables: void, context: unknown) => void; 10 | } 11 | 12 | export const useRequestVerification = (args: UseRequestVerificationArgs = {}) => { 13 | const { onError, onSuccess } = args; 14 | 15 | return useMutation( 16 | async (data) => { 17 | return await api 18 | .post>(`/auth/verify/request`, data) 19 | .then((res) => res.data.data) 20 | .catch((err: AxiosError) => { 21 | throw err.response?.data; 22 | }); 23 | }, 24 | { 25 | onSuccess, 26 | onError 27 | } 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useResetPassword.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation, useQueryClient } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { ChangePasswordPayload } from '../../utils'; 7 | 8 | interface UseResetPasswordArgs { 9 | onError?: ( 10 | error: ErroredAPIResponse | undefined, 11 | variables: ChangePasswordPayload, 12 | context: unknown 13 | ) => void; 14 | onSuccess?: (data: void, variables: ChangePasswordPayload, context: unknown) => void; 15 | } 16 | 17 | export const useResetPassword = (args: UseResetPasswordArgs = {}) => { 18 | const { onError, onSuccess } = args; 19 | 20 | const queryClient = useQueryClient(); 21 | 22 | return useMutation( 23 | async (data) => { 24 | return await api 25 | .post>(`/auth/password/reset`, data) 26 | .then((res) => res.data.data) 27 | .catch((err: AxiosError) => { 28 | throw err.response?.data; 29 | }); 30 | }, 31 | { 32 | onSuccess: (...rest) => { 33 | void queryClient.invalidateQueries('user'); 34 | 35 | onSuccess?.(...rest); 36 | }, 37 | onError 38 | } 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useSignIn.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation, useQueryClient } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SignInPayload, StrippedUser } from '../../utils'; 7 | 8 | interface UseSignInArgs { 9 | onError?: ( 10 | error: ErroredAPIResponse | undefined, 11 | variables: SignInPayload, 12 | context: unknown 13 | ) => void; 14 | onSuccess?: (data: StrippedUser, variables: SignInPayload, context: unknown) => void; 15 | } 16 | 17 | export const useSignIn = (args: UseSignInArgs = {}) => { 18 | const { onError, onSuccess } = args; 19 | 20 | const queryClient = useQueryClient(); 21 | 22 | return useMutation( 23 | async (data) => { 24 | return await api 25 | .post>(`/auth/signin`, data) 26 | .then((res) => res.data.data) 27 | .catch((err: AxiosError) => { 28 | throw err.response?.data; 29 | }); 30 | }, 31 | { 32 | onSuccess: (...rest) => { 33 | void queryClient.refetchQueries('user'); 34 | 35 | onSuccess?.(...rest); 36 | }, 37 | onError 38 | } 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useSignOut.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation, useQueryClient } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | 7 | interface UseSignOutArgs { 8 | onError?: (error: ErroredAPIResponse | undefined, variables: void, context: unknown) => void; 9 | onSuccess?: (data: void, variables: void, context: unknown) => void; 10 | } 11 | 12 | export const useSignOut = (args: UseSignOutArgs = {}) => { 13 | const { onError, onSuccess } = args; 14 | 15 | const queryClient = useQueryClient(); 16 | 17 | return useMutation( 18 | async () => { 19 | return await api 20 | .delete>(`/auth/signout`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | onSuccess: (...rest) => { 28 | void queryClient.refetchQueries('user'); 29 | 30 | onSuccess?.(...rest); 31 | }, 32 | onError 33 | } 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useSignUp.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation, useQueryClient } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SignUpPayload, StrippedUser } from '../../utils'; 7 | 8 | interface UseSignUpArgs { 9 | onError?: ( 10 | error: ErroredAPIResponse | undefined, 11 | variables: SignUpPayload, 12 | context: unknown 13 | ) => void; 14 | onSuccess?: (data: StrippedUser, variables: SignUpPayload, context: unknown) => void; 15 | } 16 | 17 | export const useSignUp = (args: UseSignUpArgs = {}) => { 18 | const { onError, onSuccess } = args; 19 | const queryClient = useQueryClient(); 20 | 21 | return useMutation( 22 | async (data) => { 23 | return await api 24 | .post>(`/auth/signup`, data) 25 | .then((res) => res.data.data) 26 | .catch((err: AxiosError) => { 27 | throw err.response?.data; 28 | }); 29 | }, 30 | { 31 | onSuccess: (...rest) => { 32 | void queryClient.refetchQueries('user'); 33 | 34 | onSuccess?.(...rest); 35 | }, 36 | onError 37 | } 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /shared/hooks/mutations/useVerifyEmail.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useMutation, useQueryClient } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { VerifyEmailPayload } from '../../utils'; 7 | 8 | interface UseVerifyEmailArgs { 9 | onError?: ( 10 | error: ErroredAPIResponse | undefined, 11 | variables: VerifyEmailPayload, 12 | context: unknown 13 | ) => void; 14 | onSuccess?: (data: void, variables: VerifyEmailPayload, context: unknown) => void; 15 | } 16 | 17 | export const useVerifyEmail = (args: UseVerifyEmailArgs = {}) => { 18 | const { onError, onSuccess } = args; 19 | 20 | const queryClient = useQueryClient(); 21 | 22 | return useMutation( 23 | async (data) => { 24 | return await api 25 | .post>(`/auth/verify`, data) 26 | .then((res) => res.data.data) 27 | .catch((err: AxiosError) => { 28 | throw err.response?.data; 29 | }); 30 | }, 31 | { 32 | onSuccess: (...rest) => { 33 | void queryClient.invalidateQueries('user'); 34 | 35 | onSuccess?.(...rest); 36 | }, 37 | onError 38 | } 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /shared/hooks/queries/useAttendee.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseAttendeeArgs { 9 | eid?: string; 10 | uid?: string; 11 | } 12 | 13 | export const useAttendee = (args: UseAttendeeArgs = {}) => { 14 | const { eid, uid } = args; 15 | 16 | return useQuery( 17 | ['attendee', eid, uid], 18 | async () => { 19 | return api 20 | .get>(`/events/${eid}/attendees/${uid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(uid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/useAttendees.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseAttendeesArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useAttendees = (args: UseAttendeesArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['attendees', eid], 17 | async () => { 18 | return api 19 | .get>(`/events/${eid}/attendees`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useAttendeesByName.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseAttendeesByNameArgs { 9 | limit?: number; 10 | eid?: string; 11 | name?: string; 12 | } 13 | 14 | export const useAttendeesByName = (args: UseAttendeesByNameArgs = {}) => { 15 | const { limit, eid, name } = args; 16 | let params = new URLSearchParams(); 17 | 18 | params.append('name', String(name)); 19 | params.append('limit', String(limit)); 20 | params.append('type', 'name'); 21 | 22 | return useQuery( 23 | ['attendees-by-name', eid, name], 24 | async () => { 25 | return api 26 | .get>( 27 | `/events/${eid}/attendees?${params.toString()}` 28 | ) 29 | .then((res) => res.data.data) 30 | .catch((err: AxiosError) => { 31 | throw err.response?.data; 32 | }); 33 | }, 34 | { 35 | retry: 0, 36 | enabled: Boolean(eid) && Boolean(name) 37 | } 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /shared/hooks/queries/useAttendeesByRole.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseAttendeesByRoleArgs { 9 | eid?: string; 10 | rid?: string; 11 | } 12 | 13 | export const useAttendeesByRole = (args: UseAttendeesByRoleArgs = {}) => { 14 | const { eid, rid } = args; 15 | let params = new URLSearchParams(); 16 | 17 | params.append('role', String(rid)); 18 | 19 | return useQuery( 20 | ['attendees-role', eid, rid], 21 | async () => { 22 | return api 23 | .get>( 24 | `/events/${eid}/attendees?${params.toString()}` 25 | ) 26 | .then((res) => res.data.data) 27 | .catch((err: AxiosError) => { 28 | throw err.response?.data; 29 | }); 30 | }, 31 | { 32 | retry: 0, 33 | enabled: Boolean(eid) 34 | } 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /shared/hooks/queries/useAttendingEvents.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export const useAttendingEvents = () => { 9 | return useQuery( 10 | ['attending-events'], 11 | async () => { 12 | return api 13 | .get>(`/events/attending`) 14 | .then((res) => res.data.data) 15 | .catch((err: AxiosError) => { 16 | throw err.response?.data; 17 | }); 18 | }, 19 | { 20 | retry: 0 21 | } 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /shared/hooks/queries/useEvent.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseEventArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useEvent = (args: UseEventArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['event', eid], 17 | async () => { 18 | return api 19 | .get>(`/events/${eid}`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useEventMessage.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseEventMessageArgs { 9 | eid?: string; 10 | mid?: string; 11 | } 12 | 13 | export const useEventMessage = (args: UseEventMessageArgs = {}) => { 14 | const { eid, mid } = args; 15 | 16 | return useQuery( 17 | ['message', eid, mid], 18 | async () => { 19 | return await api 20 | .get>(`/events/${eid}/messages/${mid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(mid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/useEventMessages.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseEventMessagesArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useEventMessages = (args: UseEventMessagesArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['messages', eid], 17 | async () => { 18 | return await api 19 | .get>(`/events/${eid}/messages`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useIsAttendee.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | 7 | export interface UseIsAttendeeArgs { 8 | eid?: string; 9 | } 10 | 11 | export const useIsAttendee = (args: UseIsAttendeeArgs = {}) => { 12 | const { eid } = args; 13 | 14 | return useQuery( 15 | ['isAttendee', eid], 16 | async () => { 17 | return api 18 | .get>(`/events/${eid}/attendee`) 19 | .then((res) => res.data.data) 20 | .catch((err: AxiosError) => { 21 | throw err.response?.data; 22 | }); 23 | }, 24 | { 25 | retry: 0, 26 | enabled: Boolean(eid) 27 | } 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /shared/hooks/queries/useIsFounder.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | 7 | export interface UseIsFounderArgs { 8 | eid?: string; 9 | } 10 | 11 | export const useIsFounder = (args: UseIsFounderArgs = {}) => { 12 | const { eid } = args; 13 | 14 | return useQuery( 15 | ['isFounder', eid], 16 | async () => { 17 | return api 18 | .get>(`/events/${eid}/founder`) 19 | .then((res) => res.data.data) 20 | .catch((err: AxiosError) => { 21 | throw err.response?.data; 22 | }); 23 | }, 24 | { 25 | retry: 0, 26 | enabled: Boolean(eid) 27 | } 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /shared/hooks/queries/useIsOrganizer.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | 7 | export interface UseIsOrganizerArgs { 8 | eid?: string; 9 | } 10 | 11 | export const useIsOrganizer = (args: UseIsOrganizerArgs = {}) => { 12 | const { eid } = args; 13 | 14 | return useQuery( 15 | ['isOrganizer', eid], 16 | async () => { 17 | return api 18 | .get>(`/events/${eid}/organizer`) 19 | .then((res) => res.data.data) 20 | .catch((err: AxiosError) => { 21 | throw err.response?.data; 22 | }); 23 | }, 24 | { 25 | retry: 0, 26 | enabled: Boolean(eid) 27 | } 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /shared/hooks/queries/useIsSessionAttendee.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | 7 | export interface UseIsSessionAttendeeArgs { 8 | eid?: string; 9 | sid?: string; 10 | } 11 | 12 | export const useIsSessionAttendee = (args: UseIsSessionAttendeeArgs = {}) => { 13 | const { eid, sid } = args; 14 | 15 | return useQuery( 16 | ['isSessionAttendee', eid, sid], 17 | async () => { 18 | return api 19 | .get>(`/events/${eid}/sessions/${sid}/attendee`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) && Boolean(sid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useNotificationPreferences.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export const useNotificationPreferences = () => { 9 | return useQuery( 10 | ['notification-preference'], 11 | async () => { 12 | return await api 13 | .get>(`/user/notifications`) 14 | .then((res) => res.data.data) 15 | .catch((err: AxiosError) => { 16 | throw err.response?.data; 17 | }); 18 | }, 19 | { 20 | retry: 0 21 | } 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /shared/hooks/queries/useOrganizers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseOrganizersArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useOrganizers = (args: UseOrganizersArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['organizers', eid], 17 | async () => { 18 | return api 19 | .get>(`/events/${eid}/admin/organizers`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useOrganizingEvents.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export const useOrganizingEvents = () => { 9 | return useQuery( 10 | ['organizing-events'], 11 | async () => { 12 | return api 13 | .get>(`/events/organizing`) 14 | .then((res) => res.data.data) 15 | .catch((err: AxiosError) => { 16 | throw err.response?.data; 17 | }); 18 | }, 19 | { 20 | retry: 0 21 | } 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /shared/hooks/queries/usePage.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UsePageArgs { 9 | eid?: string; 10 | pid?: string; 11 | } 12 | 13 | export const usePage = (args: UsePageArgs = {}) => { 14 | const { eid, pid } = args; 15 | 16 | return useQuery( 17 | ['page', eid, pid], 18 | async () => { 19 | return await api 20 | .get>(`/events/${eid}/pages/${pid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(pid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/usePages.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UsePagesArgs { 9 | eid?: string; 10 | } 11 | 12 | export const usePages = (args: UsePagesArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['pages', eid], 17 | async () => { 18 | return await api 19 | .get>(`/events/${eid}/pages`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useRole.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseRoleArgs { 9 | eid?: string; 10 | rid?: string; 11 | } 12 | 13 | export const useRole = (args: UseRoleArgs = {}) => { 14 | const { eid, rid } = args; 15 | 16 | return useQuery( 17 | ['role', eid, rid], 18 | async () => { 19 | return api 20 | .get>(`/events/${eid}/roles/${rid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(rid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/useRoles.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseRolesArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useRoles = (args: UseRolesArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['roles', eid], 17 | async () => { 18 | return api 19 | .get>(`/events/${eid}/roles`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSession.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionWithVenue } from '../../utils'; 7 | 8 | export interface UseSessionArgs { 9 | eid?: string; 10 | sid?: string; 11 | } 12 | 13 | export const useSession = (args: UseSessionArgs = {}) => { 14 | const { eid, sid } = args; 15 | 16 | return useQuery( 17 | ['session', eid, sid], 18 | async () => { 19 | return await api 20 | .get>(`/events/${eid}/sessions/${sid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(sid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionAttendee.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseSessionAttendeeArgs { 9 | eid?: string; 10 | sid?: string; 11 | uid?: string; 12 | } 13 | 14 | export const useSessionAttendee = (args: UseSessionAttendeeArgs = {}) => { 15 | const { eid, sid, uid } = args; 16 | 17 | return useQuery( 18 | ['attendee', eid, sid, uid], 19 | async () => { 20 | return api 21 | .get>( 22 | `/events/${eid}/sessions/${sid}/attendees/${uid}` 23 | ) 24 | .then((res) => res.data.data) 25 | .catch((err: AxiosError) => { 26 | throw err.response?.data; 27 | }); 28 | }, 29 | { 30 | retry: 0, 31 | enabled: Boolean(eid) && Boolean(sid) && Boolean(uid) 32 | } 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionAttendees.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseSessionsAttendeesArgs { 9 | eid?: string; 10 | sid?: string; 11 | } 12 | 13 | export const useSessionAttendees = (args: UseSessionsAttendeesArgs = {}) => { 14 | const { eid, sid } = args; 15 | 16 | return useQuery( 17 | ['attendees', eid, sid], 18 | async () => { 19 | return api 20 | .get>( 21 | `/events/${eid}/sessions/${sid}/attendees?type=ATTENDEE` 22 | ) 23 | .then((res) => res.data.data) 24 | .catch((err: AxiosError) => { 25 | throw err.response?.data; 26 | }); 27 | }, 28 | { 29 | retry: 0, 30 | enabled: Boolean(eid) && Boolean(sid) 31 | } 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionCategories.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionCategoryWithCount } from '../../utils'; 7 | 8 | export interface UseSessionCategoriesArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useSessionCategories = (args: UseSessionCategoriesArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['session-categories', eid], 17 | async () => { 18 | return await api 19 | .get>(`/events/${eid}/sessions/categories`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionCategory.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionCategoryWithCount } from '../../utils'; 7 | 8 | export interface UseSessionsCategoryArgs { 9 | eid?: string; 10 | cid?: string; 11 | } 12 | 13 | export const useSessionCategory = (args: UseSessionsCategoryArgs = {}) => { 14 | const { eid, cid } = args; 15 | 16 | return useQuery( 17 | ['session-category', eid, cid], 18 | async () => { 19 | return await api 20 | .get>( 21 | `/events/${eid}/sessions/categories/${cid}` 22 | ) 23 | .then((res) => res.data.data) 24 | .catch((err: AxiosError) => { 25 | throw err.response?.data; 26 | }); 27 | }, 28 | { 29 | retry: 0, 30 | enabled: Boolean(eid) && Boolean(cid) 31 | } 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionRoleAttendees.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { AttendeeWithUser } from '../../utils'; 7 | 8 | export interface UseSessionRoleAttendeesArgs { 9 | eid?: string; 10 | sid?: string; 11 | } 12 | 13 | export const useSessionRoleAttendees = (args: UseSessionRoleAttendeesArgs = {}) => { 14 | const { eid, sid } = args; 15 | 16 | return useQuery( 17 | ['role-attendees', eid, sid], 18 | async () => { 19 | return api 20 | .get>( 21 | `/events/${eid}/sessions/${sid}/attendees?type=ROLE` 22 | ) 23 | .then((res) => res.data.data) 24 | .catch((err: AxiosError) => { 25 | throw err.response?.data; 26 | }); 27 | }, 28 | { 29 | retry: 0, 30 | enabled: Boolean(eid) && Boolean(sid) 31 | } 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessions.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionWithVenue } from '../../utils'; 7 | 8 | export interface UseSessionsArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useSessions = (args: UseSessionsArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['sessions', eid], 17 | async () => { 18 | return await api 19 | .get>(`/events/${eid}/sessions`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionsByCategory.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionWithVenue } from '../../utils'; 7 | 8 | export interface UseSessionsByCategoryArgs { 9 | eid?: string; 10 | cid?: string; 11 | } 12 | 13 | export const useSessionsByCategory = (args: UseSessionsByCategoryArgs = {}) => { 14 | const { eid, cid } = args; 15 | 16 | let params = new URLSearchParams(); 17 | 18 | params.append('category', String(cid)); 19 | 20 | return useQuery( 21 | ['category-sessions', eid, cid], 22 | async () => { 23 | return await api 24 | .get>(`/events/${eid}/sessions?${params.toString()}`) 25 | .then((res) => res.data.data) 26 | .catch((err: AxiosError) => { 27 | throw err.response?.data; 28 | }); 29 | }, 30 | { 31 | retry: 0, 32 | enabled: Boolean(eid) && Boolean(cid) 33 | } 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionsByDate.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionWithVenue } from '../../utils'; 7 | 8 | export interface UseSessionsByDateArgs { 9 | eid?: string; 10 | date?: string; 11 | } 12 | 13 | export const useSessionsByDate = (args: UseSessionsByDateArgs = {}) => { 14 | const { eid, date } = args; 15 | 16 | let params = new URLSearchParams(); 17 | 18 | params.append('date', String(date)); 19 | 20 | return useQuery( 21 | ['date-sessions', eid, date], 22 | async () => { 23 | return await api 24 | .get>(`/events/${eid}/sessions?${params}`) 25 | .then((res) => res.data.data) 26 | .catch((err: AxiosError) => { 27 | throw err.response?.data; 28 | }); 29 | }, 30 | { 31 | retry: 0, 32 | enabled: Boolean(eid) && Boolean(date) 33 | } 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /shared/hooks/queries/useSessionsByVenue.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionWithVenue } from '../../utils'; 7 | 8 | export interface UseSessionsByVenueArgs { 9 | eid?: string; 10 | vid?: string; 11 | } 12 | 13 | export const useSessionsByVenue = (args: UseSessionsByVenueArgs = {}) => { 14 | const { eid, vid } = args; 15 | 16 | let params = new URLSearchParams(); 17 | 18 | params.append('venue', String(vid)); 19 | 20 | return useQuery( 21 | ['venue-sessions', eid, vid], 22 | async () => { 23 | return await api 24 | .get>(`/events/${eid}/sessions?${params}`) 25 | .then((res) => res.data.data) 26 | .catch((err: AxiosError) => { 27 | throw err.response?.data; 28 | }); 29 | }, 30 | { 31 | retry: 0, 32 | enabled: Boolean(eid) && Boolean(vid) 33 | } 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /shared/hooks/queries/useUnclaimedUser.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { FullUser } from '../../utils'; 7 | 8 | export interface UseUnclaimedUserArgs { 9 | eid?: string; 10 | uid?: string; 11 | } 12 | 13 | export const useUnclaimedUser = (args: UseUnclaimedUserArgs = {}) => { 14 | const { eid, uid } = args; 15 | 16 | return useQuery( 17 | ['full-user', uid], 18 | async () => { 19 | return await api 20 | .get>(`/events/${eid}/admin/attendees/${uid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(uid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/useUpcomingEvents.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export const useUpcomingEvents = () => { 9 | return useQuery( 10 | ['upcoming-events'], 11 | async () => { 12 | return api 13 | .get>(`/events/`) 14 | .then((res) => res.data.data) 15 | .catch((err: AxiosError) => { 16 | throw err.response?.data; 17 | }); 18 | }, 19 | { 20 | retry: 0 21 | } 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /shared/hooks/queries/useUser.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { FullUser } from '../../utils'; 7 | 8 | export const useUser = () => { 9 | return useQuery(['user'], async () => { 10 | return await api 11 | .get>(`/user`) 12 | .then((res) => res.data.data) 13 | .catch((err: AxiosError) => { 14 | throw err.response?.data; 15 | }); 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /shared/hooks/queries/useUserById.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { StrippedUser } from '../../utils'; 7 | 8 | export interface UseUserByIdArgs { 9 | uid?: string; 10 | } 11 | 12 | export const useUserById = (args: UseUserByIdArgs = {}) => { 13 | const { uid } = args; 14 | 15 | return useQuery( 16 | ['user', uid], 17 | async () => { 18 | return await api 19 | .get>(`/user/${uid}`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(uid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/hooks/queries/useUserByIdSchedule.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 3 | import { useQuery } from 'react-query'; 4 | 5 | import { api } from '../../api'; 6 | import { SessionWithVenueEvent } from '../../utils'; 7 | 8 | export interface UseUserByIdScheduleArgs { 9 | uid?: string; 10 | } 11 | 12 | export const useUserByIdSchedule = (args: UseUserByIdScheduleArgs = {}) => { 13 | const { uid } = args; 14 | 15 | let params = new URLSearchParams(); 16 | 17 | params.append('user', String(uid)); 18 | 19 | return useQuery( 20 | ['user-sessions', uid], 21 | async () => { 22 | return await api 23 | .get>(`/user/${uid}/sessions?${params}`) 24 | .then((res) => res.data.data) 25 | .catch((err: AxiosError) => { 26 | throw err.response?.data; 27 | }); 28 | }, 29 | { 30 | retry: 0, 31 | enabled: Boolean(uid) 32 | } 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /shared/hooks/queries/useVenue.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseVenueArgs { 9 | eid?: string; 10 | vid?: string; 11 | } 12 | 13 | export const useVenue = (args: UseVenueArgs = {}) => { 14 | const { eid, vid } = args; 15 | 16 | return useQuery( 17 | ['venue', eid, vid], 18 | async () => { 19 | return api 20 | .get>(`/events/${eid}/venues/${vid}`) 21 | .then((res) => res.data.data) 22 | .catch((err: AxiosError) => { 23 | throw err.response?.data; 24 | }); 25 | }, 26 | { 27 | retry: 0, 28 | enabled: Boolean(eid) && Boolean(vid) 29 | } 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /shared/hooks/queries/useVenues.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | import { AxiosError } from 'axios'; 3 | import { ErroredAPIResponse, SuccessAPIResponse } from 'nextkit'; 4 | import { useQuery } from 'react-query'; 5 | 6 | import { api } from '../../api'; 7 | 8 | export interface UseVenuesArgs { 9 | eid?: string; 10 | } 11 | 12 | export const useVenues = (args: UseVenuesArgs = {}) => { 13 | const { eid } = args; 14 | 15 | return useQuery( 16 | ['venues', eid], 17 | async () => { 18 | return api 19 | .get>(`/events/${eid}/venues`) 20 | .then((res) => res.data.data) 21 | .catch((err: AxiosError) => { 22 | throw err.response?.data; 23 | }); 24 | }, 25 | { 26 | retry: 0, 27 | enabled: Boolean(eid) 28 | } 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eventalapp/shared", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "prepare": "yarn generate", 7 | "generate": "dotenv -e ./.env -- yarn prisma generate --data-proxy --schema=\"./db/prisma/schema.prisma\"", 8 | "push": "dotenv -e ./.env.direct -- yarn prisma db push --schema=\"./db/prisma/schema.prisma\"", 9 | "seed": "ts-node --project tsconfig.json --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} prisma/seed.ts", 10 | "format": "dotenv -e ./.env -- yarn prisma format --schema=\"./db/prisma/schema.prisma\"", 11 | "validate": "dotenv -e ./.env -- yarn prisma validate --schema=\"./db/prisma/schema.prisma\"", 12 | "barrels": "barrelsby --delete --config \"./barrel.config.json\" --singleQuotes" 13 | }, 14 | "dependencies": { 15 | "@prisma/client": "^4.1.0", 16 | "axios": "^0.27.2" 17 | }, 18 | "devDependencies": { 19 | "barrelsby": "^2.3.4", 20 | "dotenv-cli": "^6.0.0", 21 | "prisma": "^4.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /shared/utils/color.ts: -------------------------------------------------------------------------------- 1 | import { theme } from '../../web/tailwind.config'; 2 | 3 | export const colors = theme.extend.colors; 4 | -------------------------------------------------------------------------------- /shared/utils/config.ts: -------------------------------------------------------------------------------- 1 | export const NEAREST_MINUTE = 15; 2 | 3 | export const PASSWORD_RESET_EXPIRY = 60 * 30; 4 | 5 | export const VERIFY_EMAIL_EXPIRY = 60 * 30; 6 | 7 | export const ORGANIZER_INVITE_EXPIRY = 60 * 60 * 24 * 7; 8 | 9 | export const CLAIM_PROFILE_EXPIRY = 60 * 60 * 24 * 7; 10 | 11 | export const SESSION_EXPIRY = 60 * 60 * 24 * 7; 12 | 13 | export const CURRENCY = 'usd'; 14 | 15 | export const MIN_AMOUNT = 10.0; 16 | 17 | export const MAX_AMOUNT = 5000.0; 18 | 19 | export const SEED_USERS_TO_GENERATE = 50; 20 | -------------------------------------------------------------------------------- /shared/utils/date.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | type FormatDateRangeArgs = { 4 | showHour?: boolean; 5 | }; 6 | 7 | export const formatDateRange = (start: Date, end: Date, args: FormatDateRangeArgs = {}) => { 8 | const { showHour = true } = args; 9 | 10 | const startDate = dayjs(start); 11 | const endDate = dayjs(end); 12 | 13 | if (startDate.day() === endDate.day() && startDate.month() === endDate.month()) { 14 | return `${dayjs(startDate).format('MMM D')}${ 15 | showHour ? dayjs(startDate).format(', h:mm a') : '' 16 | } ${showHour ? dayjs(endDate).format('- h:mm a') : ''}`; 17 | } 18 | 19 | if (startDate.month() !== endDate.month()) { 20 | return `${dayjs(startDate).format('MMM D')}${ 21 | showHour ? dayjs(startDate).format(', h:mm a') : '' 22 | } ${dayjs(endDate).format('- MMM Do')}${showHour ? dayjs(endDate).format(', h:mm a') : ''}`; 23 | } 24 | 25 | if (startDate.month() === endDate.month()) { 26 | return `${dayjs(startDate).format('MMM D')}${ 27 | showHour ? dayjs(startDate).format(', h:mm a') : '' 28 | } ${dayjs(endDate).format('- Do')}${showHour ? dayjs(endDate).format(', h:mm a') : ''}`; 29 | } 30 | 31 | return `${dayjs(startDate).format('MMM Do')}${ 32 | showHour ? dayjs(startDate).format(', h:mm a') : '' 33 | } - ${dayjs(endDate).format('MMM Do')}${showHour ? dayjs(endDate).format(', h:mm a') : ''}`; 34 | }; 35 | -------------------------------------------------------------------------------- /shared/utils/form.ts: -------------------------------------------------------------------------------- 1 | export const populateFormData = (data: Record) => { 2 | const formData = new FormData(); 3 | 4 | Object.entries(data).forEach(([key, value]) => { 5 | if (value instanceof File) { 6 | formData.append(key, value, value.name); 7 | } else if (value instanceof Date) { 8 | formData.append(key, value.toISOString()); 9 | } else { 10 | if (value !== undefined && !(typeof value === 'string' && value?.length === 0)) { 11 | formData.append(key, String(value)); 12 | } 13 | } 14 | }); 15 | 16 | return formData; 17 | }; 18 | -------------------------------------------------------------------------------- /shared/utils/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Automatically generated by barrelsby. 3 | */ 4 | 5 | export * from './color'; 6 | export * from './config'; 7 | export * from './const'; 8 | export * from './date'; 9 | export * from './form'; 10 | export * from './icons'; 11 | export * from './isBrowser'; 12 | export * from './schema'; 13 | export * from './session'; 14 | export * from './string'; 15 | export * from './user'; 16 | export * from './sharing/email'; 17 | export * from './sharing/facebook'; 18 | export * from './sharing/linkedin'; 19 | export * from './sharing/objectToGetParams'; 20 | export * from './sharing/twitter'; 21 | -------------------------------------------------------------------------------- /shared/utils/isBrowser.ts: -------------------------------------------------------------------------------- 1 | export const isBrowser = typeof window !== 'undefined'; 2 | -------------------------------------------------------------------------------- /shared/utils/sharing/email.ts: -------------------------------------------------------------------------------- 1 | import { objectToGetParams } from './objectToGetParams'; 2 | 3 | type Options = { 4 | body?: string; 5 | separator?: string; 6 | subject?: string; 7 | }; 8 | 9 | export function emailLink(url: string, { subject, body, separator }: Options) { 10 | return 'mailto:' + objectToGetParams({ subject, body: body ? body + separator + url : url }); 11 | } 12 | -------------------------------------------------------------------------------- /shared/utils/sharing/facebook.ts: -------------------------------------------------------------------------------- 1 | import { objectToGetParams } from './objectToGetParams'; 2 | 3 | export function facebookLink( 4 | url: string, 5 | { quote, hashtag }: { quote?: string; hashtag?: string } 6 | ) { 7 | return ( 8 | 'https://www.facebook.com/sharer/sharer.php' + 9 | objectToGetParams({ 10 | u: url, 11 | quote, 12 | hashtag 13 | }) 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /shared/utils/sharing/linkedin.ts: -------------------------------------------------------------------------------- 1 | import { objectToGetParams } from './objectToGetParams'; 2 | 3 | type Options = { 4 | title?: string; 5 | summary?: string; 6 | source?: string; 7 | }; 8 | 9 | export function linkedinLink(url: string, { title, summary, source }: Options) { 10 | return ( 11 | 'https://linkedin.com/shareArticle' + 12 | objectToGetParams({ url, mini: 'true', title, summary, source }) 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /shared/utils/sharing/objectToGetParams.ts: -------------------------------------------------------------------------------- 1 | export function objectToGetParams(object: { [key: string]: string | number | undefined | null }) { 2 | const params = Object.entries(object) 3 | .filter(([, value]) => value !== undefined && value !== null) 4 | .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); 5 | 6 | return params.length > 0 ? `?${params.join('&')}` : ''; 7 | } 8 | -------------------------------------------------------------------------------- /shared/utils/sharing/twitter.ts: -------------------------------------------------------------------------------- 1 | import { objectToGetParams } from './objectToGetParams'; 2 | 3 | export function twitterLink( 4 | url: string, 5 | { 6 | title, 7 | via, 8 | hashtags = [], 9 | related = [] 10 | }: { title?: string; via?: string; hashtags?: string[]; related?: string[] } 11 | ) { 12 | return ( 13 | 'https://twitter.com/share' + 14 | objectToGetParams({ 15 | url, 16 | text: title, 17 | via, 18 | hashtags: hashtags.length > 0 ? hashtags.join(',') : undefined, 19 | related: related.length > 0 ? related.join(',') : undefined 20 | }) 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /shared/utils/string.ts: -------------------------------------------------------------------------------- 1 | export const capitalizeFirstLetter = (string: string) => { 2 | return string.charAt(0).toUpperCase() + string.slice(1); 3 | }; 4 | 5 | export const capitalizeOnlyFirstLetter = (string: string) => { 6 | return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); 7 | }; 8 | 9 | export const processSlug = (input: string) => { 10 | return input 11 | ?.toString() 12 | .toLowerCase() 13 | .replace(/\s+/g, '-') // Replace spaces with - 14 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars 15 | .replace(/\-\-+/g, '-') // Replace multiple - with single - 16 | .replace(/^-+/, '') // Trim - from start of text 17 | .replace(/-+$/, ''); // Trim - from end of text 18 | }; 19 | 20 | export const generateSlug = async ( 21 | name: string, 22 | validate: (slug: string) => Promise 23 | ): Promise => { 24 | const currentSlug = processSlug(name); 25 | 26 | const isValid = await validate(currentSlug); 27 | 28 | if (isValid) { 29 | return currentSlug; 30 | } 31 | 32 | return generateSlug(name + Math.ceil(Math.random() * 10), validate); 33 | }; 34 | 35 | export const slugify = (input: string) => { 36 | return input 37 | ?.toString() 38 | .toLowerCase() 39 | .replace(/\s+/g, '-') // Replace spaces with - 40 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars 41 | .replace(/\-\-+/g, '-'); // Replace multiple - with single - 42 | }; 43 | -------------------------------------------------------------------------------- /shared/utils/user.ts: -------------------------------------------------------------------------------- 1 | import * as Prisma from '@prisma/client'; 2 | 3 | export type StrippedUser = Omit; 4 | export type FullUser = Omit; 5 | 6 | export const attendeeWithUserInclude = { 7 | attendee: { 8 | include: { 9 | user: true, 10 | role: true 11 | } 12 | } 13 | }; 14 | 15 | export type AttendeeWithUser = Prisma.EventAttendee & { 16 | user: StrippedUser; 17 | role: Prisma.EventRole; 18 | }; 19 | 20 | export type AttendeeWithUserInput = Prisma.EventAttendee & { 21 | user: Prisma.User; 22 | role: Prisma.EventRole; 23 | }; 24 | 25 | export const fullUser = (user: Prisma.User): FullUser => { 26 | const { password, ...rest } = user; 27 | 28 | return rest; 29 | }; 30 | 31 | export const stripUser = (user: Prisma.User): StrippedUser => { 32 | const { password, email, role, ...rest } = user; 33 | 34 | return rest; 35 | }; 36 | 37 | export const stripAttendeeWithUser = (attendee: AttendeeWithUserInput): AttendeeWithUser => { 38 | const { user, ...rest } = attendee; 39 | 40 | const userStripped = stripUser(user); 41 | 42 | return { 43 | ...rest, 44 | user: userStripped 45 | }; 46 | }; 47 | 48 | export const stripAttendeesWithUser = ( 49 | attendees: Array 50 | ): AttendeeWithUser[] => { 51 | return attendees.map((attendee) => stripAttendeeWithUser(attendee)); 52 | }; 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "skipLibCheck": true, 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "resolveJsonModule": true, 8 | "noEmit": true 9 | }, 10 | "exclude": ["node_modules"], 11 | "extends": "expo/tsconfig.base" 12 | } 13 | -------------------------------------------------------------------------------- /web/.env.local.example: -------------------------------------------------------------------------------- 1 | # AWS 2 | EVENTAL_AWS_ACCESS_KEY_ID="YOUR_EVENTAL_AWS_ACCESS_KEY_ID" 3 | EVENTAL_AWS_SECRET_ACCESS_KEY="YOUR_EVENTAL_AWS_SECRET_ACCESS_KEY" 4 | EVENTAL_AWS_REGION="YOUR_EVENTAL_AWS_REGION" 5 | 6 | # Upstash 7 | UPSTASH_TOKEN="YOUR_UPSTASH_TOKEN" 8 | UPSTASH_URL="YOUR_UPSTASH_URL" 9 | 10 | # Stripe 11 | NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=YOUR_STRIPE_PUBLISHABLE_KEY 12 | STRIPE_SECRET_KEY=YOUR_STRIPE_SECRET_KEY 13 | STRIPE_WEBHOOK_SECRET=YOUR_STRIPE_WEBHOOK_SECRET 14 | 15 | # Google Analytics 16 | NEXT_PUBLIC_GOOGLE_ANALYTICS="YOUR_NEXT_PUBLIC_GOOGLE_ANALYTICS" -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # testing 2 | /coverage/ 3 | 4 | # next.js 5 | /.next/ 6 | /out/ 7 | 8 | # production 9 | /build/ 10 | 11 | # deploy 12 | .vercel/ 13 | 14 | #SES Templates 15 | email/output/**/operation.json 16 | 17 | #Sales 18 | email/sendSalesData.ts 19 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | ## Todo List 2 | 3 | - featured role members 4 | 5 | ## Post MVP Todo List 6 | 7 | - session tags 8 | - adjust session times when event date is moved 9 | - bulk create sessions 10 | - session file upload 11 | - rate limit request password reset & verify email request etc 12 | - customize react toast 13 | - clean up mutations 14 | - event banner 15 | - Excel export for event data 16 | - print your schedule 17 | - attach people -> create attendee -> create role dialog is under 18 | - railway redis migration 19 | - make events truly private 20 | - sort labels in dropdowns 21 | - Create event full form/multistep form (with skip functionality) 22 | - role member's can edit sessions? 23 | - google maps embed for addresses 24 | - search by name debounce (see if react query has debounce built in?) 25 | - Google oauth for login 26 | -------------------------------------------------------------------------------- /web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['next/babel'], 3 | plugins: ['superjson-next'] 4 | }; 5 | -------------------------------------------------------------------------------- /web/components/billing/EventalPro.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type EventalProProps = { 4 | className?: string; 5 | isEducation?: boolean; 6 | }; 7 | 8 | export const EventalPro: React.FC = (props) => { 9 | const { isEducation = false } = props; 10 | 11 | return ( 12 |
13 | Evental 14 | 15 | {isEducation ? 'EDU' : 'PRO'} 16 | 17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /web/components/billing/PromotionalOffer.tsx: -------------------------------------------------------------------------------- 1 | import { faPercent } from '@fortawesome/free-solid-svg-icons'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import React from 'react'; 4 | 5 | import { faBadge } from '@eventalapp/shared/utils/icons'; 6 | 7 | import { sale } from '../../utils/price'; 8 | 9 | export const PromotionalOffer = () => { 10 | if (sale.percentage > 0 || sale.flatAmount > 0) { 11 | return ( 12 |
13 |
14 | 20 | 26 |
27 |
28 |

29 | Promotional Offer! 30 |

31 | {sale.percentage > 0 && ( 32 |

33 | {sale.percentage}% off until October 31st, 2022. 34 |

35 | )} 36 | {sale.flatAmount > 0 && ( 37 | 38 | ${sale.flatAmount} off until October 31st, 2022. 39 | 40 | )} 41 |
42 |
43 | ); 44 | } 45 | 46 | return null; 47 | }; 48 | -------------------------------------------------------------------------------- /web/components/error/AlreadySignedInPage.tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | import React from 'react'; 4 | 5 | import Column from '../layout/Column'; 6 | import { Footer } from '../layout/Footer'; 7 | import PageWrapper from '../layout/PageWrapper'; 8 | import { Navigation } from '../navigation'; 9 | import { Heading } from '../primitives/Heading'; 10 | import { LinkButton } from '../primitives/LinkButton'; 11 | 12 | export const AlreadySignedInPage = () => ( 13 | 14 | 15 | Already Signed In 16 | 17 | 18 | 19 | 20 | 21 |
22 | Sign in 23 |
24 |

You are already signed in.

25 | 26 | 27 | Return home 28 | 29 | 30 |
31 | 32 |