@frigear-nu/subscriptions 0.0.10 → 0.1.1

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 (38) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +12 -4
  3. package/dist/runtime/app/components/SubscriptionPlans.d.vue.ts +4 -4
  4. package/dist/runtime/app/components/SubscriptionPlans.vue +4 -1
  5. package/dist/runtime/app/components/SubscriptionPlans.vue.d.ts +4 -4
  6. package/dist/runtime/app/components/SubscriptionStatus.d.vue.ts +7 -2
  7. package/dist/runtime/app/components/SubscriptionStatus.vue +25 -22
  8. package/dist/runtime/app/components/SubscriptionStatus.vue.d.ts +7 -2
  9. package/dist/runtime/app/components/Users/Admin/Membership.d.vue.ts +3 -3
  10. package/dist/runtime/app/components/Users/Admin/Membership.vue +90 -94
  11. package/dist/runtime/app/components/Users/Admin/Membership.vue.d.ts +3 -3
  12. package/dist/runtime/app/composables/useSubscription.d.ts +8 -1
  13. package/dist/runtime/app/pages/cancel.d.vue.ts +1 -1
  14. package/dist/runtime/app/pages/cancel.vue +7 -15
  15. package/dist/runtime/app/pages/cancel.vue.d.ts +1 -1
  16. package/dist/runtime/app/pages/membership.d.vue.ts +1 -1
  17. package/dist/runtime/app/pages/membership.vue +21 -23
  18. package/dist/runtime/app/pages/membership.vue.d.ts +1 -1
  19. package/dist/runtime/app/pages/success.d.vue.ts +1 -1
  20. package/dist/runtime/app/pages/success.vue +7 -17
  21. package/dist/runtime/app/pages/success.vue.d.ts +1 -1
  22. package/dist/runtime/app/tabs/Membership.d.vue.ts +3 -3
  23. package/dist/runtime/app/tabs/Membership.vue +26 -21
  24. package/dist/runtime/app/tabs/Membership.vue.d.ts +3 -3
  25. package/dist/runtime/app/widgets/AdminCurrentSubscription.d.vue.ts +1 -1
  26. package/dist/runtime/app/widgets/AdminCurrentSubscription.vue +9 -5
  27. package/dist/runtime/app/widgets/AdminCurrentSubscription.vue.d.ts +1 -1
  28. package/dist/runtime/app/widgets/MembershipSummary.d.vue.ts +3 -0
  29. package/dist/runtime/app/widgets/MembershipSummary.vue +36 -0
  30. package/dist/runtime/app/widgets/MembershipSummary.vue.d.ts +3 -0
  31. package/dist/runtime/emails/subscriptions-payment-link.d.vue.ts +8 -0
  32. package/dist/runtime/emails/subscriptions-payment-link.vue +34 -0
  33. package/dist/runtime/emails/subscriptions-payment-link.vue.d.ts +8 -0
  34. package/dist/runtime/i18n/lang/da.json +16 -4
  35. package/dist/runtime/i18n/lang/en.json +16 -4
  36. package/dist/runtime/menus.js +3 -5
  37. package/dist/runtime/server/routes/admin/payment-link.post.js +2 -1
  38. package/package.json +24 -18
@@ -1,23 +1,15 @@
1
1
  <script setup>
2
2
  defineOptions({ name: "SubscriptionCancelPage" });
3
3
  const t = useModuleT("subscriptions.cancel");
4
+ const { localize } = useLocaleLink();
4
5
  const membership = useRuntimeConfig().public.subscriptions?.pages?.membership;
5
6
  </script>
6
7
 
7
8
  <template>
