@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.
- package/dist/module.d.mts +105 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +40 -31
- package/dist/runtime/app/components/EventAddonGroupSlideover.d.vue.ts +18 -0
- package/dist/runtime/app/components/EventAddonGroupSlideover.vue +66 -0
- package/dist/runtime/app/components/EventAddonGroupSlideover.vue.d.ts +18 -0
- package/dist/runtime/app/components/EventAddonSlideover.d.vue.ts +20 -0
- package/dist/runtime/app/components/EventAddonSlideover.vue +103 -0
- package/dist/runtime/app/components/EventAddonSlideover.vue.d.ts +20 -0
- package/dist/runtime/app/components/EventAdminAddonGroups.d.vue.ts +1 -9
- package/dist/runtime/app/components/EventAdminAddonGroups.vue +6 -48
- package/dist/runtime/app/components/EventAdminAddonGroups.vue.d.ts +1 -9
- package/dist/runtime/app/components/EventFormSlideover.vue +45 -23
- package/dist/runtime/app/components/EventTicketCard.vue +59 -62
- package/dist/runtime/app/components/EventTicketTypeSlideover.d.vue.ts +20 -0
- package/dist/runtime/app/components/EventTicketTypeSlideover.vue +176 -0
- package/dist/runtime/app/components/EventTicketTypeSlideover.vue.d.ts +20 -0
- package/dist/runtime/app/composables/index.d.ts +2 -0
- package/dist/runtime/app/composables/index.js +1 -0
- package/dist/runtime/app/composables/useEventActions.d.ts +3 -3
- package/dist/runtime/app/composables/useEventActions.js +9 -4
- package/dist/runtime/app/composables/useEventCatalogActions.d.ts +33 -0
- package/dist/runtime/app/composables/useEventCatalogActions.js +107 -0
- package/dist/runtime/app/composables/useEvents.d.ts +7 -6
- package/dist/runtime/app/composables/useEvents.js +17 -10
- package/dist/runtime/app/pages/admin-event.vue +71 -136
- package/dist/runtime/app/pages/admin.vue +91 -29
- package/dist/runtime/app/pages/event.vue +8 -4
- package/dist/runtime/app/pages/tickets.vue +24 -2
- package/dist/runtime/app/tabs/Attendees.vue +78 -39
- package/dist/runtime/app/tabs/Details.d.vue.ts +1 -1
- package/dist/runtime/app/tabs/Details.vue +112 -30
- package/dist/runtime/app/tabs/Details.vue.d.ts +1 -1
- package/dist/runtime/app/tabs/Tickets.vue +105 -132
- package/dist/runtime/app/utils/admin-event.d.ts +53 -38
- package/dist/runtime/app/utils/admin-event.js +14 -0
- package/dist/runtime/app/widgets/UpcomingEvents.vue +2 -1
- package/dist/runtime/app/widgets/UpcomingEventsStat.vue +2 -1
- package/dist/runtime/i18n/lang/da.json +24 -3
- package/dist/runtime/i18n/lang/en.json +24 -3
- package/dist/runtime/server/bootstrap/my-tickets.d.ts +7 -0
- package/dist/runtime/server/{routes/public/tickets-me.get.js → bootstrap/my-tickets.js} +2 -2
- package/dist/runtime/server/routes/admin/addon-group.delete.js +6 -3
- package/dist/runtime/server/routes/admin/addon-group.patch.js +6 -4
- package/dist/runtime/server/routes/admin/addon-groups.post.js +8 -6
- package/dist/runtime/server/routes/admin/addon.delete.js +6 -3
- package/dist/runtime/server/routes/admin/addon.patch.js +8 -5
- package/dist/runtime/server/routes/admin/addons.post.js +8 -6
- package/dist/runtime/server/routes/admin/attendees.get.js +38 -8
- package/dist/runtime/server/routes/{public → admin}/check-in.post.js +4 -3
- package/dist/runtime/server/routes/admin/event-clone.post.js +37 -19
- package/dist/runtime/server/routes/admin/event.delete.js +7 -4
- package/dist/runtime/server/routes/admin/event.get.js +4 -2
- package/dist/runtime/server/routes/admin/event.patch.js +4 -2
- package/dist/runtime/server/routes/admin/events.get.js +36 -3
- package/dist/runtime/server/routes/admin/events.post.js +3 -4
- package/dist/runtime/server/routes/admin/membership-options.get.js +3 -2
- package/dist/runtime/server/routes/admin/ticket-cancel.post.js +7 -8
- package/dist/runtime/server/routes/admin/ticket-refund.post.js +7 -5
- package/dist/runtime/server/routes/admin/ticket-type.delete.js +7 -4
- package/dist/runtime/server/routes/admin/ticket-type.patch.js +4 -2
- package/dist/runtime/server/routes/admin/ticket-types.post.js +10 -7
- package/dist/runtime/server/routes/admin/upcoming.get.js +7 -3
- package/dist/runtime/server/routes/me/ticket-pay.post.js +13 -0
- package/dist/runtime/server/routes/me/ticket.get.js +6 -0
- package/dist/runtime/server/routes/me/tickets.get.js +2 -0
- package/dist/runtime/server/routes/public/event.get.js +5 -2
- package/dist/runtime/server/routes/public/purchase.post.js +3 -1
- package/dist/runtime/server/utils/admin-dto.d.ts +30 -0
- package/dist/runtime/server/utils/admin-dto.js +101 -0
- package/dist/runtime/server/utils/admin-schemas.js +2 -1
- package/dist/runtime/server/utils/admin.d.ts +3 -1
- package/dist/runtime/server/utils/admin.js +20 -6
- package/dist/runtime/server/utils/auth.d.ts +5 -4
- package/dist/runtime/server/utils/auth.js +6 -7
- package/dist/runtime/server/utils/tickets.js +6 -11
- package/dist/runtime/utils/api.d.ts +48 -0
- package/dist/runtime/utils/api.js +51 -0
- package/dist/runtime/utils/index.d.ts +2 -1
- package/dist/runtime/utils/index.js +1 -0
- package/dist/types.d.mts +1 -1
- package/package.json +21 -16
- package/dist/runtime/app/components/EventAdminAddonDialog.d.vue.ts +0 -11
- package/dist/runtime/app/components/EventAdminAddonDialog.vue +0 -112
- package/dist/runtime/app/components/EventAdminAddonDialog.vue.d.ts +0 -11
- package/dist/runtime/app/components/EventAdminAddonGroupDialog.d.vue.ts +0 -11
- package/dist/runtime/app/components/EventAdminAddonGroupDialog.vue +0 -78
- package/dist/runtime/app/components/EventAdminAddonGroupDialog.vue.d.ts +0 -11
- package/dist/runtime/app/components/EventAdminTicketTypeDialog.d.vue.ts +0 -20
- package/dist/runtime/app/components/EventAdminTicketTypeDialog.vue +0 -174
- package/dist/runtime/app/components/EventAdminTicketTypeDialog.vue.d.ts +0 -20
- package/dist/runtime/server/routes/public/ticket-pay.post.js +0 -10
- package/dist/runtime/server/routes/public/ticket.get.js +0 -4
- /package/dist/runtime/server/routes/{public → admin}/check-in.post.d.ts +0 -0
- /package/dist/runtime/server/routes/{public → me}/ticket-pay.post.d.ts +0 -0
- /package/dist/runtime/server/routes/{public → me}/ticket.get.d.ts +0 -0
- /package/dist/runtime/server/routes/{public/tickets-me.get.d.ts → me/tickets.get.d.ts} +0 -0
package/dist/module.d.mts
CHANGED
|
@@ -2,6 +2,7 @@ import * as _nuxt_schema from '@nuxt/schema';
|
|
|
2
2
|
import { H3Event } from 'h3';
|
|
3
3
|
import { Translated } from '@frigear-nu/ui/translated';
|
|
4
4
|
export { Translated } from '@frigear-nu/ui/translated';
|
|
5
|
+
import { Schedule } from '@frigear-nu/module-kit/schedule';
|
|
5
6
|
|
|
6
7
|
/** `null` = open to everyone, `'any'` = any active subscription, or a list of plan keys. */
|
|
7
8
|
type MembershipRequirement = 'any' | string[] | null;
|
|
@@ -133,6 +134,109 @@ type CheckInResponse = {
|
|
|
133
134
|
checkedInAt: string;
|
|
134
135
|
ticket: TicketDto;
|
|
135
136
|
};
|
|
137
|
+
/** An event as staff see it (`/api/admin/events`): every field, whatever its status. Dates are ISO strings. */
|
|
138
|
+
type AdminEventDto = {
|
|
139
|
+
id: string;
|
|
140
|
+
slug: string;
|
|
141
|
+
name: Translated;
|
|
142
|
+
description: Translated | null;
|
|
143
|
+
body: Translated | null;
|
|
144
|
+
image: string | null;
|
|
145
|
+
location: Translated | null;
|
|
146
|
+
locationUrl: string | null;
|
|
147
|
+
startsAt: string;
|
|
148
|
+
endsAt: string | null;
|
|
149
|
+
status: EventStatus;
|
|
150
|
+
requiresMembership: MembershipRequirement;
|
|
151
|
+
createdAt: string;
|
|
152
|
+
updatedAt: string;
|
|
153
|
+
};
|
|
154
|
+
/** A ticket type as staff see it, with its capacity, sale window and membership rules. */
|
|
155
|
+
type AdminTicketTypeDto = {
|
|
156
|
+
id: string;
|
|
157
|
+
eventId: string;
|
|
158
|
+
key: string;
|
|
159
|
+
name: Translated;
|
|
160
|
+
description: Translated | null;
|
|
161
|
+
price: number;
|
|
162
|
+
currency: string;
|
|
163
|
+
hidePrice: boolean;
|
|
164
|
+
capacity: number | null;
|
|
165
|
+
saleStartsAt: string | null;
|
|
166
|
+
saleEndsAt: string | null;
|
|
167
|
+
saleSchedule: Schedule | null;
|
|
168
|
+
maxPerUser: number;
|
|
169
|
+
requiresMembership: MembershipRequirement;
|
|
170
|
+
excludesMembership: MembershipRequirement;
|
|
171
|
+
isDefault: boolean;
|
|
172
|
+
sortOrder: number;
|
|
173
|
+
createdAt: string;
|
|
174
|
+
updatedAt: string;
|
|
175
|
+
};
|
|
176
|
+
/** An add-on group of a ticket type, as staff see it. */
|
|
177
|
+
type AdminAddonGroupDto = {
|
|
178
|
+
id: string;
|
|
179
|
+
ticketTypeId: string;
|
|
180
|
+
title: Translated;
|
|
181
|
+
required: boolean;
|
|
182
|
+
multiple: boolean;
|
|
183
|
+
sortOrder: number;
|
|
184
|
+
};
|
|
185
|
+
/** An add-on (one choice of a group), as staff see it. */
|
|
186
|
+
type AdminAddonDto = {
|
|
187
|
+
id: string;
|
|
188
|
+
groupId: string;
|
|
189
|
+
label: Translated;
|
|
190
|
+
description: Translated | null;
|
|
191
|
+
price: number;
|
|
192
|
+
capacity: number | null;
|
|
193
|
+
sortOrder: number;
|
|
194
|
+
};
|
|
195
|
+
/** One event with its whole catalogue (`GET /api/admin/events/:id`). */
|
|
196
|
+
type AdminEventDetailDto = AdminEventDto & {
|
|
197
|
+
ticketTypes: Array<AdminTicketTypeDto & {
|
|
198
|
+
addonGroups: Array<AdminAddonGroupDto & {
|
|
199
|
+
addons: AdminAddonDto[];
|
|
200
|
+
}>;
|
|
201
|
+
}>;
|
|
202
|
+
};
|
|
203
|
+
/** One ticket of an event with its holder (`GET /api/admin/events/:id/tickets`). `code` is there for staff check-in. */
|
|
204
|
+
type AttendeeDto = {
|
|
205
|
+
id: string;
|
|
206
|
+
eventId: string;
|
|
207
|
+
ticketTypeId: string;
|
|
208
|
+
userId: number;
|
|
209
|
+
status: TicketStatus;
|
|
210
|
+
addons: Array<{
|
|
211
|
+
id: string;
|
|
212
|
+
label: Translated;
|
|
213
|
+
price: number;
|
|
214
|
+
}>;
|
|
215
|
+
amount: number;
|
|
216
|
+
currency: string;
|
|
217
|
+
paymentId: string | null;
|
|
218
|
+
code: string;
|
|
219
|
+
locale: string | null;
|
|
220
|
+
checkedInAt: string | null;
|
|
221
|
+
createdAt: string;
|
|
222
|
+
userName: string | null;
|
|
223
|
+
userEmail: string | null;
|
|
224
|
+
};
|
|
225
|
+
/** The admin dashboard's upcoming events (`GET /api/admin/events/upcoming`). */
|
|
226
|
+
type UpcomingEventsDto = {
|
|
227
|
+
total: number;
|
|
228
|
+
items: Array<{
|
|
229
|
+
id: string;
|
|
230
|
+
name: Translated;
|
|
231
|
+
location: Translated | null;
|
|
232
|
+
startsAt: string;
|
|
233
|
+
}>;
|
|
234
|
+
};
|
|
235
|
+
/** What a membership provider offers (`GET /api/admin/events/membership-options`). */
|
|
236
|
+
type MembershipOptionsDto = {
|
|
237
|
+
available: boolean;
|
|
238
|
+
options: MembershipOption[];
|
|
239
|
+
};
|
|
136
240
|
type PurchaseBeforeContext = {
|
|
137
241
|
event: H3Event;
|
|
138
242
|
userId: number;
|
|
@@ -203,4 +307,4 @@ declare module 'nitropack' {
|
|
|
203
307
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
204
308
|
|
|
205
309
|
export { _default as default };
|
|
206
|
-
export type { AddonDto, AddonGroupDto, AuthorizeContext, CheckInResponse, EventDetailDto, EventStatus, EventSummaryDto, EventsRuntimeConfig, MembershipCheckContext, MembershipOption, MembershipOptionsContext, MembershipRequirement, ModuleOptions, PublicModuleOptions, PurchaseBeforeContext, PurchaseBody, PurchaseResponse, TicketDto, TicketHookContext, TicketStatus, TicketTypeDto };
|
|
310
|
+
export type { AddonDto, AddonGroupDto, AdminAddonDto, AdminAddonGroupDto, AdminEventDetailDto, AdminEventDto, AdminTicketTypeDto, AttendeeDto, AuthorizeContext, CheckInResponse, EventDetailDto, EventStatus, EventSummaryDto, EventsRuntimeConfig, MembershipCheckContext, MembershipOption, MembershipOptionsContext, MembershipOptionsDto, MembershipRequirement, ModuleOptions, PublicModuleOptions, PurchaseBeforeContext, PurchaseBody, PurchaseResponse, TicketDto, TicketHookContext, TicketStatus, TicketTypeDto, UpcomingEventsDto };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, hasNuxtModule, addServerImportsDir, addServerPlugin, addImports, addComponentsDir
|
|
2
|
-
import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, registerDatabaseTable, inlineModuleRuntime, addModulePages, addOptionalMenuSource } from '@frigear-nu/module-kit/utils';
|
|
1
|
+
import { defineNuxtModule, createResolver, hasNuxtModule, addServerImportsDir, addServerPlugin, addImports, addComponentsDir } from '@nuxt/kit';
|
|
2
|
+
import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, registerDatabaseTable, addModuleRoutes, registerBootstrapProvider, inlineModuleRuntime, addModulePages, addOptionalMenuSource } from '@frigear-nu/module-kit/utils';
|
|
3
3
|
import { adminRoute } from '@frigear-nu/admin/utils';
|
|
4
4
|
import { addAuthScope } from '@frigear-nu/auth/utils';
|
|
5
5
|
import { addMcpDefinitions } from '@frigear-nu/mcp/utils';
|
|
@@ -57,34 +57,42 @@ const module$1 = defineNuxtModule({
|
|
|
57
57
|
resolver,
|
|
58
58
|
schema: (dialect) => `./runtime/server/db/schema.${dialect}`
|
|
59
59
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
60
|
+
addModuleRoutes(resolver, [
|
|
61
|
+
{ method: "get", path: "", file: "public/events.get" },
|
|
62
|
+
{ method: "get", path: "me/tickets", file: "me/tickets.get" },
|
|
63
|
+
{ method: "get", path: "me/tickets/:id", file: "me/ticket.get" },
|
|
64
|
+
{ method: "post", path: "me/tickets/:id/pay", file: "me/ticket-pay.post" },
|
|
65
|
+
{ method: "get", path: ":slug", file: "public/event.get" },
|
|
66
|
+
{ method: "post", path: ":slug/purchase", file: "public/purchase.post" }
|
|
67
|
+
], { base: "/api/events" });
|
|
68
|
+
registerBootstrapProvider(nuxt, {
|
|
69
|
+
scope: "account",
|
|
70
|
+
key: "events:tickets:mine",
|
|
71
|
+
handler: resolver.resolve("./runtime/server/bootstrap/my-tickets")
|
|
72
|
+
});
|
|
73
|
+
addModuleRoutes(resolver, [
|
|
74
|
+
{ method: "get", path: "", file: "admin/events.get" },
|
|
75
|
+
{ method: "post", path: "", file: "admin/events.post" },
|
|
76
|
+
{ method: "get", path: "membership-options", file: "admin/membership-options.get" },
|
|
77
|
+
{ method: "get", path: "upcoming", file: "admin/upcoming.get" },
|
|
78
|
+
{ method: "post", path: "check-in", file: "admin/check-in.post" },
|
|
79
|
+
{ method: "get", path: ":id", file: "admin/event.get" },
|
|
80
|
+
{ method: "patch", path: ":id", file: "admin/event.patch" },
|
|
81
|
+
{ method: "delete", path: ":id", file: "admin/event.delete" },
|
|
82
|
+
{ method: "post", path: ":id/clone", file: "admin/event-clone.post" },
|
|
83
|
+
{ method: "get", path: ":id/tickets", file: "admin/attendees.get" },
|
|
84
|
+
{ method: "post", path: ":id/ticket-types", file: "admin/ticket-types.post" },
|
|
85
|
+
{ method: "patch", path: "ticket-types/:id", file: "admin/ticket-type.patch" },
|
|
86
|
+
{ method: "delete", path: "ticket-types/:id", file: "admin/ticket-type.delete" },
|
|
87
|
+
{ method: "post", path: "ticket-types/:id/addon-groups", file: "admin/addon-groups.post" },
|
|
88
|
+
{ method: "patch", path: "addon-groups/:id", file: "admin/addon-group.patch" },
|
|
89
|
+
{ method: "delete", path: "addon-groups/:id", file: "admin/addon-group.delete" },
|
|
90
|
+
{ method: "post", path: "addon-groups/:id/addons", file: "admin/addons.post" },
|
|
91
|
+
{ method: "patch", path: "addons/:id", file: "admin/addon.patch" },
|
|
92
|
+
{ method: "delete", path: "addons/:id", file: "admin/addon.delete" },
|
|
93
|
+
{ method: "post", path: "tickets/:id/cancel", file: "admin/ticket-cancel.post" },
|
|
94
|
+
{ method: "post", path: "tickets/:id/refund", file: "admin/ticket-refund.post" }
|
|
95
|
+
], { base: "/api/admin/events" });
|
|
88
96
|
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
89
97
|
inlineModuleRuntime(nuxt, resolver);
|
|
90
98
|
if (settings.mcp === true) addMcpDefinitions(nuxt, resolver.resolve("./runtime/server/mcp"));
|
|
@@ -103,7 +111,8 @@ const module$1 = defineNuxtModule({
|
|
|
103
111
|
{ name: "useEventsFallbackLocale", from: resolver.resolve("./runtime/app/composables/useEvents") },
|
|
104
112
|
{ name: "useEventsTranslate", from: resolver.resolve("./runtime/app/composables/useEvents") },
|
|
105
113
|
{ name: "useEventLinks", from: resolver.resolve("./runtime/app/composables/useEventLinks") },
|
|
106
|
-
{ name: "useEventActions", from: resolver.resolve("./runtime/app/composables/useEventActions") }
|
|
114
|
+
{ name: "useEventActions", from: resolver.resolve("./runtime/app/composables/useEventActions") },
|
|
115
|
+
{ name: "useEventCatalogActions", from: resolver.resolve("./runtime/app/composables/useEventCatalogActions") }
|
|
107
116
|
]);
|
|
108
117
|
addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
|
|
109
118
|
addModulePages(MODULE_NAME, resolver, [
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { StaffAddonGroup } from '../utils/admin-event.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** The ticket type a new group is added to. */
|
|
4
|
+
ticketTypeId: string;
|
|
5
|
+
/** The group to edit; leave out to add one. */
|
|
6
|
+
group?: StaffAddonGroup;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
close: (saved: false | {
|
|
10
|
+
id: string;
|
|
11
|
+
}) => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
+
onClose?: ((saved: false | {
|
|
14
|
+
id: string;
|
|
15
|
+
}) => any) | undefined;
|
|
16
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { eventsApi } from "../../utils/api";
|
|
4
|
+
defineOptions({ name: "EventAddonGroupSlideover" });
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
ticketTypeId: { type: String, required: true },
|
|
7
|
+
group: { type: Object, required: false }
|
|
8
|
+
});
|
|
9
|
+
const emit = defineEmits(["close"]);
|
|
10
|
+
const t = useModuleT("events");
|
|
11
|
+
const tr = useEventsTranslate();
|
|
12
|
+
const { busy, run } = useAction();
|
|
13
|
+
const schema = z.object({
|
|
14
|
+
title: z.custom().refine((value) => !!tr(value).trim(), t("admin.groupTitleRequired", "Enter a title.")),
|
|
15
|
+
required: z.boolean(),
|
|
16
|
+
multiple: z.boolean()
|
|
17
|
+
});
|
|
18
|
+
const state = reactive({
|
|
19
|
+
title: props.group?.title,
|
|
20
|
+
required: props.group?.required ?? false,
|
|
21
|
+
multiple: props.group?.multiple ?? false
|
|
22
|
+
});
|
|
23
|
+
async function save(event) {
|
|
24
|
+
const body = event.data;
|
|
25
|
+
const saved = await run(
|
|
26
|
+
() => props.group ? $fetch(eventsApi.admin.addonGroup(props.group.id), { method: "PATCH", body }) : $fetch(eventsApi.admin.addonGroups(props.ticketTypeId), { method: "POST", body }),
|
|
27
|
+
{ success: t("admin.groupSaved", "Add-on group saved.") }
|
|
28
|
+
);
|
|
29
|
+
if (saved) emit("close", saved);
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<FormSlideover
|
|
35
|
+
form-id="event-addon-group-form"
|
|
36
|
+
:title="group ? t('admin.editGroup', 'Edit add-on group') : t('admin.addGroup', 'Add add-on group')"
|
|
37
|
+
:submit-label="group ? t('admin.saveGroup', 'Save add-on group') : t('admin.addGroup', 'Add add-on group')"
|
|
38
|
+
:loading="busy"
|
|
39
|
+
@close="emit('close', false)"
|
|
40
|
+
>
|
|
41
|
+
<UForm
|
|
42
|
+
id="event-addon-group-form"
|
|
43
|
+
:schema="schema"
|
|
44
|
+
:state="state"
|
|
45
|
+
class="flex flex-col gap-4"
|
|
46
|
+
@submit="save"
|
|
47
|
+
>
|
|
48
|
+
<UFormField
|
|
49
|
+
name="title"
|
|
50
|
+
:label="t('admin.groupTitle', 'Title')"
|
|
51
|
+
:description="t('admin.groupTitleHelp', 'Shown above the choices, e.g. Food.')"
|
|
52
|
+
required
|
|
53
|
+
>
|
|
54
|
+
<TranslatedInput v-model="state.title" />
|
|
55
|
+
</UFormField>
|
|
56
|
+
<UCheckbox
|
|
57
|
+
v-model="state.required"
|
|
58
|
+
:label="t('admin.groupRequiredLabel', 'Buyers must choose at least one')"
|
|
59
|
+
/>
|
|
60
|
+
<UCheckbox
|
|
61
|
+
v-model="state.multiple"
|
|
62
|
+
:label="t('admin.groupMultipleLabel', 'Buyers can choose several')"
|
|
63
|
+
/>
|
|
64
|
+
</UForm>
|
|
65
|
+
</FormSlideover>
|
|
66
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { StaffAddonGroup } from '../utils/admin-event.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** The ticket type a new group is added to. */
|
|
4
|
+
ticketTypeId: string;
|
|
5
|
+
/** The group to edit; leave out to add one. */
|
|
6
|
+
group?: StaffAddonGroup;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
close: (saved: false | {
|
|
10
|
+
id: string;
|
|
11
|
+
}) => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
+
onClose?: ((saved: false | {
|
|
14
|
+
id: string;
|
|
15
|
+
}) => any) | undefined;
|
|
16
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StaffAddon } from '../utils/admin-event.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** The add-on group a new choice is added to. */
|
|
4
|
+
groupId: string;
|
|
5
|
+
/** The currency of the ticket type the group belongs to. */
|
|
6
|
+
currency: string;
|
|
7
|
+
/** The choice to edit; leave out to add one. */
|
|
8
|
+
addon?: StaffAddon;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
|
+
close: (saved: false | {
|
|
12
|
+
id: string;
|
|
13
|
+
}) => any;
|
|
14
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
+
onClose?: ((saved: false | {
|
|
16
|
+
id: string;
|
|
17
|
+
}) => any) | undefined;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { fromCents, toCents } from "@frigear-nu/ui/money";
|
|
4
|
+
import { eventsApi } from "../../utils/api";
|
|
5
|
+
defineOptions({ name: "EventAddonSlideover" });
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
groupId: { type: String, required: true },
|
|
8
|
+
currency: { type: String, required: true },
|
|
9
|
+
addon: { type: Object, required: false }
|
|
10
|
+
});
|
|
11
|
+
const emit = defineEmits(["close"]);
|
|
12
|
+
const t = useModuleT("events");
|
|
13
|
+
const tr = useEventsTranslate();
|
|
14
|
+
const { busy, run } = useAction();
|
|
15
|
+
const schema = z.object({
|
|
16
|
+
label: z.custom().refine((value) => !!tr(value).trim(), t("admin.nameRequired", "Enter a name.")),
|
|
17
|
+
description: z.custom().optional(),
|
|
18
|
+
price: z.coerce.number().min(0, t("admin.priceInvalid", "Enter a price of 0 or more.")),
|
|
19
|
+
capacity: z.union([z.literal(""), z.null(), z.undefined(), z.coerce.number().int(t("admin.countInvalid", "Enter a whole number of 0 or more.")).min(0, t("admin.countInvalid", "Enter a whole number of 0 or more."))]).transform((value) => typeof value === "number" ? value : null)
|
|
20
|
+
});
|
|
21
|
+
const state = reactive({
|
|
22
|
+
label: props.addon?.label,
|
|
23
|
+
description: props.addon?.description ?? void 0,
|
|
24
|
+
price: props.addon ? fromCents(props.addon.price, props.currency) : 0,
|
|
25
|
+
capacity: props.addon?.capacity ?? ""
|
|
26
|
+
});
|
|
27
|
+
async function save(event) {
|
|
28
|
+
const data = event.data;
|
|
29
|
+
const body = {
|
|
30
|
+
label: data.label,
|
|
31
|
+
description: data.description ?? null,
|
|
32
|
+
// Prices are stored in minor units (øre/cents).
|
|
33
|
+
price: toCents(data.price, props.currency) ?? 0,
|
|
34
|
+
capacity: data.capacity
|
|
35
|
+
};
|
|
36
|
+
const saved = await run(
|
|
37
|
+
() => props.addon ? $fetch(eventsApi.admin.addon(props.addon.id), { method: "PATCH", body }) : $fetch(eventsApi.admin.addons(props.groupId), { method: "POST", body }),
|
|
38
|
+
{ success: t("admin.addonSaved", "Add-on saved.") }
|
|
39
|
+
);
|
|
40
|
+
if (saved) emit("close", saved);
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<FormSlideover
|
|
46
|
+
form-id="event-addon-form"
|
|
47
|
+
:title="addon ? t('admin.editAddon', 'Edit choice') : t('admin.addAddon', 'Add choice')"
|
|
48
|
+
:submit-label="addon ? t('admin.saveAddon', 'Save choice') : t('admin.addAddon', 'Add choice')"
|
|
49
|
+
:loading="busy"
|
|
50
|
+
@close="emit('close', false)"
|
|
51
|
+
>
|
|
52
|
+
<UForm
|
|
53
|
+
id="event-addon-form"
|
|
54
|
+
:schema="schema"
|
|
55
|
+
:state="state"
|
|
56
|
+
class="flex flex-col gap-4"
|
|
57
|
+
@submit="save"
|
|
58
|
+
>
|
|
59
|
+
<UFormField
|
|
60
|
+
name="label"
|
|
61
|
+
:label="t('admin.name', 'Name')"
|
|
62
|
+
required
|
|
63
|
+
>
|
|
64
|
+
<TranslatedInput v-model="state.label" />
|
|
65
|
+
</UFormField>
|
|
66
|
+
<UFormField
|
|
67
|
+
name="description"
|
|
68
|
+
:label="t('admin.description', 'Description')"
|
|
69
|
+
>
|
|
70
|
+
<TranslatedInput
|
|
71
|
+
v-model="state.description"
|
|
72
|
+
multiline
|
|
73
|
+
:rows="2"
|
|
74
|
+
/>
|
|
75
|
+
</UFormField>
|
|
76
|
+
<UFormField
|
|
77
|
+
name="price"
|
|
78
|
+
:label="t('admin.price', 'Price')"
|
|
79
|
+
:description="t('admin.addonPriceHelp', 'Added to the ticket price. 0 means included.')"
|
|
80
|
+
>
|
|
81
|
+
<UInput
|
|
82
|
+
v-model="state.price"
|
|
83
|
+
type="number"
|
|
84
|
+
min="0"
|
|
85
|
+
step="0.01"
|
|
86
|
+
class="w-full"
|
|
87
|
+
/>
|
|
88
|
+
</UFormField>
|
|
89
|
+
<UFormField
|
|
90
|
+
name="capacity"
|
|
91
|
+
:label="t('admin.capacity', 'Capacity')"
|
|
92
|
+
:description="t('admin.capacityHelp', 'Leave empty for unlimited.')"
|
|
93
|
+
>
|
|
94
|
+
<UInput
|
|
95
|
+
v-model="state.capacity"
|
|
96
|
+
type="number"
|
|
97
|
+
min="0"
|
|
98
|
+
class="w-full"
|
|
99
|
+
/>
|
|
100
|
+
</UFormField>
|
|
101
|
+
</UForm>
|
|
102
|
+
</FormSlideover>
|
|
103
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StaffAddon } from '../utils/admin-event.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** The add-on group a new choice is added to. */
|
|
4
|
+
groupId: string;
|
|
5
|
+
/** The currency of the ticket type the group belongs to. */
|
|
6
|
+
currency: string;
|
|
7
|
+
/** The choice to edit; leave out to add one. */
|
|
8
|
+
addon?: StaffAddon;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
|
+
close: (saved: false | {
|
|
12
|
+
id: string;
|
|
13
|
+
}) => any;
|
|
14
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
+
onClose?: ((saved: false | {
|
|
16
|
+
id: string;
|
|
17
|
+
}) => any) | undefined;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StaffTicketType } from '../utils/admin-event.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
ticketType: StaffTicketType;
|
|
4
4
|
};
|
|
5
5
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
6
|
refresh: () => any;
|
|
7
|
-
addGroup: () => any;
|
|
8
|
-
editGroup: (group: StaffAddonGroup) => any;
|
|
9
|
-
addAddon: (group: StaffAddonGroup) => any;
|
|
10
|
-
editAddon: (group: StaffAddonGroup, addon: StaffAddon) => any;
|
|
11
7
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
8
|
onRefresh?: (() => any) | undefined;
|
|
13
|
-
onAddGroup?: (() => any) | undefined;
|
|
14
|
-
onEditGroup?: ((group: StaffAddonGroup) => any) | undefined;
|
|
15
|
-
onAddAddon?: ((group: StaffAddonGroup) => any) | undefined;
|
|
16
|
-
onEditAddon?: ((group: StaffAddonGroup, addon: StaffAddon) => any) | undefined;
|
|
17
9
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
10
|
declare const _default: typeof __VLS_export;
|
|
19
11
|
export default _default;
|
|
@@ -3,53 +3,11 @@ defineOptions({ name: "EventAdminAddonGroups" });
|
|
|
3
3
|
defineProps({
|
|
4
4
|
ticketType: { type: Object, required: true }
|
|
5
5
|
});
|
|
6
|
-
const emit = defineEmits(["refresh"
|
|
6
|
+
const emit = defineEmits(["refresh"]);
|
|
7
7
|
const t = useModuleT("events");
|
|
8
8
|
const format = useFormat();
|
|
9
9
|
const tr = useEventsTranslate();
|
|
10
|
-
const
|
|
11
|
-
const { run } = useAction();
|
|
12
|
-
const removeGroup = async (addonGroup) => {
|
|
13
|
-
const title = tr(addonGroup.title);
|
|
14
|
-
if (!await confirmAction({
|
|
15
|
-
title: t("admin.confirmRemoveGroup", "Delete {name}?", { name: title }),
|
|
16
|
-
description: t("admin.confirmRemoveGroupDescription", "Its choices are deleted too. Tickets already bought keep the choices they made."),
|
|
17
|
-
confirmText: t("admin.removeGroup", "Delete add-on group"),
|
|
18
|
-
destructive: true
|
|
19
|
-
})) return;
|
|
20
|
-
return run(async () => {
|
|
21
|
-
await $fetch(`/api/events/admin/addon-groups/${addonGroup.id}`, { method: "DELETE" });
|
|
22
|
-
emit("refresh");
|
|
23
|
-
}, { success: t("admin.groupDeleted", "Deleted {name}", { name: title }) });
|
|
24
|
-
};
|
|
25
|
-
const removeAddon = async (existing) => {
|
|
26
|
-
const label = tr(existing.label);
|
|
27
|
-
if (!await confirmAction({
|
|
28
|
-
title: t("admin.confirmRemoveAddon", "Delete {name}?", { name: label }),
|
|
29
|
-
description: t("admin.confirmRemoveAddonDescription", "Tickets already bought keep the choice they made."),
|
|
30
|
-
confirmText: t("admin.removeAddon", "Delete choice"),
|
|
31
|
-
destructive: true
|
|
32
|
-
})) return;
|
|
33
|
-
return run(async () => {
|
|
34
|
-
await $fetch(`/api/events/admin/addons/${existing.id}`, { method: "DELETE" });
|
|
35
|
-
emit("refresh");
|
|
36
|
-
}, { success: t("admin.addonDeleted", "Deleted {name}", { name: label }) });
|
|
37
|
-
};
|
|
38
|
-
const groupMenu = (addonGroup) => [
|
|
39
|
-
[
|
|
40
|
-
{ label: t("admin.addAddon", "Add choice"), icon: "i-lucide-plus", onSelect: () => emit("addAddon", addonGroup) },
|
|
41
|
-
{ label: t("admin.editGroup", "Edit add-on group"), icon: "i-lucide-pencil", onSelect: () => emit("editGroup", addonGroup) }
|
|
42
|
-
],
|
|
43
|
-
[{ label: t("admin.removeGroup", "Delete add-on group"), icon: "i-lucide-trash-2", color: "error", onSelect: () => {
|
|
44
|
-
void removeGroup(addonGroup);
|
|
45
|
-
} }]
|
|
46
|
-
];
|
|
47
|
-
const addonMenu = (addonGroup, item) => [
|
|
48
|
-
[{ label: t("admin.editAddon", "Edit choice"), icon: "i-lucide-pencil", onSelect: () => emit("editAddon", addonGroup, item) }],
|
|
49
|
-
[{ label: t("admin.removeAddon", "Delete choice"), icon: "i-lucide-trash-2", color: "error", onSelect: () => {
|
|
50
|
-
void removeAddon(item);
|
|
51
|
-
} }]
|
|
52
|
-
];
|
|
10
|
+
const catalog = useEventCatalogActions({ onChanged: () => emit("refresh") });
|
|
53
11
|
</script>
|
|
54
12
|
|
|
55
13
|
<template>
|
|
@@ -68,7 +26,7 @@ const addonMenu = (addonGroup, item) => [
|
|
|
68
26
|
</span>
|
|
69
27
|
</div>
|
|
70
28
|
<RowMenu
|
|
71
|
-
:items="groupMenu(addonGroup)"
|
|
29
|
+
:items="catalog.groupMenu(ticketType, addonGroup)"
|
|
72
30
|
:subject="tr(addonGroup.title)"
|
|
73
31
|
size="xs"
|
|
74
32
|
/>
|
|
@@ -84,10 +42,10 @@ const addonMenu = (addonGroup, item) => [
|
|
|
84
42
|
>
|
|
85
43
|
<div>
|
|
86
44
|
{{ tr(item.label) }}
|
|
87
|
-
<span class="text-muted">· {{ item.price === 0 ? t("admin.included", "Included") : `+ ${format.money(item.price, ticketType.currency)}` }}<template v-if="item.capacity !== null"> · {{ t("admin.capacity", "Capacity").toLowerCase() }} {{ item.capacity }}</template></span>
|
|
45
|
+
<span class="text-muted">· {{ item.price === 0 ? t("admin.included", "Included") : `+ ${format.money(item.price, ticketType.currency)}` }}<template v-if="item.capacity !== null"> · {{ t("admin.capacity", "Capacity").toLowerCase() }} <span class="tabular-nums">{{ item.capacity }}</span></template></span>
|
|
88
46
|
</div>
|
|
89
47
|
<RowMenu
|
|
90
|
-
:items="addonMenu(addonGroup, item)"
|
|
48
|
+
:items="catalog.addonMenu(ticketType, addonGroup, item)"
|
|
91
49
|
:subject="tr(item.label)"
|
|
92
50
|
size="xs"
|
|
93
51
|
/>
|
|
@@ -107,7 +65,7 @@ const addonMenu = (addonGroup, item) => [
|
|
|
107
65
|
variant="outline"
|
|
108
66
|
size="xs"
|
|
109
67
|
:label="t('admin.addGroup', 'Add add-on group')"
|
|
110
|
-
@click="
|
|
68
|
+
@click="catalog.openGroup(ticketType)"
|
|
111
69
|
/>
|
|
112
70
|
</div>
|
|
113
71
|
</div>
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StaffTicketType } from '../utils/admin-event.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
ticketType: StaffTicketType;
|
|
4
4
|
};
|
|
5
5
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
6
|
refresh: () => any;
|
|
7
|
-
addGroup: () => any;
|
|
8
|
-
editGroup: (group: StaffAddonGroup) => any;
|
|
9
|
-
addAddon: (group: StaffAddonGroup) => any;
|
|
10
|
-
editAddon: (group: StaffAddonGroup, addon: StaffAddon) => any;
|
|
11
7
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
8
|
onRefresh?: (() => any) | undefined;
|
|
13
|
-
onAddGroup?: (() => any) | undefined;
|
|
14
|
-
onEditGroup?: ((group: StaffAddonGroup) => any) | undefined;
|
|
15
|
-
onAddAddon?: ((group: StaffAddonGroup) => any) | undefined;
|
|
16
|
-
onEditAddon?: ((group: StaffAddonGroup, addon: StaffAddon) => any) | undefined;
|
|
17
9
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
10
|
declare const _default: typeof __VLS_export;
|
|
19
11
|
export default _default;
|