@frigear-nu/events 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/module.d.mts +105 -1
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +35 -31
  4. package/dist/runtime/app/components/EventAddonGroupSlideover.d.vue.ts +18 -0
  5. package/dist/runtime/app/components/EventAddonGroupSlideover.vue +67 -0
  6. package/dist/runtime/app/components/EventAddonGroupSlideover.vue.d.ts +18 -0
  7. package/dist/runtime/app/components/EventAddonSlideover.d.vue.ts +20 -0
  8. package/dist/runtime/app/components/EventAddonSlideover.vue +104 -0
  9. package/dist/runtime/app/components/EventAddonSlideover.vue.d.ts +20 -0
  10. package/dist/runtime/app/components/EventAdminAddonGroups.d.vue.ts +1 -9
  11. package/dist/runtime/app/components/EventAdminAddonGroups.vue +7 -48
  12. package/dist/runtime/app/components/EventAdminAddonGroups.vue.d.ts +1 -9
  13. package/dist/runtime/app/components/EventCard.vue +1 -0
  14. package/dist/runtime/app/components/EventFormSlideover.vue +46 -23
  15. package/dist/runtime/app/components/EventTicketCard.vue +60 -62
  16. package/dist/runtime/app/components/EventTicketPicker.vue +1 -0
  17. package/dist/runtime/app/components/EventTicketTypeSlideover.d.vue.ts +20 -0
  18. package/dist/runtime/app/components/EventTicketTypeSlideover.vue +177 -0
  19. package/dist/runtime/app/components/EventTicketTypeSlideover.vue.d.ts +20 -0
  20. package/dist/runtime/app/composables/index.d.ts +2 -0
  21. package/dist/runtime/app/composables/index.js +1 -0
  22. package/dist/runtime/app/composables/useEventActions.d.ts +3 -3
  23. package/dist/runtime/app/composables/useEventActions.js +10 -4
  24. package/dist/runtime/app/composables/useEventCatalogActions.d.ts +33 -0
  25. package/dist/runtime/app/composables/useEventCatalogActions.js +108 -0
  26. package/dist/runtime/app/composables/useEventLinks.js +1 -0
  27. package/dist/runtime/app/composables/useEvents.d.ts +29 -16
  28. package/dist/runtime/app/composables/useEvents.js +7 -6
  29. package/dist/runtime/app/pages/admin-event.vue +72 -136
  30. package/dist/runtime/app/pages/admin.vue +92 -29
  31. package/dist/runtime/app/pages/event.vue +9 -4
  32. package/dist/runtime/app/pages/events.vue +1 -0
  33. package/dist/runtime/app/pages/tickets.vue +5 -0
  34. package/dist/runtime/app/tabs/Attendees.vue +79 -39
  35. package/dist/runtime/app/tabs/Details.d.vue.ts +1 -1
  36. package/dist/runtime/app/tabs/Details.vue +113 -30
  37. package/dist/runtime/app/tabs/Details.vue.d.ts +1 -1
  38. package/dist/runtime/app/tabs/Tickets.vue +103 -139
  39. package/dist/runtime/app/utils/admin-event.d.ts +53 -38
  40. package/dist/runtime/app/utils/admin-event.js +14 -0
  41. package/dist/runtime/app/widgets/UpcomingEvents.vue +3 -1
  42. package/dist/runtime/app/widgets/UpcomingEventsStat.vue +3 -1
  43. package/dist/runtime/i18n/lang/da.json +22 -3
  44. package/dist/runtime/i18n/lang/en.json +22 -3
  45. package/dist/runtime/menus.js +1 -0
  46. package/dist/runtime/server/bootstrap/my-tickets.d.ts +1 -1
  47. package/dist/runtime/server/mcp/schemas.d.ts +4 -4
  48. package/dist/runtime/server/routes/admin/addon-group.delete.js +6 -3
  49. package/dist/runtime/server/routes/admin/addon-group.patch.js +6 -4
  50. package/dist/runtime/server/routes/admin/addon-groups.post.js +8 -6
  51. package/dist/runtime/server/routes/admin/addon.delete.js +6 -3
  52. package/dist/runtime/server/routes/admin/addon.patch.js +8 -5
  53. package/dist/runtime/server/routes/admin/addons.post.js +8 -6
  54. package/dist/runtime/server/routes/admin/attendees.get.js +38 -8
  55. package/dist/runtime/server/routes/{public → admin}/check-in.post.js +4 -3
  56. package/dist/runtime/server/routes/admin/event-clone.post.js +37 -19
  57. package/dist/runtime/server/routes/admin/event.delete.js +7 -4
  58. package/dist/runtime/server/routes/admin/event.get.js +4 -2
  59. package/dist/runtime/server/routes/admin/event.patch.js +4 -2
  60. package/dist/runtime/server/routes/admin/events.get.js +36 -3
  61. package/dist/runtime/server/routes/admin/events.post.js +3 -4
  62. package/dist/runtime/server/routes/admin/membership-options.get.js +3 -2
  63. package/dist/runtime/server/routes/admin/ticket-cancel.post.js +7 -8
  64. package/dist/runtime/server/routes/admin/ticket-refund.post.js +7 -5
  65. package/dist/runtime/server/routes/admin/ticket-type.delete.js +7 -4
  66. package/dist/runtime/server/routes/admin/ticket-type.patch.js +4 -2
  67. package/dist/runtime/server/routes/admin/ticket-types.post.js +10 -7
  68. package/dist/runtime/server/routes/admin/upcoming.get.js +7 -3
  69. package/dist/runtime/server/routes/me/ticket-pay.post.js +13 -0
  70. package/dist/runtime/server/routes/me/ticket.get.js +6 -0
  71. package/dist/runtime/server/routes/public/event.get.js +5 -2
  72. package/dist/runtime/server/routes/public/purchase.post.js +3 -1
  73. package/dist/runtime/server/utils/admin-dto.d.ts +30 -0
  74. package/dist/runtime/server/utils/admin-dto.js +101 -0
  75. package/dist/runtime/server/utils/admin-schemas.js +2 -1
  76. package/dist/runtime/server/utils/admin.d.ts +10 -8
  77. package/dist/runtime/server/utils/admin.js +20 -6
  78. package/dist/runtime/server/utils/auth.d.ts +5 -4
  79. package/dist/runtime/server/utils/auth.js +6 -7
  80. package/dist/runtime/server/utils/tickets.js +6 -11
  81. package/dist/runtime/utils/api.d.ts +48 -0
  82. package/dist/runtime/utils/api.js +51 -0
  83. package/dist/runtime/utils/index.d.ts +2 -1
  84. package/dist/runtime/utils/index.js +1 -0
  85. package/dist/types.d.mts +1 -1
  86. package/package.json +19 -14
  87. package/dist/runtime/app/components/EventAdminAddonDialog.d.vue.ts +0 -11
  88. package/dist/runtime/app/components/EventAdminAddonDialog.vue +0 -112
  89. package/dist/runtime/app/components/EventAdminAddonDialog.vue.d.ts +0 -11
  90. package/dist/runtime/app/components/EventAdminAddonGroupDialog.d.vue.ts +0 -11
  91. package/dist/runtime/app/components/EventAdminAddonGroupDialog.vue +0 -78
  92. package/dist/runtime/app/components/EventAdminAddonGroupDialog.vue.d.ts +0 -11
  93. package/dist/runtime/app/components/EventAdminTicketTypeDialog.d.vue.ts +0 -20
  94. package/dist/runtime/app/components/EventAdminTicketTypeDialog.vue +0 -174
  95. package/dist/runtime/app/components/EventAdminTicketTypeDialog.vue.d.ts +0 -20
  96. package/dist/runtime/server/routes/public/ticket-pay.post.js +0 -10
  97. package/dist/runtime/server/routes/public/ticket.get.js +0 -4
  98. /package/dist/runtime/server/routes/{public → admin}/check-in.post.d.ts +0 -0
  99. /package/dist/runtime/server/routes/{public → me}/ticket-pay.post.d.ts +0 -0
  100. /package/dist/runtime/server/routes/{public → me}/ticket.get.d.ts +0 -0
  101. /package/dist/runtime/server/routes/{public/tickets-me.get.d.ts → me/tickets.get.d.ts} +0 -0
  102. /package/dist/runtime/server/routes/{public/tickets-me.get.js → me/tickets.get.js} +0 -0