8
- <UContainer>
9
- <UPage>
10
- <UPageBody class="space-y-6">
11
- <UPageHeader
12
- :title="t('title', 'Checkout canceled')"
13
- :description="t('body', 'No changes were made and you have not been charged.')"
14
- />
15
- <UButton
16
- v-if="membership"
17
- :to="membership"
18
- :label="t('back', 'Back to plans')"
19
- />
20
- </UPageBody>
21
- </UPage>
22
- </UContainer>
9
+ <UEmpty
10
+ icon="i-lucide-circle-x"
11
+ :title="t('title', 'Checkout canceled')"
12
+ :description="t('body', 'No changes were made and you have not been charged.')"
13
+ :actions="membership ? [{ label: t('back', 'Back to membership'), to: localize(membership) }] : []"
14
+ />
23
15
  </template>
@@ -1,3 +1,3 @@
1
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<{}, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, true, {}, any>;
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
2
  declare const _default: typeof __VLS_export;
3
3
  export default _default;
@@ -1,3 +1,3 @@
1
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<{}, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, true, {}, any>;
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
2
  declare const _default: typeof __VLS_export;
3
3
  export default _default;
@@ -9,27 +9,25 @@ const { loggedIn } = useUserSession();
9
9
  </script>
10
10
 
11
11
  <template>
12
- <UContainer>
13
- <UPage>
14
- <UPageBody class="space-y-8">
15
- <SubscriptionStatus v-if="isActive" />
16
- <div
17
- v-else-if="loggedIn"
18
- class="space-y-2 text-center"
19
- >
20
- <h1 class="text-3xl font-bold tracking-tight sm:text-4xl">
21
- {{ t("status.heroTitle", "Join the team today") }}
22
- </h1>
23
- <p class="text-muted">
24
- {{ t("status.heroDescription", "Set up your membership to get started!") }}
25
- </p>
26
- </div>
27
- <UPageHeader
28
- v-if="cancelsAt"
29
- :title="t('plans.resubscribe', 'Resubscribe')"
30
- />
31
- <SubscriptionPlans confirm-change />
32
- </UPageBody>
33
- </UPage>
34
- </UContainer>
12
+ <div class="flex flex-col gap-6">
13
+ <SubscriptionStatus v-if="isActive" />
14
+ <div
15
+ v-else-if="loggedIn"
16
+ class="flex flex-col gap-1"
17
+ >
18
+ <h2 class="text-highlighted text-lg font-semibold">
19
+ {{ t("status.heroTitle", "Join the team today") }}
20
+ </h2>
21
+ <p class="text-muted text-sm">
22
+ {{ t("status.heroDescription", "Set up your membership to get started!") }}
23
+ </p>
24
+ </div>
25
+ <h2
26
+ v-if="cancelsAt"
27
+ class="text-highlighted text-lg font-semibold"
28
+ >
29
+ {{ t("plans.resubscribe", "Resubscribe") }}
30
+ </h2>
31
+ <SubscriptionPlans confirm-change />
32
+ </div>
35
33
  </template>
@@ -1,3 +1,3 @@
1
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<{}, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, true, {}, any>;
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
2
  declare const _default: typeof __VLS_export;
3
3
  export default _default;
@@ -1,3 +1,3 @@
1
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<{}, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, true, {}, any>;
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
2
  declare const _default: typeof __VLS_export;
3
3
  export default _default;
@@ -1,9 +1,9 @@
1
1
  <script setup>
2
- import SubscriptionStatus from "../components/SubscriptionStatus.vue";
3
2
  import { useSubscription } from "../composables/useSubscription";
4
3
  defineOptions({ name: "SubscriptionSuccessPage" });
5
4
  const t = useModuleT("subscriptions.success");
6
5
  const { refresh, isActive } = useSubscription();
6
+ const { localize } = useLocaleLink();
7
7
  const membership = useRuntimeConfig().public.subscriptions?.pages?.membership;
8
8
  const MAX_ATTEMPTS = 40;
9
9
  const INTERVAL_MS = 3e3;
