@frigear-nu/events 0.1.1 → 0.1.3

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 (97) hide show
  1. package/dist/module.d.mts +105 -1
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +40 -31
  4. package/dist/runtime/app/components/EventAddonGroupSlideover.d.vue.ts +18 -0
  5. package/dist/runtime/app/components/EventAddonGroupSlideover.vue +66 -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 +103 -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 +6 -48
  12. package/dist/runtime/app/components/EventAdminAddonGroups.vue.d.ts +1 -9
  13. package/dist/runtime/app/components/EventFormSlideover.vue +45 -23
  14. package/dist/runtime/app/components/EventTicketCard.vue +59 -62
  15. package/dist/runtime/app/components/EventTicketTypeSlideover.d.vue.ts +20 -0
  16. package/dist/runtime/app/components/EventTicketTypeSlideover.vue +176 -0
  17. package/dist/runtime/app/components/EventTicketTypeSlideover.vue.d.ts +20 -0
  18. package/dist/runtime/app/composables/index.d.ts +2 -0
  19. package/dist/runtime/app/composables/index.js +1 -0
  20. package/dist/runtime/app/composables/useEventActions.d.ts +3 -3
  21. package/dist/runtime/app/composables/useEventActions.js +9 -4
  22. package/dist/runtime/app/composables/useEventCatalogActions.d.ts +33 -0
  23. package/dist/runtime/app/composables/useEventCatalogActions.js +107 -0
  24. package/dist/runtime/app/composables/useEvents.d.ts +7 -6
  25. package/dist/runtime/app/composables/useEvents.js +17 -10
  26. package/dist/runtime/app/pages/admin-event.vue +71 -136
  27. package/dist/runtime/app/pages/admin.vue +91 -29
  28. package/dist/runtime/app/pages/event.vue +8 -4
  29. package/dist/runtime/app/pages/tickets.vue +24 -2
  30. package/dist/runtime/app/tabs/Attendees.vue +78 -39
  31. package/dist/runtime/app/tabs/Details.d.vue.ts +1 -1
  32. package/dist/runtime/app/tabs/Details.vue +112 -30
  33. package/dist/runtime/app/tabs/Details.vue.d.ts +1 -1
  34. package/dist/runtime/app/tabs/Tickets.vue +105 -132
  35. package/dist/runtime/app/utils/admin-event.d.ts +53 -38
  36. package/dist/runtime/app/utils/admin-event.js +14 -0
  37. package/dist/runtime/app/widgets/UpcomingEvents.vue +2 -1
  38. package/dist/runtime/app/widgets/UpcomingEventsStat.vue +2 -1
  39. package/dist/runtime/i18n/lang/da.json +24 -3
  40. package/dist/runtime/i18n/lang/en.json +24 -3
  41. package/dist/runtime/server/bootstrap/my-tickets.d.ts +7 -0
  42. package/dist/runtime/server/{routes/public/tickets-me.get.js → bootstrap/my-tickets.js} +2 -2
  43. package/dist/runtime/server/routes/admin/addon-group.delete.js +6 -3
  44. package/dist/runtime/server/routes/admin/addon-group.patch.js +6 -4
  45. package/dist/runtime/server/routes/admin/addon-groups.post.js +8 -6
  46. package/dist/runtime/server/routes/admin/addon.delete.js +6 -3
  47. package/dist/runtime/server/routes/admin/addon.patch.js +8 -5
  48. package/dist/runtime/server/routes/admin/addons.post.js +8 -6
  49. package/dist/runtime/server/routes/admin/attendees.get.js +38 -8
  50. package/dist/runtime/server/routes/{public → admin}/check-in.post.js +4 -3
  51. package/dist/runtime/server/routes/admin/event-clone.post.js +37 -19
  52. package/dist/runtime/server/routes/admin/event.delete.js +7 -4
  53. package/dist/runtime/server/routes/admin/event.get.js +4 -2
  54. package/dist/runtime/server/routes/admin/event.patch.js +4 -2
  55. package/dist/runtime/server/routes/admin/events.get.js +36 -3
  56. package/dist/runtime/server/routes/admin/events.post.js +3 -4
  57. package/dist/runtime/server/routes/admin/membership-options.get.js +3 -2
  58. package/dist/runtime/server/routes/admin/ticket-cancel.post.js +7 -8
  59. package/dist/runtime/server/routes/admin/ticket-refund.post.js +7 -5
  60. package/dist/runtime/server/routes/admin/ticket-type.delete.js +7 -4
  61. package/dist/runtime/server/routes/admin/ticket-type.patch.js +4 -2
  62. package/dist/runtime/server/routes/admin/ticket-types.post.js +10 -7
  63. package/dist/runtime/server/routes/admin/upcoming.get.js +7 -3
  64. package/dist/runtime/server/routes/me/ticket-pay.post.js +13 -0
  65. package/dist/runtime/server/routes/me/ticket.get.js +6 -0
  66. package/dist/runtime/server/routes/me/tickets.get.js +2 -0
  67. package/dist/runtime/server/routes/public/event.get.js +5 -2
  68. package/dist/runtime/server/routes/public/purchase.post.js +3 -1
  69. package/dist/runtime/server/utils/admin-dto.d.ts +30 -0
  70. package/dist/runtime/server/utils/admin-dto.js +101 -0
  71. package/dist/runtime/server/utils/admin-schemas.js +2 -1
  72. package/dist/runtime/server/utils/admin.d.ts +3 -1
  73. package/dist/runtime/server/utils/admin.js +20 -6
  74. package/dist/runtime/server/utils/auth.d.ts +5 -4
  75. package/dist/runtime/server/utils/auth.js +6 -7
  76. package/dist/runtime/server/utils/tickets.js +6 -11
  77. package/dist/runtime/utils/api.d.ts +48 -0
  78. package/dist/runtime/utils/api.js +51 -0
  79. package/dist/runtime/utils/index.d.ts +2 -1
  80. package/dist/runtime/utils/index.js +1 -0
  81. package/dist/types.d.mts +1 -1
  82. package/package.json +21 -16
  83. package/dist/runtime/app/components/EventAdminAddonDialog.d.vue.ts +0 -11
  84. package/dist/runtime/app/components/EventAdminAddonDialog.vue +0 -112
  85. package/dist/runtime/app/components/EventAdminAddonDialog.vue.d.ts +0 -11
  86. package/dist/runtime/app/components/EventAdminAddonGroupDialog.d.vue.ts +0 -11
  87. package/dist/runtime/app/components/EventAdminAddonGroupDialog.vue +0 -78
  88. package/dist/runtime/app/components/EventAdminAddonGroupDialog.vue.d.ts +0 -11
  89. package/dist/runtime/app/components/EventAdminTicketTypeDialog.d.vue.ts +0 -20
  90. package/dist/runtime/app/components/EventAdminTicketTypeDialog.vue +0 -174
  91. package/dist/runtime/app/components/EventAdminTicketTypeDialog.vue.d.ts +0 -20
  92. package/dist/runtime/server/routes/public/ticket-pay.post.js +0 -10
  93. package/dist/runtime/server/routes/public/ticket.get.js +0 -4
  94. /package/dist/runtime/server/routes/{public → admin}/check-in.post.d.ts +0 -0
  95. /package/dist/runtime/server/routes/{public → me}/ticket-pay.post.d.ts +0 -0
  96. /package/dist/runtime/server/routes/{public → me}/ticket.get.d.ts +0 -0
  97. /package/dist/runtime/server/routes/{public/tickets-me.get.d.ts → me/tickets.get.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/events",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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.1",
34
- "@frigear-nu/module-kit": "1.1.1",
35
- "@frigear-nu/payments": "0.1.1",
36
- "@frigear-nu/stripe": "0.1.1",
37
- "@frigear-nu/subscriptions": "0.1.1",
38
- "@frigear-nu/ui": "0.1.1",
39
- "@frigear-nu/auth": "0.1.2",
40
- "@frigear-nu/mcp": "0.1.1",
41
- "@frigear-nu/users": "1.1.2",
38
+ "@frigear-nu/admin": "0.1.3",
39
+ "@frigear-nu/module-kit": "1.1.3",
40
+ "@frigear-nu/payments": "0.1.3",
41
+ "@frigear-nu/stripe": "0.1.3",
42
+ "@frigear-nu/subscriptions": "0.1.3",
43
+ "@frigear-nu/ui": "0.1.3",
44
+ "@frigear-nu/auth": "0.1.4",
45
+ "@frigear-nu/mcp": "0.1.3",
46
+ "@frigear-nu/users": "1.1.4",
42
47
  "@libsql/client": "^0.18.0",
43
48
  "@nuxt/kit": "^4.6.1",
44
49
  "@vueuse/core": "^15.0.0",
@@ -51,9 +56,9 @@
51
56
  "zod": "^4.6.5"
52
57
  },