@@ -2,7 +2,8 @@ import { translate } from '@frigear-nu/ui/translated';
2
2
  import type { MembershipRequirement } from '../types.js';
3
3
  export { translate };
4
4
  export * from './links.js';
5
- export type { AddonDto, AddonGroupDto, AuthorizeContext, CheckInResponse, EventDetailDto, EventStatus, EventSummaryDto, MembershipRequirement, ModuleOptions, PurchaseBeforeContext, PurchaseBody, PurchaseResponse, TicketDto, TicketHookContext, TicketStatus, TicketTypeDto, Translated, } from '../types.js';
5
+ export * from './api.js';
6
+ export type { AddonDto, AdminAddonDto, AdminAddonGroupDto, AdminEventDetailDto, AdminEventDto, AdminTicketTypeDto, AttendeeDto, AddonGroupDto, AuthorizeContext, CheckInResponse, EventDetailDto, EventStatus, EventSummaryDto, MembershipOptionsDto, MembershipRequirement, ModuleOptions, PurchaseBeforeContext, PurchaseBody, PurchaseResponse, TicketDto, TicketHookContext, TicketStatus, TicketTypeDto, Translated, UpcomingEventsDto, } from '../types.js';
6
7
  /** The currency tickets and add-ons are sold in unless they say otherwise (the column default). */