@@ -20,20 +20,10 @@ onMounted(async () => {
20
20
  </script>
21
21
 
22
22
  <template>
23
- <UContainer>
24
- <UPage>
25
- <UPageBody class="space-y-6">
26
- <UPageHeader
27
- :title="t('title', 'Thank you!')"
28
- :description="isActive ? t('activeBody', 'Your subscription is now active.') : t('body', 'Your subscription is being activated. This can take a few seconds.')"
29
- />
30
- <SubscriptionStatus />
31
- <UButton
32
- v-if="membership"
33
- :to="membership"
34
- :label="t('continue', 'Continue')"
35
- />
36
- </UPageBody>
37
- </UPage>
38
- </UContainer>
23
+ <UEmpty
24
+ :icon="isActive ? 'i-lucide-circle-check' : 'i-lucide-loader-circle'"
25
+ :title="t('title', 'Thank you!')"
26
+ :description="isActive ? t('activeBody', 'Your subscription is now active.') : t('body', 'Your subscription is being activated. This can take a few seconds.')"
27
+ :actions="membership ? [{ label: t('back', 'Back to membership'), to: localize(membership) }] : []"
28
+ />
39
29
  </template>
@@ -1,3 +1,3 @@
1
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<{}, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, true, {}, any>;
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
2
  declare const _default: typeof __VLS_export;
3
3
  export default _default;
@@ -5,10 +5,10 @@ type __VLS_Props = {
5
5
  email: string;
6
6
  };
7
7
  };
8
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
9
  refresh: () => any;
10
- }, string, import("@vue/runtime-core").PublicProps, Readonly<__VLS_Props> & Readonly<{
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
11
  onRefresh?: (() => any) | undefined;
12
- }>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, false, {}, any>;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -5,14 +5,16 @@ const props = defineProps({
5
5
  });
6
6
  const emit = defineEmits(["refresh"]);
7
7
  const t = useModuleT("subscriptions.admin");
8
+ const format = useFormat();
9
+ const UBadge = resolveComponent("UBadge");
8
10
  const { data: history, refresh: refreshHistory, status } = useLazyFetch(
9
11
  () => `/api/admin/users/${props.user.id}/subscriptions`,
10
12
  { key: () => `admin-subscriptions-${props.user.id}` }
11
13
  );
12
- const date = (value) => value ? new Date(value).toLocaleDateString() : "\u2013";
14
+ const date = (value) => format.date(value) || "\u2013";
13
15
  const columns = computed(() => [
14
16
  { accessorKey: "planKey", header: t("history.plan", "Plan"), cell: ({ row }) => row.original.planKey ?? row.original.priceId },
15
- { accessorKey: "status", header: t("history.status", "Status") },
17
+ { accessorKey: "status", header: t("history.status", "Status"), cell: ({ row }) => h(UBadge, { label: row.original.status, variant: "subtle", color: statusColor(row.original.status) }) },
16
18
  { accessorKey: "source", header: t("history.source", "Source"), cell: ({ row }) => row.original.source === "manual" ? t("free", "Free of charge") : t("paid", "Paid (Stripe)") },
17
19
  { accessorKey: "currentPeriodStart", header: t("history.started", "Started"), cell: ({ row }) => date(row.original.currentPeriodStart) },
18
20
  { accessorKey: "currentPeriodEnd", header: t("history.periodEnd", "Period end"), cell: ({ row }) => date(row.original.currentPeriodEnd) },
@@ -25,29 +27,32 @@ async function changed() {
25
27
  </script>
26
28
 
27
29
  <template>
28
- <div class="flex flex-col gap-6">
29
- <AdminSection :title="t('membership', 'Membership')">
30
+ <div class="flex flex-col gap-4">
31
+ <SectionCard :title="t('membership', 'Membership')">
30
32
  <UsersAdminMembership
31
33
  :user="user"
32
34
  @refresh="changed"
33
35
  />
34
- </AdminSection>
36
+ </SectionCard>
35
37
 
36
- <AdminSection :title="t('history.title', 'Subscription history')">
37
- <template #flush>
38
- <UTable
39
- :data="history?.subscriptions ?? []"
40
- :columns="columns"
41
- :loading="status === 'pending'"
42
- >
43
- <template #empty>
44
- <AdminEmpty
45
- icon="i-lucide-badge-check"
46
- :title="t('history.empty', 'No subscriptions yet.')"
47
- />
48
- </template>
49
- </UTable>
50
- </template>
51
- </AdminSection>
38
+ <SectionCard
39
+ :title="t('history.title', 'Subscription history')"
40
+ flush
41
+ >
42
+ <UTable
43
+ :data="history?.subscriptions ?? []"
44
+ :columns="columns"
45
+ :loading="status === 'pending'"
46
+ :ui="tableUi({ tr: '' })"
47
+ >
48
+ <template #empty>
49
+ <UEmpty
50
+ variant="naked"
51
+ icon="i-lucide-badge-check"
52
+ :title="t('history.empty', 'No subscriptions yet.')"
53
+ />
54
+ </template>
55
+ </UTable>
56
+ </SectionCard>
52
57
  </div>
53
58
  </template>
@@ -5,10 +5,10 @@ type __VLS_Props = {
5
5
  email: string;
6
6
  };
7
7
  };
8
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
9
  refresh: () => any;
10
- }, string, import("@vue/runtime-core").PublicProps, Readonly<__VLS_Props> & Readonly<{
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
11
  onRefresh?: (() => any) | undefined;
12
- }>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, false, {}, any>;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -6,6 +6,6 @@ type __VLS_Props = {
6
6
  } | null;
7
7
  };
