Events

Events app

The event pages, the composables and components behind them, and how to add tabs to the admin event page.

This page covers what Events adds to the Vue side of your app: pages, components, composables and the admin event page's tabs. It is for app developers and module authors. Set up the module first: Overview.

Pages

Each page is opt-in through its option and follows the UX patterns of its area.

PageDefault pathWho
Event list/eventsEveryone
Event page, with the ticket picker/events/:slugEveryone
My tickets, each with its QR code/account/ticketsSigned-in users
Event list for staff/admin/eventsStaff, with Admin
Event page for staff, with Details, Tickets and Attendees tabs/admin/events/:idStaff, with Admin
  • Menus. With Menu, the tickets page joins the account menu and the staff list joins the admin sidebar (admin.nav).
  • Account layout. The tickets page has no container of its own. The account layout of Account draws the navigation around it.
  • Deleting. An event with paid tickets cannot be deleted. Archive it instead.
  • Locale. Every link to an event goes through useEventLinks(), so a visitor on /en/events stays under /en.

Components

All components are auto-imported.

  • Public: EventCard, EventTicketPicker and EventTicketCard, which shows the QR code.
  • Staff: EventFormSlideover creates an event. EventTicketTypeSlideover, EventAddonGroupSlideover and EventAddonSlideover are the catalogue forms, and each closes with { id } or false. EventAdminAddonGroups lists a ticket type's add-on groups and emits only refresh.

Composables

All composables are auto-imported. @frigear-nu/events/composables exports them too.

ComposableReturns
useEvents(){ events, grouped, status, refresh }. grouped splits into ongoing, upcoming and past.
useEvent(slug){ event, status, error, refresh, purchase(ticketTypeId, addonIds?) }.
useMyTickets(){ tickets, status, loading, error, refresh, pay(ticketId) }. Await it in pages. It survives navigation.
useEventLinks()Locale-aware links: events(), event(slug), tickets(), admin() and adminEvent(id).
useEventActions()Staff actions on events: create(), duplicate(event), remove(event) and menu(event, options?).
useEventCatalogActions(options?)Staff actions on ticket types, add-on groups and add-ons. See below.
useEventsLocale()The current locale from @nuxtjs/i18n, else en.
useEventsFallbackLocale()The site's default locale, else en.
useEventsTranslate()tr(value): a translated text in the current locale, then the default locale, then en.

useEventActions().create() opens the slideover, then goes to the new event's page. It resolves with { id, slug }, or false when cancelled. menu(event, { extra?, onRemoved? }) builds the row menu.

Catalogue actions

useEventCatalogActions(options?) owns the forms, deletes and row menus of an event's ticket types, add-on groups and add-ons. Its menus have no Open item, because these records have no page of their own.

OptionPurpose
eventIdThe event that new ticket types join: a value, ref or getter. Needed only for openTicketType() without a record.
onChangedRuns after anything is saved or deleted, for example to refresh the event.
membershipA getter for the membership plans the ticket type form offers. It is read when the form opens.
ReturnedPurpose
openTicketType(ticketType?), openGroup(ticketType, group?), openAddon(ticketType, group, addon?)Open the create form (no record) or the edit form. Resolve { id } or false.
removeTicketType(ticketType), removeGroup(group), removeAddon(addon)Confirm, delete, and resolve true once deleted.
typeMenu(ticketType), groupMenu(ticketType, group), addonMenu(ticketType, group, addon)Row menu items: the edits, then the delete in its own red group.

Utils

@frigear-nu/events/utils is safe in the Vue app and in Nitro. It exports the DTO and hook types, translate, normalizeMembership, DEFAULT_CURRENCY and id helpers. @frigear-nu/events/api exports eventsApi, the typed paths of the routes.

Add a tab to the admin event page

The staff event page renders the admin.events tab area of Widget. Events registers Details, Tickets and Attendees there. Any module can add a tab from its setup():

import { extendTabs } from '@frigear-nu/widget/utils'

extendTabs(nuxt, 'admin.events', { id: 'notes', component: resolver.resolve('./runtime/app/tabs/Notes.vue'), label: 'Notes', order: 30 })

Each tab component receives event as a prop, the response of GET /api/admin/events/:id, and may emit refresh. Without Widget, the three built-in tabs render stacked.

Hiding a tab is not access control. Check the tab's API calls with authorize(event, 'events:manage') too.

Next steps

  • Reference – the routes and translation keys these pages use.
  • Widget – how tab areas and extendTabs work.
  • Forms and actions – the slideover and row menu patterns of the admin pages.
Copyright © 2026