@frigear-nu/events 0.1.1 → 0.1.2
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 +6 -1
- package/dist/runtime/app/composables/useEvents.d.ts +7 -6
- package/dist/runtime/app/composables/useEvents.js +12 -5
- package/dist/runtime/app/pages/tickets.vue +20 -2
- package/dist/runtime/app/tabs/Tickets.vue +14 -4
- package/dist/runtime/i18n/lang/da.json +2 -0
- package/dist/runtime/i18n/lang/en.json +2 -0
- package/dist/runtime/server/bootstrap/my-tickets.d.ts +7 -0
- package/dist/runtime/server/bootstrap/my-tickets.js +4 -0
- package/dist/runtime/server/routes/public/tickets-me.get.js +2 -4
- package/package.json +16 -16
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, inlineModuleRuntime, addModulePages, addOptionalMenuSource } from '@frigear-nu/module-kit/utils';
|
|
2
|
+
import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, registerDatabaseTable, 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';
|
|
@@ -60,6 +60,11 @@ const module$1 = defineNuxtModule({
|
|
|
60
60
|
const route = (method, path, file) => addServerHandler({ route: `/api/events${path}`, method, handler: resolver.resolve(`./runtime/server/routes/${file}`) });
|
|
61
61
|
route("get", "", "public/events.get");
|
|
62
62
|
route("get", "/tickets/me", "public/tickets-me.get");
|
|
63
|
+
registerBootstrapProvider(nuxt, {
|
|
64
|
+
scope: "account",
|
|
65
|
+
key: "events:tickets:mine",
|
|
66
|
+
handler: resolver.resolve("./runtime/server/bootstrap/my-tickets")
|
|
67
|
+
});
|
|
63
68
|
route("get", "/tickets/:id", "public/ticket.get");
|
|
64
69
|
route("post", "/tickets/:id/pay", "public/ticket-pay.post");
|
|
65
70
|
route("post", "/check-in", "public/check-in.post");
|
|
@@ -16,12 +16,13 @@ export declare function useEvent(slug: MaybeRefOrGetter<string>): Promise<{
|
|
|
16
16
|
url?: string;
|
|
17
17
|
}>;
|
|
18
18
|
}>;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The signed-in user's tickets. Awaitable: `await useMyTickets()` in a page renders them on the server; a widget can
|
|
21
|
+
* use it without `await` and show a skeleton while `loading`.
|
|
22
|
+
*
|
|
23
|
+
* `tickets` is `[]` until loaded and after an error: check `loading` and `error` before showing an empty state.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useMyTickets(): any;
|
|
25
26
|
/** Locale for translated texts: `@nuxtjs/i18n`'s when installed, otherwise `en`. */
|
|
26
27
|
export declare function useEventsLocale(): any;
|
|
27
28
|
/** The site's default locale: shown for texts that have no translation in the current locale. */
|
|
@@ -32,19 +32,26 @@ export async function useEvent(slug) {
|
|
|
32
32
|
}
|
|
33
33
|
return { event: data, status, error, refresh, purchase };
|
|
34
34
|
}
|
|
35
|
+
const MY_TICKETS_TTL_MS = 60 * 1e3;
|
|
35
36
|
export function useMyTickets() {
|
|
36
37
|
const { loggedIn } = useUserSession();
|
|
37
|
-
const
|
|
38
|
+
const fetched = useCachedFetch("/api/events/tickets/me", {
|
|
38
39
|
key: "events:tickets:mine",
|
|
39
|
-
|
|
40
|
-
watch: false
|
|
41
|
-
default: () => []
|
|
40
|
+
ttl: MY_TICKETS_TTL_MS,
|
|
41
|
+
watch: false
|
|
42
42
|
});
|
|
43
|
+
const { status, error, loading, refresh, clear } = fetched;
|
|
44
|
+
watch(loggedIn, (value) => {
|
|
45
|
+
if (value) refresh();
|
|
46
|
+
else clear();
|
|
47
|
+
});
|
|
48
|
+
const tickets = computed(() => fetched.data.value ?? []);
|
|
43
49
|
async function pay(ticketId) {
|
|
44
50
|
const { url } = await $fetch(`/api/events/tickets/${ticketId}/pay`, { method: "POST" });
|
|
45
51
|
await navigateTo(url, { external: true });
|
|
46
52
|
}
|
|
47
|
-
|
|
53
|
+
const api = { tickets, status, error, loading, refresh, pay };
|
|
54
|
+
return Object.assign(fetched.then(() => api), api);
|
|
48
55
|
}
|
|
49
56
|
export function useEventsLocale() {
|
|
50
57
|
const nuxtApp = useNuxtApp();
|
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
defineOptions({ name: "EventTicketsPage" });
|
|
3
3
|
const t = useModuleT("events");
|
|
4
|
-
const { tickets, pay } = useMyTickets();
|
|
4
|
+
const { tickets, loading, error, refresh, pay } = await useMyTickets();
|
|
5
5
|
const links = useEventLinks();
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<template>
|
|
9
9
|
<div class="flex flex-col gap-4">
|
|
10
|
+
<div
|
|
11
|
+
v-if="loading"
|
|
12
|
+
class="grid gap-4 md:grid-cols-2"
|
|
13
|
+
aria-busy="true"
|
|
14
|
+
>
|
|
15
|
+
<SectionCard
|
|
16
|
+
v-for="index in 2"
|
|
17
|
+
:key="index"
|
|
18
|
+
loading
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
<UEmpty
|
|
22
|
+
v-else-if="error && !tickets.length"
|
|
23
|
+
icon="i-lucide-circle-alert"
|
|
24
|
+
:title="t('ticketsLoadFailed', 'Your tickets could not be loaded')"
|
|
25
|
+
:description="errorMessage(error, '')"
|
|
26
|
+
:actions="[{ label: t('tryAgain', 'Try again'), icon: 'i-lucide-rotate-ccw', color: 'neutral', variant: 'outline', onClick: () => refresh() }]"
|
|
27
|
+
/>
|
|
10
28
|
<UEmpty
|
|
11
|
-
v-if="!tickets.length"
|
|
29
|
+
v-else-if="!tickets.length"
|
|
12
30
|
icon="i-lucide-ticket"
|
|
13
31
|
:title="t('noTicketsTitle', 'No tickets yet')"
|
|
14
32
|
:description="t('noTickets', 'You do not have any tickets yet.')"
|
|
@@ -19,9 +19,12 @@ const typeColumns = computed(() => [
|
|
|
19
19
|
{ id: "addons", header: t("admin.addonGroups", "Add-ons") },
|
|
20
20
|
{ id: "actions" }
|
|
21
21
|
]);
|
|
22
|
-
const { data: membershipOptions } =
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const { data: membershipOptions, loading: optionsLoading } = useCachedFetch("/api/events/admin/membership-options", {
|
|
23
|
+
key: "events:admin:membership-options",
|
|
24
|
+
ttl: 5 * 6e4
|
|
25
|
+
});
|
|
26
|
+
const membershipEnabled = computed(() => membershipOptions.value?.available === true);
|
|
27
|
+
const plans = computed(() => (membershipOptions.value?.options ?? []).map((option) => ({ label: option.label, value: option.value })));
|
|
25
28
|
const planLabel = (key) => plans.value.find((plan) => plan.value === key)?.label ?? key;
|
|
26
29
|
const describeRule = (value) => value === "any" ? t("admin.ruleAny", "Any membership") : Array.isArray(value) ? value.map(planLabel).join(", ") : "";
|
|
27
30
|
const typeDialog = useTemplateRef("typeDialog");
|
|
@@ -89,7 +92,14 @@ const menu = (ticketType) => [
|
|
|
89
92
|
{{ row.original.capacity ?? "\u2013" }}
|
|
90
93
|
</template>
|
|
91
94
|
<template #access-cell="{ row }">
|
|
92
|
-
<
|
|
95
|
+
<USkeleton
|
|
96
|
+
v-if="optionsLoading && (row.original.requiresMembership || row.original.excludesMembership)"
|
|
97
|
+
class="h-5 w-28"
|
|
98
|
+
/>
|
|
99
|
+
<div
|
|
100
|
+
v-else
|
|
101
|
+
class="flex flex-wrap gap-1"
|
|
102
|
+
>
|
|
93
103
|
<UBadge
|
|
94
104
|
v-if="row.original.requiresMembership"
|
|
95
105
|
variant="subtle"
|
|
@@ -229,6 +229,8 @@
|
|
|
229
229
|
},
|
|
230
230
|
"listDescription": "Find et arrangement og få din billet.",
|
|
231
231
|
"noTicketsTitle": "Ingen billetter endnu",
|
|
232
|
+
"ticketsLoadFailed": "Dine billetter kunne ikke indlæses",
|
|
233
|
+
"tryAgain": "Prøv igen",
|
|
232
234
|
"browseEvents": "Se arrangementer"
|
|
233
235
|
}
|
|
234
236
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { TicketDto } from '../../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The caller's tickets: the body of `GET /api/events/tickets/me` and the account bootstrap's `events:tickets:mine`
|
|
5
|
+
* (the key `useMyTickets()` reads), so both always return the same thing.
|
|
6
|
+
*/
|
|
7
|
+
export default function myTickets(event: H3Event): Promise<TicketDto[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frigear-nu/events",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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.1.
|
|
34
|
-
"@frigear-nu/module-kit": "1.1.
|
|
35
|
-
"@frigear-nu/payments": "0.1.
|
|
36
|
-
"@frigear-nu/stripe": "0.1.
|
|
37
|
-
"@frigear-nu/subscriptions": "0.1.
|
|
38
|
-
"@frigear-nu/ui": "0.1.
|
|
39
|
-
"@frigear-nu/auth": "0.1.
|
|
40
|
-
"@frigear-nu/mcp": "0.1.
|
|
41
|
-
"@frigear-nu/users": "1.1.
|
|
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",
|
|
42
42
|
"@libsql/client": "^0.18.0",
|
|
43
43
|
"@nuxt/kit": "^4.6.1",
|
|
44
44
|
"@vueuse/core": "^15.0.0",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"zod": "^4.6.5"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@frigear-nu/menu": "0.1.
|
|
55
|
-
"@frigear-nu/widget": "0.1.
|
|
56
|
-
"@nuxt/ui": "^4.
|
|
54
|
+
"@frigear-nu/menu": "0.1.2",
|
|
55
|
+
"@frigear-nu/widget": "0.1.2",
|
|
56
|
+
"@nuxt/ui": "^4.12.0",
|
|
57
57
|
"@nuxthub/core": "^0.10.8"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
@@ -68,14 +68,14 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@frigear-nu/menu": "0.1.
|
|
72
|
-
"@frigear-nu/widget": "0.1.
|
|
71
|
+
"@frigear-nu/menu": "0.1.2",
|
|
72
|
+
"@frigear-nu/widget": "0.1.2",
|
|
73
73
|
"@nuxt/devtools": "^3.4.2",
|
|
74
74
|
"@nuxt/eslint-config": "^1.17.0",
|
|
75
75
|
"@nuxt/module-builder": "^1.0.3",
|
|
76
76
|
"@nuxt/schema": "^4.6.1",
|
|
77
77
|
"@nuxt/test-utils": "^4.3.2",
|
|
78
|
-
"@nuxt/ui": "^4.
|
|
78
|
+
"@nuxt/ui": "^4.12.0",
|
|
79
79
|
"@nuxthub/core": "^0.10.8",
|
|
80
80
|
"@types/node": "^26.6.5",
|
|
81
81
|
"changelogen": "^0.6.2",
|