8
8
  };
9
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, false, {}, any>;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
10
  declare const _default: typeof __VLS_export;
11
11
  export default _default;
@@ -7,17 +7,21 @@ const t = useModuleT("subscriptions.admin");
7
7
  </script>
8
8
 
9
9
  <template>
10
- <AdminStatCard
10
+ <StatCard
11
11
  :label="t('currentSubscription', 'Current subscription')"
12
+ icon="i-lucide-badge-check"
12
13
  :empty="t('none', 'No active membership.')"
13
14
  >
14
- <template v-if="user.subscription">
15
+ <template
16
+ v-if="user.subscription"
17
+ #default
18
+ >
15
19
  <UBadge
16
20
  variant="subtle"
17
- :color="['active', 'trialing'].includes(user.subscription.status) ? 'success' : 'neutral'"
21
+ :color="statusColor(user.subscription.status)"
18
22
  :label="user.subscription.planKey ?? user.subscription.status"
19
23
  />
20
- <span class="text-muted">{{ user.subscription.status }}</span>
24
+ <span class="text-muted text-sm font-normal">{{ user.subscription.status }}</span>
21
25
  </template>
22
- </AdminStatCard>
26
+ </StatCard>
23
27
  </template>
@@ -6,6 +6,6 @@ type __VLS_Props = {
6
6
  } | null;
7
7
  };
8
8
  };
9
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {}, string, import("@vue/runtime-core").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, false, {}, any>;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
10
  declare const _default: typeof __VLS_export;
11
11
  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,36 @@