53
58
  "peerDependencies": {
54
- "@frigear-nu/menu": "0.1.1",
55
- "@frigear-nu/widget": "0.1.1",
56
- "@nuxt/ui": "^4.11.2",
59
+ "@frigear-nu/menu": "0.1.3",
60
+ "@frigear-nu/widget": "0.1.3",
61
+ "@nuxt/ui": "^4.12.0",
57
62
  "@nuxthub/core": "^0.10.8"
58
63
  },
59
64
  "peerDependenciesMeta": {
@@ -68,14 +73,14 @@
68
73
  }
69
74
  },
70
75
  "devDependencies": {
71
- "@frigear-nu/menu": "0.1.1",
72
- "@frigear-nu/widget": "0.1.1",
76
+ "@frigear-nu/menu": "0.1.3",
77
+ "@frigear-nu/widget": "0.1.3",
73
78
  "@nuxt/devtools": "^3.4.2",
74
79
  "@nuxt/eslint-config": "^1.17.0",
75
80
  "@nuxt/module-builder": "^1.0.3",
76
81
  "@nuxt/schema": "^4.6.1",
77
82
  "@nuxt/test-utils": "^4.3.2",
78
- "@nuxt/ui": "^4.11.2",
83
+ "@nuxt/ui": "^4.12.0",
79
84
  "@nuxthub/core": "^0.10.8",
80
85
  "@types/node": "^26.6.5",
81
86
  "changelogen": "^0.6.2",
@@ -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
- });