@frigear-nu/events 0.0.8 → 0.0.9
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.json +1 -1
- package/dist/module.mjs +8 -1
- package/dist/runtime/app/widgets/UpcomingEvents.d.vue.ts +3 -0
- package/dist/runtime/app/widgets/UpcomingEvents.vue +39 -0
- package/dist/runtime/app/widgets/UpcomingEvents.vue.d.ts +3 -0
- package/dist/runtime/app/widgets/UpcomingEventsStat.d.vue.ts +3 -0
- package/dist/runtime/app/widgets/UpcomingEventsStat.vue +17 -0
- package/dist/runtime/app/widgets/UpcomingEventsStat.vue.d.ts +3 -0
- package/dist/runtime/i18n/lang/da.json +6 -0
- package/dist/runtime/i18n/lang/en.json +6 -0
- package/dist/runtime/server/mcp/money.d.ts +24 -0
- package/dist/runtime/server/mcp/money.js +42 -0
- package/dist/runtime/server/mcp/schemas.js +4 -3
- package/dist/runtime/server/mcp/tools/admin/create-event.js +3 -2
- package/dist/runtime/server/mcp/tools/admin/get-event-definition.js +3 -2
- package/dist/runtime/server/mcp/tools/admin/list-attendees.js +3 -2
- package/dist/runtime/server/mcp/tools/admin/update-ticket-type.js +9 -2
- package/dist/runtime/server/mcp/tools/get-event.js +3 -2
- package/dist/runtime/server/mcp/tools/my-tickets.js +3 -2
- package/dist/runtime/server/routes/admin/upcoming.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/upcoming.get.js +17 -0
- package/package.json +14 -14
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, hasNuxtModule, addServerImportsDir, addServerPlugin, addImports, addComponentsDir, addServerHandler } from '@nuxt/kit';
|
|
2
|
-
import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, registerDatabaseTable, addModulePages, addOptionalMenuSource } from '@frigear-nu/module-kit/utils';
|
|
2
|
+
import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, registerDatabaseTable, 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';
|
|
@@ -66,6 +66,7 @@ const module$1 = defineNuxtModule({
|
|
|
66
66
|
route("get", "/:slug", "public/event.get");
|
|
67
67
|
route("post", "/:slug/purchase", "public/purchase.post");
|
|
68
68
|
route("get", "/admin/membership-options", "admin/membership-options.get");
|
|
69
|
+
route("get", "/admin/upcoming", "admin/upcoming.get");
|
|
69
70
|
route("get", "/admin/events", "admin/events.get");
|
|
70
71
|
route("get", "/admin/events/:id", "admin/event.get");
|
|
71
72
|
route("post", "/admin/events", "admin/events.post");
|
|
@@ -85,6 +86,7 @@ const module$1 = defineNuxtModule({
|
|
|
85
86
|
route("post", "/admin/tickets/:id/cancel", "admin/ticket-cancel.post");
|
|
86
87
|
route("post", "/admin/tickets/:id/refund", "admin/ticket-refund.post");
|
|
87
88
|
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
89
|
+
inlineModuleRuntime(nuxt, resolver);
|
|
88
90
|
if (settings.mcp === true) addMcpDefinitions(nuxt, resolver.resolve("./runtime/server/mcp"));
|
|
89
91
|
addServerPlugin(resolver.resolve("./runtime/server/plugins/payment-hooks"));
|
|
90
92
|
addServerPlugin(resolver.resolve("./runtime/server/plugins/staff-scopes"));
|
|
@@ -117,6 +119,11 @@ const module$1 = defineNuxtModule({
|
|
|
117
119
|
{ id: "attendees", component: resolver.resolve("./runtime/app/tabs/Attendees.vue"), order: 20, label: "Attendees", labelKey: "events.admin.tabs.attendees", icon: "i-lucide-users" }
|
|
118
120
|
]);
|
|
119
121
|
}
|
|
122
|
+
if (adminPath && hasNuxtModule("@frigear-nu/widget")) {
|
|
123
|
+
const { extendWidgets } = await import('@frigear-nu/widget/utils');
|
|
124
|
+
extendWidgets(nuxt, "admin.dashboard.top", { id: "events-upcoming-stat", component: resolver.resolve("./runtime/app/widgets/UpcomingEventsStat.vue"), order: 20 });
|
|
125
|
+
extendWidgets(nuxt, "admin.dashboard.side", { id: "events-upcoming", component: resolver.resolve("./runtime/app/widgets/UpcomingEvents.vue"), order: 10 });
|
|
126
|
+
}
|
|
120
127
|
addOptionalMenuSource(nuxt, { file: resolver.resolve("./runtime/menus"), menu: "dashboard.account" });
|
|
121
128
|
}
|
|
122
129
|
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineOptions({ name: "EventsUpcomingList" });
|
|
3
|
+
const t = useModuleT("events");
|
|
4
|
+
const locale = useEventsLocale();
|
|
5
|
+
const tr = useEventsTranslate();
|
|
6
|
+
const pages = useRuntimeConfig().public.events?.pages;
|
|
7
|
+
const { data, status } = useLazyFetch("/api/events/admin/upcoming", { key: "admin-events-upcoming" });
|
|
8
|
+
const eventTo = (id) => (pages?.adminEvent || "/admin/events/:id").replace(":id", encodeURIComponent(id));
|
|
9
|
+
const when = (value) => new Intl.DateTimeFormat(locale.value, { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<AdminListCard
|
|
14
|
+
:title="t('admin.upcoming.title', 'Upcoming events')"
|
|
15
|
+
:loading="status === 'pending' && !data"
|
|
16
|
+
:is-empty="!data?.items.length"
|
|
17
|
+
:empty="t('admin.upcoming.empty', 'No upcoming events.')"
|
|
18
|
+
:to="pages?.admin"
|
|
19
|
+
:to-label="t('admin.upcoming.all', 'All events')"
|
|
20
|
+
>
|
|
21
|
+
<li
|
|
22
|
+
v-for="item in data?.items"
|
|
23
|
+
:key="item.id"
|
|
24
|
+
class="min-w-0"
|
|
25
|
+
>
|
|
26
|
+
<NuxtLink
|
|
27
|
+
:to="eventTo(item.id)"
|
|
28
|
+
class="text-highlighted block truncate font-medium hover:underline"
|
|
29
|
+
>
|
|
30
|
+
{{ tr(item.name) }}
|
|
31
|
+
</NuxtLink>
|
|
32
|
+
<p class="text-muted truncate text-xs">
|
|
33
|
+
{{ when(item.startsAt) }}<template v-if="item.location">
|
|
34
|
+
· {{ tr(item.location) }}
|
|
35
|
+
</template>
|
|
36
|
+
</p>
|
|
37
|
+
</li>
|
|
38
|
+
</AdminListCard>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineOptions({ name: "EventsUpcomingStat" });
|
|
3
|
+
const t = useModuleT("events");
|
|
4
|
+
const pages = useRuntimeConfig().public.events?.pages;
|
|
5
|
+
const { data, status } = useLazyFetch("/api/events/admin/upcoming", { key: "admin-events-upcoming" });
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<AdminStatCard
|
|
10
|
+
:label="t('admin.upcoming.stat', 'Upcoming events')"
|
|
11
|
+
icon="i-lucide-calendar-days"
|
|
12
|
+
:to="pages?.admin"
|
|
13
|
+
:loading="status === 'pending' && !data"
|
|
14
|
+
>
|
|
15
|
+
<span v-if="data">{{ data.total }}</span>
|
|
16
|
+
</AdminStatCard>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -114,6 +114,12 @@
|
|
|
114
114
|
"details": "Detaljer",
|
|
115
115
|
"tickets": "Billetter",
|
|
116
116
|
"attendees": "Deltagere"
|
|
117
|
+
},
|
|
118
|
+
"upcoming": {
|
|
119
|
+
"stat": "Kommende arrangementer",
|
|
120
|
+
"title": "Kommende arrangementer",
|
|
121
|
+
"empty": "Ingen kommende arrangementer.",
|
|
122
|
+
"all": "Alle arrangementer"
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
}
|
|
@@ -114,6 +114,12 @@
|
|
|
114
114
|
"details": "Details",
|
|
115
115
|
"tickets": "Tickets",
|
|
116
116
|
"attendees": "Attendees"
|
|
117
|
+
},
|
|
118
|
+
"upcoming": {
|
|
119
|
+
"stat": "Upcoming events",
|
|
120
|
+
"title": "Upcoming events",
|
|
121
|
+
"empty": "No upcoming events.",
|
|
122
|
+
"all": "All events"
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type Priced = {
|
|
2
|
+
price?: number | null;
|
|
3
|
+
};
|
|
4
|
+
type TypeLike = Priced & {
|
|
5
|
+
currency?: string | null;
|
|
6
|
+
addonGroups?: Array<{
|
|
7
|
+
addons?: Priced[];
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
/** Output: a ticket type (DTO or row) with its price and add-on prices in whole units, currency upper-cased. */
|
|
11
|
+
export declare function ticketTypeToMajor<T extends TypeLike>(type: T): T;
|
|
12
|
+
/** Output: an event with `ticketTypes` converted by `ticketTypeToMajor`. */
|
|
13
|
+
export declare function eventToMajor<T extends {
|
|
14
|
+
ticketTypes: TypeLike[];
|
|
15
|
+
}>(event: T): T;
|
|
16
|
+
/** Output: a ticket (or attendee) with `amount` (and add-on prices) in whole units of its own currency. */
|
|
17
|
+
export declare function ticketToMajor<T extends {
|
|
18
|
+
amount: number;
|
|
19
|
+
currency: string;
|
|
20
|
+
addons?: Array<Priced & Record<string, unknown>>;
|
|
21
|
+
}>(ticket: T): T;
|
|
22
|
+
/** Input: a ticket type payload (agent, whole units) to minor units, using `fallbackCurrency` when it names none. */
|
|
23
|
+
export declare function ticketTypeToMinor<T extends TypeLike>(type: T, fallbackCurrency?: string): T;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { fromCents, toCents } from "@frigear-nu/mcp/utils";
|
|
2
|
+
import { DEFAULT_CURRENCY } from "../../utils/index.js";
|
|
3
|
+
const currencyOf = (type) => (type.currency ?? DEFAULT_CURRENCY).toUpperCase();
|
|
4
|
+
export function ticketTypeToMajor(type) {
|
|
5
|
+
const currency = currencyOf(type);
|
|
6
|
+
return {
|
|
7
|
+
...type,
|
|
8
|
+
...typeof type.price === "number" ? { price: fromCents(type.price, currency) } : {},
|
|
9
|
+
...type.currency ? { currency } : {},
|
|
10
|
+
...type.addonGroups ? {
|
|
11
|
+
addonGroups: type.addonGroups.map((group) => ({
|
|
12
|
+
...group,
|
|
13
|
+
...group.addons ? { addons: group.addons.map((addon) => typeof addon.price === "number" ? { ...addon, price: fromCents(addon.price, currency) } : addon) } : {}
|
|
14
|
+
}))
|
|
15
|
+
} : {}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function eventToMajor(event) {
|
|
19
|
+
return { ...event, ticketTypes: event.ticketTypes.map(ticketTypeToMajor) };
|
|
20
|
+
}
|
|
21
|
+
export function ticketToMajor(ticket) {
|
|
22
|
+
const currency = ticket.currency.toUpperCase();
|
|
23
|
+
return {
|
|
24
|
+
...ticket,
|
|
25
|
+
currency,
|
|
26
|
+
amount: fromCents(ticket.amount, currency),
|
|
27
|
+
...Array.isArray(ticket.addons) ? { addons: ticket.addons.map((addon) => typeof addon.price === "number" ? { ...addon, price: fromCents(addon.price, currency) } : addon) } : {}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function ticketTypeToMinor(type, fallbackCurrency = DEFAULT_CURRENCY) {
|
|
31
|
+
const currency = (type.currency ?? fallbackCurrency).toUpperCase();
|
|
32
|
+
return {
|
|
33
|
+
...type,
|
|
34
|
+
...typeof type.price === "number" ? { price: toCents(type.price, currency) } : {},
|
|
35
|
+
...type.addonGroups ? {
|
|
36
|
+
addonGroups: type.addonGroups.map((group) => ({
|
|
37
|
+
...group,
|
|
38
|
+
...group.addons ? { addons: group.addons.map((addon) => typeof addon.price === "number" ? { ...addon, price: toCents(addon.price, currency) } : addon) } : {}
|
|
39
|
+
}))
|
|
40
|
+
} : {}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { MONEY_NOTICE } from "@frigear-nu/mcp/utils";
|
|
2
3
|
const text = z.union([z.string().min(1), z.record(z.string(), z.string())]).describe('Text in one language, or a locale -> text map such as {"en": "Party", "da": "Fest"}.');
|
|
3
4
|
const membership = z.union([z.literal("any"), z.array(z.string())]).nullable().describe('"any", a list of plan keys, or null for no rule.');
|
|
4
5
|
const isoDate = z.string().describe("ISO 8601 date-time, e.g. 2026-12-24T18:00:00Z.");
|
|
5
6
|
const addon = z.object({
|
|
6
7
|
label: text,
|
|
7
8
|
description: text.nullish(),
|
|
8
|
-
price: z.number().
|
|
9
|
+
price: z.number().min(0).optional().describe(`${MONEY_NOTICE} In the ticket type's currency.`),
|
|
9
10
|
capacity: z.number().int().min(0).nullish(),
|
|
10
11
|
sortOrder: z.number().int().optional()
|
|
11
12
|
});
|
|
@@ -20,8 +21,8 @@ const ticketType = z.object({
|
|
|
20
21
|
key: z.string().min(1).max(64),
|
|
21
22
|
name: text,
|
|
22
23
|
description: text.nullish(),
|
|
23
|
-
price: z.number().
|
|
24
|
-
currency: z.string().length(3).optional(),
|
|
24
|
+
price: z.number().min(0).optional().describe(MONEY_NOTICE),
|
|
25
|
+
currency: z.string().length(3).optional().describe("ISO 4217 code such as DKK or USD. The price is in this currency; defaults to DKK for new ticket types."),
|
|
25
26
|
hidePrice: z.boolean().optional(),
|
|
26
27
|
capacity: z.number().int().min(0).nullish(),
|
|
27
28
|
saleStartsAt: isoDate.nullish(),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createEventWithCatalog } from "../../../utils/admin.js";
|
|
2
2
|
import { eventInput } from "../../../utils/admin-schemas.js";
|
|
3
3
|
import { createEventShape } from "../../schemas.js";
|
|
4
|
+
import { ticketTypeToMinor } from "../../money.js";
|
|
4
5
|
export default defineScopedMcpTool({
|
|
5
|
-
description: "Create an event, optionally with ticket types, addon groups and addons (staff).",
|
|
6
|
+
description: "Create an event, optionally with ticket types, addon groups and addons (staff). Prices are in whole currency units (52.2 = 52.20), not cents.",
|
|
6
7
|
scopes: ["events:manage"],
|
|
7
8
|
gate: canManageEvents,
|
|
8
9
|
annotations: { destructiveHint: false },
|
|
9
10
|
inputSchema: createEventShape,
|
|
10
|
-
handler: (args) => createEventWithCatalog(eventInput.parse(args))
|
|
11
|
+
handler: (args) => createEventWithCatalog(eventInput.parse({ ...args, ticketTypes: args.ticketTypes?.map((type) => ticketTypeToMinor(type)) }))
|
|
11
12
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { getEventWithTicketTypes } from "../../../utils/admin.js";
|
|
3
|
+
import { eventToMajor } from "../../money.js";
|
|
3
4
|
export default defineScopedMcpTool({
|
|
4
|
-
description: "Get one event with all its ticket types and their ids, whatever its status (staff). Use the ids with update-ticket-type.",
|
|
5
|
+
description: "Get one event with all its ticket types and their ids, whatever its status (staff). Use the ids with update-ticket-type. Prices are in whole currency units (52.2 = 52.20), not cents.",
|
|
5
6
|
scopes: ["events:manage"],
|
|
6
7
|
gate: canManageEvents,
|
|
7
8
|
annotations: { readOnlyHint: true },
|
|
8
9
|
inputSchema: { id: z.string().describe("The event id.") },
|
|
9
|
-
handler: ({ id }) => getEventWithTicketTypes(id)
|
|
10
|
+
handler: async ({ id }) => eventToMajor(await getEventWithTicketTypes(id))
|
|
10
11
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { db, schema } from "@nuxthub/db";
|
|
3
3
|
import { asc, eq } from "drizzle-orm";
|
|
4
|
+
import { ticketToMajor } from "../../money.js";
|
|
4
5
|
export default defineScopedMcpTool({
|
|
5
|
-
description: "List the tickets of an event (staff). Attendee names and emails are only included with the events:pii scope.",
|
|
6
|
+
description: "List the tickets of an event (staff). Attendee names and emails are only included with the events:pii scope. Amounts are in whole currency units (52.2 = 52.20), not cents.",
|
|
6
7
|
scopes: ["events:manage"],
|
|
7
8
|
gate: canManageEvents,
|
|
8
9
|
annotations: { readOnlyHint: true },
|
|
@@ -15,7 +16,7 @@ export default defineScopedMcpTool({
|
|
|
15
16
|
}).from(schema.eventTickets).leftJoin(schema.users, eq(schema.eventTickets.userId, schema.users.id)).where(eq(schema.eventTickets.eventId, eventId)).orderBy(asc(schema.eventTickets.createdAt));
|
|
16
17
|
const allow = ctx.hasScope("events:pii");
|
|
17
18
|
const attendees = rows.map(({ ticket, userName, userEmail }) => redactPii({
|
|
18
|
-
...pick(ticket, ["id", "ticketTypeId", "status", "amount", "currency", "checkedInAt", "createdAt"]),
|
|
19
|
+
...ticketToMajor(pick(ticket, ["id", "ticketTypeId", "status", "amount", "currency", "checkedInAt", "createdAt"])),
|
|
19
20
|
addons: ticket.addons.length,
|
|
20
21
|
userName,
|
|
21
22
|
userEmail
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { db, schema } from "@nuxthub/db";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
1
3
|
import { patchTicketTypeRow, ticketTypePatchSchema } from "../../../utils/admin.js";
|
|
4
|
+
import { ticketTypeToMajor, ticketTypeToMinor } from "../../money.js";
|
|
2
5
|
import { updateTicketTypeShape } from "../../schemas.js";
|
|
3
6
|
export default defineScopedMcpTool({
|
|
4
|
-
description: "Update fields of an event's ticket type, such as price, capacity or sale dates (staff). Only the given fields change.",
|
|
7
|
+
description: "Update fields of an event's ticket type, such as price, capacity or sale dates (staff). Only the given fields change. The price is in whole currency units (52.2 = 52.20), not cents, in the given or the ticket type's current currency.",
|
|
5
8
|
scopes: ["events:manage"],
|
|
6
9
|
gate: canManageEvents,
|
|
7
10
|
annotations: { idempotentHint: true },
|
|
8
11
|
inputSchema: updateTicketTypeShape,
|
|
9
|
-
handler: ({ id, patch }) =>
|
|
12
|
+
handler: async ({ id, patch }) => {
|
|
13
|
+
const [existing] = patch.price === void 0 || patch.currency ? [void 0] : await db.select({ currency: schema.eventTicketTypes.currency }).from(schema.eventTicketTypes).where(eq(schema.eventTicketTypes.id, id)).limit(1);
|
|
14
|
+
const row = await patchTicketTypeRow(id, ticketTypePatchSchema.parse(ticketTypeToMinor(patch, existing?.currency)));
|
|
15
|
+
return ticketTypeToMajor(row);
|
|
16
|
+
}
|
|
10
17
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { eventToMajor } from "../money.js";
|
|
2
3
|
export default defineScopedMcpTool({
|
|
3
|
-
description: "Get a published event with its ticket types and what you can buy.",
|
|
4
|
+
description: "Get a published event with its ticket types and what you can buy. Prices are in whole currency units (52.2 = 52.20), not cents.",
|
|
4
5
|
scopes: ["tickets:read"],
|
|
5
6
|
annotations: { readOnlyHint: true },
|
|
6
7
|
inputSchema: { slug: z.string().describe("The event slug.") },
|
|
7
8
|
handler: async ({ slug }, { user }) => {
|
|
8
9
|
const row = await getPublishedEventRow(slug);
|
|
9
10
|
if (!row) throw createError({ statusCode: 404, statusMessage: "Event not found." });
|
|
10
|
-
return getEventDetail(row, user.id);
|
|
11
|
+
return eventToMajor(await getEventDetail(row, user.id));
|
|
11
12
|
}
|
|
12
13
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { ticketToMajor } from "../money.js";
|
|
1
2
|
export default defineScopedMcpTool({
|
|
2
|
-
description: "List your own tickets.",
|
|
3
|
+
description: "List your own tickets. Amounts and prices are in whole currency units (52.2 = 52.20), not cents.",
|
|
3
4
|
scopes: ["tickets:read"],
|
|
4
5
|
annotations: { readOnlyHint: true },
|
|
5
|
-
handler: (_args, { user }) => listUserTickets(user.id)
|
|
6
|
+
handler: async (_args, { user }) => (await listUserTickets(user.id)).map(ticketToMajor)
|
|
6
7
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { db, schema } from "@nuxthub/db";
|
|
2
|
+
import { and, asc, count, eq, gte } from "drizzle-orm";
|
|
3
|
+
const LIMIT = 5;
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
await requireEventsManager(event);
|
|
6
|
+
const upcoming = and(eq(schema.events.status, "published"), gte(schema.events.startsAt, /* @__PURE__ */ new Date()));
|
|
7
|
+
const [[total], items] = await Promise.all([
|
|
8
|
+
db.select({ value: count() }).from(schema.events).where(upcoming),
|
|
9
|
+
db.select({
|
|
10
|
+
id: schema.events.id,
|
|
11
|
+
name: schema.events.name,
|
|
12
|
+
location: schema.events.location,
|
|
13
|
+
startsAt: schema.events.startsAt
|
|
14
|
+
}).from(schema.events).where(upcoming).orderBy(asc(schema.events.startsAt)).limit(LIMIT)
|
|
15
|
+
]);
|
|
16
|
+
return { total: total?.value ?? 0, items };
|
|
17
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frigear-nu/events",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
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": {
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"playground"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@frigear-nu/admin": "0.0.
|
|
34
|
-
"@frigear-nu/module-kit": "0.
|
|
35
|
-
"@frigear-nu/payments": "0.0.
|
|
36
|
-
"@frigear-nu/stripe": "0.0.
|
|
37
|
-
"@frigear-nu/subscriptions": "0.0.
|
|
38
|
-
"@frigear-nu/ui": "0.0.
|
|
39
|
-
"@frigear-nu/auth": "0.0.
|
|
40
|
-
"@frigear-nu/mcp": "0.0.
|
|
41
|
-
"@frigear-nu/users": "1.0.
|
|
33
|
+
"@frigear-nu/admin": "0.0.3",
|
|
34
|
+
"@frigear-nu/module-kit": "1.0.0",
|
|
35
|
+
"@frigear-nu/payments": "0.0.9",
|
|
36
|
+
"@frigear-nu/stripe": "0.0.9",
|
|
37
|
+
"@frigear-nu/subscriptions": "0.0.9",
|
|
38
|
+
"@frigear-nu/ui": "0.0.8",
|
|
39
|
+
"@frigear-nu/auth": "0.0.4",
|
|
40
|
+
"@frigear-nu/mcp": "0.0.4",
|
|
41
|
+
"@frigear-nu/users": "1.0.10",
|
|
42
42
|
"@libsql/client": "^0.18.0",
|
|
43
43
|
"@nuxt/kit": "^4.6.0",
|
|
44
44
|
"@vueuse/core": "^15.0.0",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"zod": "^4.6.5"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@frigear-nu/menu": "0.0.
|
|
55
|
-
"@frigear-nu/widget": "0.0.
|
|
54
|
+
"@frigear-nu/menu": "0.0.10",
|
|
55
|
+
"@frigear-nu/widget": "0.0.3",
|
|
56
56
|
"@nuxt/ui": "^4.10.0",
|
|
57
57
|
"@nuxthub/core": "^0.10.8"
|
|
58
58
|
},
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@frigear-nu/menu": "0.0.
|
|
72
|
-
"@frigear-nu/widget": "0.0.
|
|
71
|
+
"@frigear-nu/menu": "0.0.10",
|
|
72
|
+
"@frigear-nu/widget": "0.0.3",
|
|
73
73
|
"@nuxt/devtools": "^3.4.2",
|
|
74
74
|
"@nuxt/eslint-config": "^1.17.0",
|
|
75
75
|
"@nuxt/module-builder": "^1.0.3",
|