1
+ <script setup>
2
+ import { usePlans } from "../composables/usePlans";
3
+ import { useSubscription } from "../composables/useSubscription";
4
+ defineOptions({ name: "SubscriptionsMembershipSummary" });
5
+ const t = useModuleT("subscriptions.overview");
6
+ const ts = useModuleT("subscriptions.status");
7
+ const tp = useModuleT("subscriptions.catalog");
8
+ const format = useFormat();
9
+ const { localize } = useLocaleLink();
10
+ const { plans } = usePlans();
11
+ const { subscription, plan, status, isActive, cancelsAt } = useSubscription();
12
+ const membership = useRuntimeConfig().public.subscriptions?.pages?.membership;
13
+ const planLabel = computed(() => {
14
+ if (!plan.value) return "";
15
+ return tp(`${plan.value}.label`, plans.value.find((p) => p.key === plan.value)?.label ?? plan.value);
16
+ });
17
+ const hint = computed(() => {
18
+ const end = subscription.value?.currentPeriodEnd;
19
+ if (!end) return void 0;
20
+ const date = format.date(end);
21
+ if (subscription.value?.source === "manual") return ts("freeUntil", "Free membership until {date}", { date });
22
+ return cancelsAt.value ? ts("cancels", "Ends on {date}", { date }) : ts("renews", "Renews on {date}", { date });
23
+ });
24
+ </script>
25
+
26
+ <template>
27
+ <StatCard
28
+ :label="t('label', 'Membership')"
29
+ icon="i-lucide-credit-card"
30
+ :to="membership ? localize(membership) : void 0"
31
+ :loading="status === 'pending' && !subscription"
32
+ :value="isActive ? planLabel : void 0"
33
+ :hint="isActive ? hint : void 0"
34
+ :empty="t('none', 'No active membership')"
35
+ />
36
+ </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,8 @@
1
+ type __VLS_Props = {
2
+ name: string;
3
+ label: string;
4
+ url: string;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,34 @@
1
+ <script setup>
2
+ import { EmailButton, EmailDivider, EmailHeading, EmailLayout, EmailLinkFallback, EmailText } from "@frigear-nu/email-templates/components";
3
+ defineProps({
4
+ name: { type: String, required: true },
5
+ label: { type: String, required: true },
6
+ url: { type: String, required: true }
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <EmailLayout preview="Complete your membership">
12
+ <ESubject>Complete your {{ label }} membership</ESubject>
13
+ <EmailHeading>
14
+ Complete your {{ label }} membership
15
+ </EmailHeading>
16
+ <EmailDivider />
17
+ <EmailText>
18
+ Hi {{ name }},
19
+ </EmailText>
20
+ <EmailText>
21
+ Use the button below to complete your {{ label }} membership. The link is valid for 24 hours.
22
+ </EmailText>
23
+ <EmailButton
24
+ :href="url"
25
+ >
26
+ Complete membership
27
+ </EmailButton>
28
+ <EmailLinkFallback :href="url" />
29
+ <EmailDivider />
30
+ <EmailText>
31
+ If you weren’t expecting this email, you can safely ignore it.
32
+ </EmailText>
33
+ </EmailLayout>
34
+ </template>
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ name: string;
3
+ label: string;
4
+ url: string;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -20,7 +20,8 @@
20
20
  "confirmDowngrade": "Hvis du nedgraderer, kører dit nuværende abonnement til næste forfaldsdato, hvorefter det skifter.",
21
21
  "subscribeTo": "Du abonnerer nu på:",
22
22
  "changeTitle": "Opdatér abonnement",
23
- "checkoutTitle": "Bliv medlem"
23
+ "checkoutTitle": "Bliv medlem",
24
+ "confirmResume": "Dit medlemskab fornyes som hidtil."
24
25
  },
25
26
  "interval": {
26
27
  "day": "dag",
@@ -61,12 +62,12 @@
61
62
  "title": "Tak!",
62
63
  "body": "Dit abonnement bliver aktiveret. Det kan tage et par sekunder.",
63
64
  "activeBody": "Dit abonnement er aktiveret.",
64
- "continue": "Fortsæt"
65
+ "back": "Tilbage til medlemskab"
65
66
  },
66
67
  "cancel": {
67
68
  "title": "Betaling annulleret",
68
69
  "body": "Der blev ikke foretaget ændringer, og du er ikke blevet opkrævet.",
69
- "back": "Tilbage til planer"
70
+ "back": "Tilbage til medlemskab"
70
71
  },
71
72
  "errors": {
72
73
  "checkout": "Kunne ikke starte betalingen.",
@@ -113,7 +114,18 @@
113
114
  "started": "Startet",
114
115
  "periodEnd": "Periodeslut",
115
116
  "ended": "Afsluttet"
116
- }
117
+ },
118
+ "type": "Type",
119
+ "validUntil": "Gyldigt til",
120
+ "status": "Status",
121
+ "revokeTitle": "Fjern det gratis medlemskab for {name}?",
122
+ "revokeBody": "Brugeren mister medlemsfordelene med det samme. Et betalt abonnement påvirkes ikke.",
123
+ "revokeConfirm": "Fjern gratis medlemskab"
124
+ },
125
+ "overview": {
126
+ "label": "Medlemskab",
127
+ "none": "Intet aktivt medlemskab",
128
+ "manage": "Administrér medlemskab"
117
129
  }