7
8
  export declare const DEFAULT_CURRENCY = "DKK";
8
9
  /** Normalises a stored requirement: empty plan lists mean "any active subscription". */
@@ -2,6 +2,7 @@ import { translate } from "@frigear-nu/ui/translated";
2
2
  import { formatMoney as formatPrice } from "@frigear-nu/ui/money";
3
3
  export { translate };
4
4
  export * from "./links.js";
5
+ export * from "./api.js";
5
6
  export const DEFAULT_CURRENCY = "DKK";
6
7
  export function normalizeMembership(value) {
7
8
  if (value === "any") return "any";
package/dist/types.d.mts CHANGED
@@ -2,4 +2,4 @@ export { type Translated } from '@frigear-nu/ui/translated'
2
2
 
3
3
  export { default } from './module.mjs'
4
4
 
5
- export { type AddonDto, type AddonGroupDto, type AuthorizeContext, type CheckInResponse, type EventDetailDto, type EventStatus, type EventSummaryDto, type EventsRuntimeConfig, type MembershipCheckContext, type MembershipOption, type MembershipOptionsContext, type MembershipRequirement, type ModuleOptions, type PublicModuleOptions, type PurchaseBeforeContext, type PurchaseBody, type PurchaseResponse, type TicketDto, type TicketHookContext, type TicketStatus, type TicketTypeDto } from './module.mjs'
5
+ export { type AddonDto, type AddonGroupDto, type AdminAddonDto, type AdminAddonGroupDto, type AdminEventDetailDto, type AdminEventDto, type AdminTicketTypeDto, type AttendeeDto, type AuthorizeContext, type CheckInResponse, type EventDetailDto, type EventStatus, type EventSummaryDto, type EventsRuntimeConfig, type MembershipCheckContext, type MembershipOption, type MembershipOptionsContext, type MembershipOptionsDto, type MembershipRequirement, type ModuleOptions, type PublicModuleOptions, type PurchaseBeforeContext, type PurchaseBody, type PurchaseResponse, type TicketDto, type TicketHookContext, type TicketStatus, type TicketTypeDto, type UpcomingEventsDto } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/events",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Events with ticket types, addons, capacity, membership requirements, Stripe payments and QR check-in for the frigear ecosystem, built on @frigear-nu/payments.",
5
5
  "homepage": "https://docs.frigear.dev/modules/events",
6
6
  "repository": {
@@ -21,6 +21,11 @@
21
21
  "types": "./dist/runtime/utils/index.d.ts",
22
22
  "import": "./dist/runtime/utils/index.js",
23
23
  "default": "./dist/runtime/utils/index.js"
24
+ },
25
+ "./api": {
26
+ "types": "./dist/runtime/utils/api.d.ts",
27
+ "import": "./dist/runtime/utils/api.js",
28
+ "default": "./dist/runtime/utils/api.js"
24
29
  }
25
30
  },