118
130
  }
119
131
  }
@@ -20,7 +20,8 @@
20
20
  "confirmDowngrade": "If you downgrade, your current subscription runs until the next due date, then switches.",
21
21
  "subscribeTo": "You will now subscribe to:",
22
22
  "changeTitle": "Update subscription",
23
- "checkoutTitle": "Get a membership"
23
+ "checkoutTitle": "Get a membership",
24
+ "confirmResume": "Your membership will keep renewing as before."
24
25
  },
25
26
  "interval": {
26
27
  "day": "day",
@@ -61,12 +62,12 @@
61
62
  "title": "Thank you!",
62
63
  "body": "Your subscription is being activated. This can take a few seconds.",
63
64
  "activeBody": "Your subscription is now active.",
64
- "continue": "Continue"
65
+ "back": "Back to membership"
65
66
  },
66
67
  "cancel": {
67
68
  "title": "Checkout canceled",
68
69
  "body": "No changes were made and you have not been charged.",
69
- "back": "Back to plans"
70
+ "back": "Back to membership"
70
71
  },
71
72
  "errors": {
72
73
  "checkout": "Failed to start checkout.",
@@ -113,7 +114,18 @@
113
114
  "started": "Started",
114
115
  "periodEnd": "Period end",
115
116
  "ended": "Ended"
116
- }
117
+ },
118
+ "type": "Type",
119
+ "validUntil": "Valid until",
120
+ "status": "Status",
121
+ "revokeTitle": "Revoke the free membership of {name}?",
122
+ "revokeBody": "They lose the membership benefits right away. A paid subscription is not affected.",
123
+ "revokeConfirm": "Revoke free membership"
124
+ },
125
+ "overview": {
126
+ "label": "Membership",
127
+ "none": "No active membership",
128
+ "manage": "Manage membership"
117
129
  }
118
130
  }
119
131
  }