26
31
  "files": [
@@ -30,15 +35,15 @@
30
35
  "playground"
31
36
  ],
32
37
  "dependencies": {
33
- "@frigear-nu/admin": "0.1.2",
34
- "@frigear-nu/module-kit": "1.1.2",
35
- "@frigear-nu/payments": "0.1.2",
36
- "@frigear-nu/stripe": "0.1.2",
37
- "@frigear-nu/subscriptions": "0.1.2",
38
- "@frigear-nu/ui": "0.1.2",
39
- "@frigear-nu/auth": "0.1.3",
40
- "@frigear-nu/mcp": "0.1.2",
41
- "@frigear-nu/users": "1.1.3",
38
+ "@frigear-nu/admin": "0.2.0",
39
+ "@frigear-nu/module-kit": "1.2.0",
40
+ "@frigear-nu/payments": "0.2.0",
41
+ "@frigear-nu/stripe": "0.2.0",
42
+ "@frigear-nu/subscriptions": "0.2.0",
43
+ "@frigear-nu/ui": "0.2.0",
44
+ "@frigear-nu/auth": "0.2.0",
45
+ "@frigear-nu/mcp": "0.2.0",
46
+ "@frigear-nu/users": "1.2.0",
42
47
  "@libsql/client": "^0.18.0",
43
48
  "@nuxt/kit": "^4.6.1",
44
49
  "@vueuse/core": "^15.0.0",
@@ -51,8 +56,8 @@
51
56
  "zod": "^4.6.5"
52
57
  },
53
58
  "peerDependencies": {
54
- "@frigear-nu/menu": "0.1.2",
55
- "@frigear-nu/widget": "0.1.2",
59
+ "@frigear-nu/menu": "0.2.0",
60
+ "@frigear-nu/widget": "0.2.0",
56
61
  "@nuxt/ui": "^4.12.0",
57
62
  "@nuxthub/core": "^0.10.8"
58
63
  },
@@ -68,8 +73,8 @@
68
73
  }
69
74
  },