@@ -2,7 +2,7 @@ import { defineMenuItem, defineMenuSource, extendMenuItem } from "@frigear-nu/me
2
2
  export default defineMenuSource(() => {
3
3
  const config = useRuntimeConfig().public.subscriptions;
4
4
  if (config?.pages?.membership) {
5
- const item = defineMenuItem({
5
+ extendMenuItem("account", defineMenuItem({
6
6
  id: "subscription",
7
7
  label: "Subscription",
8
8
  labelKey: "subscriptions.menu.subscription.label",
@@ -10,9 +10,7 @@ export default defineMenuSource(() => {
10
10
  descriptionKey: "subscriptions.menu.subscription.description",
11
11
  icon: "i-lucide-credit-card",
12
12
  to: config.pages.membership,
13
- order: 30
14
- });
15
- extendMenuItem("account", item);
16
- extendMenuItem("dashboard.account", item);
13
+ order: 15
14
+ }));
17
15
  }
18
16
  });
@@ -38,7 +38,8 @@ export default defineEventHandler(async (event) => {
38
38
  body: `<p>Hi ${escapeHtml(target.name)},</p><p>Use the link below to complete your ${escapeHtml(label)} membership. The link is valid for 24 hours.</p><p><a href="${escapeHtml(session.url)}">${escapeHtml(session.url)}</a></p>`,
39
39
  text: `Hi ${target.name},
40
40
 
41
- Complete your ${label} membership (valid for 24 hours): ${session.url}`
41
+ Complete your ${label} membership (valid for 24 hours): ${session.url}`,
42
+ template: { name: "subscriptions-payment-link", props: { name: target.name, label, url: session.url } }
42
43
  });
43
44
  emailed = true;
44
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/subscriptions",
3
- "version": "0.0.10",
3
+ "version": "0.1.1",
4
4
  "description": "Recurring subscriptions (plans, checkout, upgrade/downgrade, billing portal and entitlements) for the frigear ecosystem, built on @frigear-nu/stripe.",
5
5
  "homepage": "https://docs.frigear.dev/modules/subscriptions",
6
6
  "repository": {
@@ -30,14 +30,15 @@
30
30
  "playground"
31
31
  ],
32
32
  "dependencies": {
33
- "@frigear-nu/auth": "0.0.5",
34
- "@frigear-nu/mcp": "0.0.5",
35
- "@frigear-nu/module-kit": "1.0.1",
36
- "@frigear-nu/stripe": "0.0.10",
37
- "@frigear-nu/ui": "0.0.9",
38
- "@frigear-nu/users": "1.0.11",
33
+ "@frigear-nu/auth": "0.1.2",
34
+ "@frigear-nu/mcp": "0.1.1",
35
+ "@frigear-nu/module-kit": "1.1.1",
36
+ "@frigear-nu/notify": "0.1.1",
37
+ "@frigear-nu/stripe": "0.1.1",
38
+ "@frigear-nu/ui": "0.1.1",
39
+ "@frigear-nu/users": "1.1.2",
39
40
  "@libsql/client": "^0.18.0",
40
- "@nuxt/kit": "^4.6.0",
41
+ "@nuxt/kit": "^4.6.1",
41
42
  "@vueuse/core": "^15.0.0",
42
43
  "drizzle-kit": "^0.31.11",
43
44
  "drizzle-orm": "^0.45.3",
@@ -47,12 +48,16 @@
47
48
  "zod": "^4.6.5"
48
49
  },
49
50
  "peerDependencies": {
50
- "@frigear-nu/menu": "0.0.11",
51
- "@frigear-nu/widget": "0.0.4",
52
- "@nuxt/ui": "^4.10.0",
51
+ "@frigear-nu/email-templates": "0.1.0",
52
+ "@frigear-nu/menu": "0.1.1",
53
+ "@frigear-nu/widget": "0.1.1",
54
+ "@nuxt/ui": "^4.11.2",
53
55
  "@nuxthub/core": "^0.10.8"
54
56
  },
55
57
  "peerDependenciesMeta": {
58
+ "@frigear-nu/email-templates": {
59
+ "optional": true
60
+ },
56
61
  "@frigear-nu/menu": {
57
62
  "optional": true
58
63
  },
@@ -64,21 +69,22 @@
64
69
  }
65
70
  },
66
71
  "devDependencies": {
67
- "@frigear-nu/menu": "0.0.11",
68
- "@frigear-nu/widget": "0.0.4",
72
+ "@frigear-nu/email-templates": "0.1.0",
73
+ "@frigear-nu/menu": "0.1.1",
74
+ "@frigear-nu/widget": "0.1.1",
69
75
  "@nuxt/devtools": "^3.4.2",
70
76
  "@nuxt/eslint-config": "^1.17.0",
71
77
  "@nuxt/module-builder": "^1.0.3",
72
- "@nuxt/schema": "^4.6.0",
78
+ "@nuxt/schema": "^4.6.1",
73
79
  "@nuxt/test-utils": "^4.3.2",
74
- "@nuxt/ui": "^4.11.0",
80
+ "@nuxt/ui": "^4.11.2",
75
81
  "@nuxthub/core": "^0.10.8",
76
- "@types/node": "latest",
82
+ "@types/node": "^26.6.5",
77
83
  "changelogen": "^0.6.2",
78
- "eslint": "^10.10.0",
84
+ "eslint": "^10.12.0",
79
85
  "nuxt": "^4.6.1",
80
86
  "typescript": "^6.0.3",
81
- "vitest": "^5.0.1",
87
+ "vitest": "^5.0.2",
82
88
  "vue-tsc": "^3.3.11"
83
89
  },
84
90
  "scripts": {