70
75
  "devDependencies": {
71
- "@frigear-nu/menu": "0.1.2",
72
- "@frigear-nu/widget": "0.1.2",
76
+ "@frigear-nu/menu": "0.2.0",
77
+ "@frigear-nu/widget": "0.2.0",
73
78
  "@nuxt/devtools": "^3.4.2",
74
79
  "@nuxt/eslint-config": "^1.17.0",
75
80
  "@nuxt/module-builder": "^1.0.3",
@@ -1,11 +0,0 @@
1
- import type { StaffAddon, StaffAddonGroup } from '../utils/admin-event.js';
2
- declare function open(addonGroup: StaffAddonGroup, currency: string, existing?: StaffAddon): void;
3
- declare const __VLS_export: import("vue").DefineComponent<{}, {
4
- open: typeof open;
5
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
- refresh: () => any;
7
- }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
- onRefresh?: (() => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
- declare const _default: typeof __VLS_export;
11
- export default _default;
@@ -1,112 +0,0 @@
1
- <script setup>
2
- import { fromCents, toCents } from "@frigear-nu/ui/money";
3
- import { DEFAULT_CURRENCY } from "../../utils";
4
- defineOptions({ name: "EventAdminAddonDialog" });
5
- const emit = defineEmits(["refresh"]);
6
- const t = useModuleT("events");
7
- const tr = useEventsTranslate();
8
- const { busy, run } = useAction();
9
- const dialog = ref(false);
10
- const groupId = ref("");
11
- const editingAddon = ref(null);
12
- const addon = reactive({ label: void 0, description: void 0, price: 0, capacity: "" });
13
- const addonCurrency = ref(DEFAULT_CURRENCY);
14
- function open(addonGroup, currency, existing) {
15
- groupId.value = addonGroup.id;
16
- addonCurrency.value = currency;
17
- editingAddon.value = existing ?? null;
18
- Object.assign(addon, {
19
- label: existing?.label,
20
- description: existing?.description ?? void 0,
21
- price: existing ? fromCents(existing.price, currency) : 0,
22
- capacity: existing?.capacity ?? ""
23
- });
24
- dialog.value = true;
25
- }
26
- defineExpose({ open });
27
- const save = () => run(async () => {
28
- const body = {
29
- label: addon.label,
30
- description: addon.description ?? null,
31
- // Prices are stored in minor units (øre/cents).
32
- price: toCents(Number(addon.price), addonCurrency.value),
33
- capacity: addon.capacity === "" ? null : Number(addon.capacity)
34
- };
35
- if (editingAddon.value) {
36
- await $fetch(`/api/events/admin/addons/${editingAddon.value.id}`, { method: "PATCH", body });
37
- } else {
38
- await $fetch(`/api/events/admin/addon-groups/${groupId.value}/addons`, { method: "POST", body });
39
- }
40
- dialog.value = false;
41
- emit("refresh");
42
- }, { success: t("admin.addonSaved", "Add-on saved.") });
43
- </script>
44
-
45
- <template>
46
- <UModal
47
- v-model:open="dialog"
48
- :title="editingAddon ? t('admin.editAddon', 'Edit choice') : t('admin.addAddon', 'Add choice')"
49
- >
50
- <template #body>
51
- <form
52
- id="admin-addon-form"
53
- class="flex flex-col gap-4"
54
- @submit.prevent="save"
55
- >
56
- <UFormField :label="t('admin.name', 'Name')">
57
- <TranslatedInput
58
- v-model="addon.label"
59
- required
60
- />
61
- </UFormField>
62
- <UFormField :label="t('admin.description', 'Description')">
63
- <TranslatedInput
64
- v-model="addon.description"
65
- multiline
66
- :rows="2"
67
- />
68
- </UFormField>
69
- <UFormField
70
- :label="t('admin.price', 'Price')"
71
- :description="t('admin.addonPriceHelp', 'Added to the ticket price. 0 means included.')"
72
- >
73
- <UInput
74
- v-model="addon.price"
75
- type="number"
76
- min="0"
77
- step="0.01"
78
- class="w-full"
79
- />
80
- </UFormField>
81
- <UFormField
82
- :label="t('admin.capacity', 'Capacity')"
83
- :description="t('admin.capacityHelp', 'Leave empty for unlimited.')"
84
- >
85
- <UInput
86
- v-model="addon.capacity"
87
- type="number"
88
- min="0"
89
- class="w-full"
90
- />
91
- </UFormField>
92
- </form>
93
- </template>
94
- <template #footer>
95
- <div class="flex w-full justify-end gap-2">
96
- <UButton
97
- variant="outline"
98
- color="neutral"
99
- :label="t('admin.cancel', 'Cancel')"
100
- @click="dialog = false"
101
- />
102
- <UButton
103
- type="submit"
104
- form="admin-addon-form"
105
- :loading="busy"
106
- :disabled="!tr(addon.label)"
107
- :label="t('admin.save', 'Save')"
108
- />
109
- </div>
110
- </template>
111
- </UModal>
112
- </template>
@@ -1,11 +0,0 @@
1
- import type { StaffAddon, StaffAddonGroup } from '../utils/admin-event.js';
2
- declare function open(addonGroup: StaffAddonGroup, currency: string, existing?: StaffAddon): void;
3
- declare const __VLS_export: import("vue").DefineComponent<{}, {
4
- open: typeof open;
5
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
- refresh: () => any;
7
- }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
- onRefresh?: (() => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
- declare const _default: typeof __VLS_export;
11
- export default _default;
@@ -1,11 +0,0 @@
1
- import type { StaffAddonGroup, StaffTicketType } from '../utils/admin-event.js';
2
- declare function open(ticketType: StaffTicketType, existing?: StaffAddonGroup): void;
3
- declare const __VLS_export: import("vue").DefineComponent<{}, {
4
- open: typeof open;
5
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
- refresh: () => any;
7
- }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
- onRefresh?: (() => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
- declare const _default: typeof __VLS_export;
11
- export default _default;
@@ -1,78 +0,0 @@
1
- <script setup>
2
- defineOptions({ name: "EventAdminAddonGroupDialog" });
3
- const emit = defineEmits(["refresh"]);
4
- const t = useModuleT("events");
5
- const tr = useEventsTranslate();
6
- const { busy, run } = useAction();
7
- const dialog = ref(false);
8
- const ticketTypeId = ref("");
9
- const editingGroup = ref(null);
10
- const group = reactive({ title: void 0, required: false, multiple: false });
11
- function open(ticketType, existing) {
12
- ticketTypeId.value = ticketType.id;
13
- editingGroup.value = existing ?? null;
14
- Object.assign(group, { title: existing?.title, required: existing?.required ?? false, multiple: existing?.multiple ?? false });
15
- dialog.value = true;
16
- }
17
- defineExpose({ open });
18
- const save = () => run(async () => {
19
- const body = { title: group.title, required: group.required, multiple: group.multiple };
20
- if (editingGroup.value) {
21
- await $fetch(`/api/events/admin/addon-groups/${editingGroup.value.id}`, { method: "PATCH", body });
22
- } else {
23
- await $fetch(`/api/events/admin/ticket-types/${ticketTypeId.value}/addon-groups`, { method: "POST", body });
24
- }
25
- dialog.value = false;
26
- emit("refresh");
27
- }, { success: t("admin.groupSaved", "Add-on group saved.") });
28
- </script>
29
-
30
- <template>
31
- <UModal
32
- v-model:open="dialog"
33
- :title="editingGroup ? t('admin.editGroup', 'Edit add-on group') : t('admin.addGroup', 'Add add-on group')"
34
- >
35
- <template #body>
36
- <form
37
- id="admin-addon-group-form"
38
- class="flex flex-col gap-4"
39
- @submit.prevent="save"
40
- >
41
- <UFormField
42
- :label="t('admin.groupTitle', 'Title')"
43
- :description="t('admin.groupTitleHelp', 'Shown above the choices, e.g. Food.')"
44
- >
45
- <TranslatedInput
46
- v-model="group.title"
47
- required
48
- />
49
- </UFormField>
50
- <UCheckbox
51
- v-model="group.required"
52
- :label="t('admin.groupRequiredLabel', 'Buyers must choose at least one')"
53
- />
54
- <UCheckbox
55
- v-model="group.multiple"
56
- :label="t('admin.groupMultipleLabel', 'Buyers can choose several')"
57
- />
58
- </form>
59
- </template>
60
- <template #footer>
61
- <div class="flex w-full justify-end gap-2">
62
- <UButton
63
- variant="outline"
64
- color="neutral"
65
- :label="t('admin.cancel', 'Cancel')"
66
- @click="dialog = false"
67
- />
68
- <UButton
69
- type="submit"
70
- form="admin-addon-group-form"
71
- :loading="busy"
72
- :disabled="!tr(group.title)"
73
- :label="t('admin.save', 'Save')"
74
- />
75
- </div>
76
- </template>
77
- </UModal>
78
- </template>
@@ -1,11 +0,0 @@
1
- import type { StaffAddonGroup, StaffTicketType } from '../utils/admin-event.js';
2
- declare function open(ticketType: StaffTicketType, existing?: StaffAddonGroup): void;
3
- declare const __VLS_export: import("vue").DefineComponent<{}, {
4
- open: typeof open;
5
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
- refresh: () => any;
7
- }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
- onRefresh?: (() => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
- declare const _default: typeof __VLS_export;
11
- export default _default;
@@ -1,20 +0,0 @@
1
- import type { StaffTicketType } from '../utils/admin-event.js';
2
- type __VLS_Props = {
3
- eventId: string;
4
- /** Membership rules are only offered when @frigear-nu/subscriptions is installed. */
5
- membershipEnabled: boolean;
6
- plans: Array<{
7
- label: string;
8
- value: string;
9
- }>;
10
- };
11
- declare function open(ticketType?: StaffTicketType): void;
12
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
13
- open: typeof open;
14
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
15
- refresh: () => any;
16
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
17
- onRefresh?: (() => any) | undefined;
18
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
- declare const _default: typeof __VLS_export;
20
- export default _default;
@@ -1,174 +0,0 @@
1
- <script setup>
2
- import { toCents, fromCents } from "@frigear-nu/ui/money";
3
- import { DEFAULT_CURRENCY, ticketMdcData } from "../../utils";
4
- defineOptions({ name: "EventAdminTicketTypeDialog" });
5
- const props = defineProps({
6
- eventId: { type: String, required: true },
7
- membershipEnabled: { type: Boolean, required: true },
8
- plans: { type: Array, required: true }
9
- });
10
- const emit = defineEmits(["refresh"]);
11
- const t = useModuleT("events");
12
- const locale = useEventsLocale();
13
- const tr = useEventsTranslate();
14
- const { busy, run } = useAction();
15
- const modes = computed(() => [
16
- { label: t("admin.ruleNone", "Nobody"), value: "none" },
17
- { label: t("admin.ruleAny", "Any membership"), value: "any" },
18
- { label: t("admin.rulePlans", "Specific memberships"), value: "plans" }
19
- ]);
20
- const toRule = (value) => value === "any" ? { mode: "any", plans: [] } : Array.isArray(value) && value.length ? { mode: "plans", plans: [...value] } : { mode: "none", plans: [] };
21
- const fromRule = (rule) => rule.mode === "any" ? "any" : rule.mode === "plans" && rule.plans.length ? [...rule.plans] : null;
22
- const dialog = ref(false);
23
- const editingType = ref(null);
24
- const type = reactive({ key: "", name: void 0, description: void 0, price: 0, capacity: "", requires: toRule(null), excludes: toRule(null) });
25
- function open(ticketType) {
26
- editingType.value = ticketType ?? null;
27
- Object.assign(type, {
28
- key: ticketType?.key ?? "",
29
- name: ticketType?.name,
30
- description: ticketType?.description ?? void 0,
31
- price: ticketType ? fromCents(ticketType.price, ticketType.currency) : 0,
32
- capacity: ticketType?.capacity ?? "",
33
- requires: toRule(ticketType?.requiresMembership),
34
- excludes: toRule(ticketType?.excludesMembership)
35
- });
36
- dialog.value = true;
37
- }
38
- defineExpose({ open });
39
- const save = () => run(async () => {
40
- const body = {
41
- name: type.name,
42
- description: type.description ?? null,
43
- // Prices are stored in minor units (øre/cents).
44
- price: toCents(Number(type.price), editingType.value?.currency ?? DEFAULT_CURRENCY),
45
- capacity: type.capacity === "" ? null : Number(type.capacity),
46
- ...props.membershipEnabled ? { requiresMembership: fromRule(type.requires), excludesMembership: fromRule(type.excludes) } : {}
47
- };
48
- if (editingType.value) {
49
- await $fetch(`/api/events/admin/ticket-types/${editingType.value.id}`, { method: "PATCH", body });
50
- } else {
51
- await $fetch(`/api/events/admin/events/${props.eventId}/ticket-types`, { method: "POST", body: { key: type.key, ...body } });
52
- }
53
- dialog.value = false;
54
- emit("refresh");
55
- }, { success: t("admin.typeSaved", "Ticket type saved.") });
56
- </script>
57
-
58
- <template>
59
- <UModal
60
- v-model:open="dialog"
61
- :title="editingType ? t('admin.editType', 'Edit ticket type') : t('admin.addType', 'Add ticket type')"
62
- >
63
- <template #body>
64
- <form
65
- id="admin-ticket-type-form"
66
- class="flex flex-col gap-4"
67
- @submit.prevent="save"
68
- >
69
- <UFormField :label="t('admin.name', 'Name')">
70
- <TranslatedInput
71
- v-model="type.name"
72
- required
73
- />
74
- </UFormField>
75
- <UFormField :label="t('admin.description', 'Description')">
76
- <TranslatedInput
77
- v-model="type.description"
78
- multiline
79
- mdc
80
- :mdc-data="ticketMdcData({ ...type, price: toCents(Number(type.price), editingType?.currency ?? DEFAULT_CURRENCY), currency: editingType?.currency ?? DEFAULT_CURRENCY }, tr(type.name), locale)"
81
- :rows="2"
82
- />
83
- </UFormField>
84
- <UFormField
85
- v-if="!editingType"
86
- :label="t('admin.key', 'Key')"
87
- :description="t('admin.keyHelp', 'A short unique id, e.g. member.')"
88
- >
89
- <UInput
90
- v-model="type.key"
91
- class="w-full"
92
- required
93
- />
94
- </UFormField>
95
- <UFormField :label="t('admin.price', 'Price')">
96
- <UInput
97
- v-model="type.price"
98
- type="number"
99
- min="0"
100
- step="0.01"
101
- class="w-full"
102
- />
103
- </UFormField>
104
- <UFormField
105
- :label="t('admin.capacity', 'Capacity')"
106
- :description="t('admin.capacityHelp', 'Leave empty for unlimited.')"
107
- >
108
- <UInput
109
- v-model="type.capacity"
110
- type="number"
111
- min="0"
112
- class="w-full"
113
- />
114
- </UFormField>
115
-
116
- <template v-if="membershipEnabled">
117
- <UFormField
118
- :label="t('admin.requiresMembership', 'Only for members of')"
119
- :description="t('admin.requiresHelp', 'Visitors without a matching active membership cannot buy this ticket.')"
120
- >
121
- <USelect
122
- v-model="type.requires.mode"
123
- :items="modes"
124
- class="w-full"
125
- />
126
- <USelectMenu
127
- v-if="type.requires.mode === 'plans'"
128
- v-model="type.requires.plans"
129
- :items="plans"
130
- value-key="value"
131
- multiple
132
- class="mt-2 w-full"
133
- />
134
- </UFormField>
135
- <UFormField
136
- :label="t('admin.excludesMembership', 'Not for members of')"
137
- :description="t('admin.excludesHelp', 'Holders of these memberships cannot buy this ticket, so members and non-members can have separate prices.')"
138
- >
139
- <USelect
140
- v-model="type.excludes.mode"
141
- :items="modes"
142
- class="w-full"
143
- />
144
- <USelectMenu
145
- v-if="type.excludes.mode === 'plans'"
146
- v-model="type.excludes.plans"
147
- :items="plans"
148
- value-key="value"
149
- multiple
150
- class="mt-2 w-full"
151
- />
152
- </UFormField>
153
- </template>
154
- </form>
155
- </template>
156
- <template #footer>
157
- <div class="flex w-full justify-end gap-2">
158
- <UButton
159
- variant="outline"
160
- color="neutral"
161
- :label="t('admin.cancel', 'Cancel')"
162
- @click="dialog = false"
163
- />
164
- <UButton
165
- type="submit"
166
- form="admin-ticket-type-form"
167
- :loading="busy"
168
- :disabled="!tr(type.name)"
169
- :label="t('admin.save', 'Save')"
170
- />
171
- </div>
172
- </template>
173
- </UModal>
174
- </template>
@@ -1,20 +0,0 @@
1
- import type { StaffTicketType } from '../utils/admin-event.js';
2
- type __VLS_Props = {
3
- eventId: string;
4
- /** Membership rules are only offered when @frigear-nu/subscriptions is installed. */
5
- membershipEnabled: boolean;
6
- plans: Array<{
7
- label: string;
8
- value: string;
9
- }>;
10
- };
11
- declare function open(ticketType?: StaffTicketType): void;
12
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
13
- open: typeof open;
14
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
15
- refresh: () => any;
16
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
17
- onRefresh?: (() => any) | undefined;
18
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
- declare const _default: typeof __VLS_export;
20
- export default _default;
@@ -1,10 +0,0 @@
1
- import { db, schema } from "@nuxthub/db";
2
- import { and, eq } from "drizzle-orm";
3
- export default defineEventHandler(async (event) => {
4
- const user = await requireUser(event, { scopes: ["tickets:write"] });
5
- const [ticket] = await db.select().from(schema.eventTickets).where(and(eq(schema.eventTickets.id, getRouterParam(event, "id")), eq(schema.eventTickets.userId, user.id))).limit(1);
6
- if (!ticket) throw createError({ statusCode: 404, statusMessage: "Ticket not found." });
7
- if (ticket.status === "paid") throw createError({ statusCode: 409, statusMessage: "This ticket is already paid." });
8
- if (ticket.status !== "pending" || !ticket.paymentId) throw createError({ statusCode: 409, statusMessage: "This ticket can no longer be paid. Buy a new one." });
9
- return resumePayment(ticket.paymentId, user.id, event);
10
- });
@@ -1,4 +0,0 @@
1
- export default defineEventHandler(async (event) => {
2
- const user = await requireUser(event, { scopes: ["tickets:read"] });
3
- return getUserTicket(user.id, getRouterParam(event, "id"));
4
- });