@frigear-nu/payments 0.0.10 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@frigear-nu/payments",
3
3
  "configKey": "payments",
4
- "version": "0.0.10",
4
+ "version": "0.1.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -38,8 +38,7 @@ const module$1 = defineNuxtModule({
38
38
  nuxt.options.runtimeConfig.payments = {
39
39
  checkoutExpiresInMinutes: options.settings?.checkoutExpiresInMinutes ?? 30
40
40
  };
41
- await registerDatabaseTable(nuxt, {
42
- name: "payments",
41
+ registerDatabaseTable(nuxt, {
43
42
  resolver,
44
43
  schema: (dialect) => `./runtime/server/db/schema.${dialect}`
45
44
  });
@@ -88,7 +87,14 @@ const module$1 = defineNuxtModule({
88
87
  if (hasNuxtModule("@frigear-nu/admin") && hasNuxtModule("@frigear-nu/widget")) {
89
88
  const { extendWidgets } = await import('@frigear-nu/widget/utils');
90
89
  extendWidgets(nuxt, "admin.dashboard.top", { id: "payments-revenue-stat", component: resolver.resolve("./runtime/app/widgets/RevenueStat.vue"), order: 30 });
91
- extendWidgets(nuxt, "admin.dashboard.side", { id: "payments-recent", component: resolver.resolve("./runtime/app/widgets/RecentTransactions.vue"), order: 20 });
90
+ extendWidgets(nuxt, "admin.dashboard.side", {
91
+ id: "payments-recent",
92
+ component: resolver.resolve("./runtime/app/widgets/RecentTransactions.vue"),
93
+ order: 20,
94
+ props: {
95
+ tabId: "payments"
96
+ }
97
+ });
92
98
  }
93
99
  addServerImportsDir(resolver.resolve("./runtime/server/utils"));
94
100
  inlineModuleRuntime(nuxt, resolver);
@@ -1,23 +1,15 @@
1
1
  <script setup>
2
- import { formatMoney as formatMinorAmount } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "UsersAdminPayments" });
4
3
  const props = defineProps({
5
4
  user: { type: Object, required: true }
6
5
  });
7
6
  const t = useModuleT("payments.admin");
8
7
  const status = useModuleT("payments.status");
8
+ const format = useFormat();
9
9
  const { data: payments, status: fetchStatus } = useLazyFetch(
10
10
  () => `/api/admin/users/${props.user.id}/payments`,
11
11
  { key: () => `admin-payments-${props.user.id}` }
12
12
  );
13
- const statusColors = {
14
- paid: "success",
15
- pending: "warning",
16
- failed: "error",
17
- expired: "neutral",
18
- refunded: "neutral",
19
- partially_refunded: "warning"
20
- };
21
13
  const statusLabel = (value) => status(value, value);
22
14
  const columns = computed(() => [
23
15
  { accessorKey: "createdAt", header: t("date", "Date") },
@@ -27,48 +19,50 @@ const columns = computed(() => [
27
19
  { accessorKey: "refundedAmount", header: t("refunded", "Refunded") },
28
20
  { accessorKey: "referenceType", header: t("reference", "Reference") }
29
21
  ]);
30
- const date = (value) => value ? new Date(value).toLocaleString() : "\u2013";
31
22
  </script>
32
23
 
33
24
  <template>
34
- <AdminSection
35
- :title="t('title', 'Payments')"
36
- >
37
- <template #flush>
38
- <UTable
39
- :data="payments ?? []"
40
- :columns="columns"
41
- :loading="fetchStatus === 'pending'"
42
- >
43
- <template #createdAt-cell="{ row }">
44
- <span class="whitespace-nowrap">{{ date(row.original.paidAt ?? row.original.createdAt) }}</span>
45
- </template>
46
- <template #description-cell="{ row }">
47
- {{ row.original.description ?? t("defaultDescription", "Payment") }}
48
- </template>
49
- <template #amount-cell="{ row }">
50
- <span class="whitespace-nowrap">{{ formatMinorAmount(row.original.amount, row.original.currency) }}</span>
51
- </template>
52
- <template #status-cell="{ row }">
53
- <UBadge
54
- variant="subtle"
55
- :color="statusColors[row.original.status] ?? 'neutral'"
56
- :label="statusLabel(row.original.status)"
57
- />
58
- </template>
59
- <template #refundedAmount-cell="{ row }">
60
- <span class="whitespace-nowrap">{{ row.original.refundedAmount ? formatMinorAmount(row.original.refundedAmount, row.original.currency) : "\u2013" }}</span>
61
- </template>
62
- <template #referenceType-cell="{ row }">
63
- <span class="text-muted">{{ row.original.referenceType ? `${row.original.referenceType} ${row.original.referenceId ?? ""}`.trim() : "\u2013" }}</span>
64
- </template>
65
- <template #empty>
66
- <AdminEmpty
67
- icon="i-lucide-credit-card"
68
- :title="t('empty', 'No payments yet.')"
69
- />
70
- </template>
71
- </UTable>
25
+ <TableCard>
26
+ <template #header>
27
+ <h2 class="text-highlighted text-sm font-semibold">
28
+ {{ t("title", "Payments") }}
29
+ </h2>
72
30
  </template>
73
- </AdminSection>
31
+ <UTable
32
+ :data="payments ?? []"
33
+ :columns="columns"
34
+ :loading="fetchStatus === 'pending'"
35
+ :ui="tableUi({ tr: '' })"
36
+ >
37
+ <template #createdAt-cell="{ row }">
38
+ <span class="whitespace-nowrap">{{ format.dateTime(row.original.paidAt ?? row.original.createdAt) || "\u2013" }}</span>
39
+ </template>
40
+ <template #description-cell="{ row }">
41
+ {{ row.original.description ?? t("defaultDescription", "Payment") }}
42
+ </template>
43
+ <template #amount-cell="{ row }">
44
+ <span class="whitespace-nowrap">{{ format.money(row.original.amount, row.original.currency) }}</span>
45
+ </template>
46
+ <template #status-cell="{ row }">
47
+ <UBadge
48
+ variant="subtle"
49
+ :color="statusColor(row.original.status, { partially_refunded: 'warning' })"
50
+ :label="statusLabel(row.original.status)"
51
+ />
52
+ </template>
53
+ <template #refundedAmount-cell="{ row }">
54
+ <span class="whitespace-nowrap">{{ row.original.refundedAmount ? format.money(row.original.refundedAmount, row.original.currency) : "\u2013" }}</span>
55
+ </template>
56
+ <template #referenceType-cell="{ row }">
57
+ <span class="text-muted">{{ row.original.referenceType ? `${row.original.referenceType} ${row.original.referenceId ?? ""}`.trim() : "\u2013" }}</span>
58
+ </template>
59
+ <template #empty>
60
+ <UEmpty
61
+ icon="i-lucide-credit-card"
62
+ variant="naked"
63
+ :title="t('empty', 'No payments yet')"
64
+ />
65
+ </template>
66
+ </UTable>
67
+ </TableCard>
74
68
  </template>
@@ -1,33 +1,45 @@
1
1
  <script setup>
2
- import { formatMoney as formatMinorAmount } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "UsersAdminLastPayment" });
4
3
  const props = defineProps({
5
4
  user: { type: Object, required: true }
6
5
  });
7
6
  const t = useModuleT("payments.admin");
8
7
  const status = useModuleT("payments.status");
8
+ const format = useFormat();
9
9
  const { data: payments, status: fetchStatus } = useLazyFetch(
10
10
  () => `/api/admin/users/${props.user.id}/payments`,
11
11
  { key: () => `admin-last-payment-${props.user.id}`, query: { limit: 1 } }
12
12
  );
13
13
  const last = computed(() => payments.value?.[0]);
14
- const date = (value) => value ? new Date(value).toLocaleDateString() : "\u2013";
14
+ const facts = computed(() => last.value ? [
15
+ { key: "amount", label: t("amount", "Amount"), value: format.money(last.value.amount, last.value.currency) },
16
+ { key: "status", label: t("statusColumn", "Status") },
17
+ { key: "date", label: t("date", "Date"), value: format.date(last.value.paidAt ?? last.value.createdAt) }
18
+ ] : []);
15
19
  </script>
16
20
 
17
21
  <template>
18
- <AdminStatCard
19
- :label="t('lastPayment', 'Last payment')"
20
- :empty="t('empty', 'No payments yet.')"
22
+ <SectionCard
23
+ :title="t('lastPayment', 'Last payment')"
21
24
  :loading="fetchStatus === 'pending' && !payments"
22
25
  >
23
- <template v-if="last">
24
- <span class="text-highlighted font-medium">{{ formatMinorAmount(last.amount, last.currency) }}</span>
25
- <UBadge
26
- variant="subtle"
27
- :color="last.status === 'paid' ? 'success' : 'neutral'"
28
- :label="status(last.status, last.status)"
29
- />
30
- <span class="text-muted">{{ date(last.paidAt ?? last.createdAt) }}</span>
31
- </template>
32
- </AdminStatCard>
26
+ <DescriptionList
27
+ v-if="last"
28
+ :items="facts"
29
+ >
30
+ <template #status>
31
+ <UBadge
32
+ variant="subtle"
33
+ :color="statusColor(last.status, { partially_refunded: 'warning' })"
34
+ :label="status(last.status, last.status)"
35
+ />
36
+ </template>
37
+ </DescriptionList>
38
+ <UEmpty
39
+ v-else
40
+ icon="i-lucide-credit-card"
41
+ variant="naked"
42
+ :title="t('empty', 'No payments yet')"
43
+ />
44
+ </SectionCard>
33
45
  </template>
@@ -1,3 +1,6 @@
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>;
1
+ type __VLS_Props = {
2
+ tabId?: string;
3
+ };
4
+ 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>;
2
5
  declare const _default: typeof __VLS_export;
3
6
  export default _default;
@@ -1,21 +1,26 @@
1
1
  <script setup>
2
- import { formatMoney } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "PaymentsRecentTransactions" });
3
+ const props = defineProps({
4
+ tabId: { type: String, required: false, default: "payments" }
5
+ });
4
6
  const t = useModuleT("payments.admin");
5
7
  const status = useModuleT("payments.status");
8
+ const format = useFormat();
9
+ const { localize } = useLocaleLink();
6
10
  const usersPage = useRuntimeConfig().public.users?.pages?.admin;
11
+ const usersPath = usersPage ? localize(usersPage.replace(/\/+$/, "")) : void 0;
7
12
  const { data, status: fetchStatus } = useLazyFetch("/api/payments/admin/recent", { key: "admin-payments-recent" });
8
- const date = (value) => value ? new Date(value).toLocaleDateString() : "\u2013";
9
- const color = (value) => value === "paid" ? "success" : value === "failed" ? "error" : "neutral";
10
- const userTo = (id) => usersPage ? `${usersPage.replace(/\/+$/, "")}/${id}` : void 0;
13
+ const userTo = (id) => usersPath ? { path: `${usersPath}/${id}`, query: { tab: props.tabId } } : void 0;
11
14
  </script>
12
15
 
13
16
  <template>
14
- <AdminListCard
17
+ <ListCard
15
18
  :title="t('recent', 'Recent transactions')"
16
19
  :loading="fetchStatus === 'pending' && !data"
17
20
  :is-empty="!data?.items.length"
18
- :empty="t('empty', 'No payments yet.')"
21
+ :empty="t('empty', 'No payments yet')"
22
+ :to="usersPath"
23
+ :to-label="t('viewUsers', 'View users')"
19
24
  >
20
25
  <li
21
26
  v-for="payment in data?.items"
@@ -35,20 +40,20 @@ const userTo = (id) => usersPage ? `${usersPage.replace(/\/+$/, "")}/${id}` : vo
35
40
  class="text-highlighted block truncate font-medium"
36
41
  >{{ payment.userName ?? payment.description ?? t("defaultDescription", "Payment") }}</span>
37
42
  <p class="text-muted truncate text-xs">
38
- {{ date(payment.paidAt ?? payment.createdAt) }}<template v-if="payment.description && payment.userName">
43
+ {{ format.date(payment.paidAt ?? payment.createdAt) }}<template v-if="payment.description && payment.userName">
39
44
  · {{ payment.description }}
40
45
  </template>
41
46
  </p>
42
47
  </div>
43
48
  <div class="flex shrink-0 flex-col items-end gap-1">
44
- <span class="text-highlighted font-medium">{{ formatMoney(payment.amount, payment.currency) }}</span>
49
+ <span class="text-highlighted font-medium">{{ format.money(payment.amount, payment.currency) }}</span>
45
50
  <UBadge
46
51
  size="sm"
47
52
  variant="subtle"
48
- :color="color(payment.status)"
53
+ :color="statusColor(payment.status, { partially_refunded: 'warning' })"
49
54
  :label="status(payment.status, payment.status)"
50
55
  />
51
56
  </div>
52
57
  </li>
53
- </AdminListCard>
58
+ </ListCard>
54
59
  </template>
@@ -1,3 +1,6 @@
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>;
1
+ type __VLS_Props = {
2
+ tabId?: string;
3
+ };
4
+ 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>;
2
5
  declare const _default: typeof __VLS_export;
3
6
  export default _default;
@@ -1,21 +1,18 @@
1
1
  <script setup>
2
- import { formatMoney } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "PaymentsRevenueStat" });
4
3
  const t = useModuleT("payments.admin");
4
+ const format = useFormat();
5
5
  const { data, status } = useLazyFetch("/api/payments/admin/recent", { key: "admin-payments-recent" });
6
6
  const main = computed(() => data.value?.revenue[0]);
7
7
  </script>
8
8
 
9
9
  <template>
10
- <AdminStatCard
10
+ <StatCard
11
11
  :label="t('revenue', 'Revenue, 30 days')"
12
12
  icon="i-lucide-circle-dollar-sign"
13
+ :value="main ? format.money(main.amount, main.currency) : void 0"
14
+ :hint="main ? t('transactions', '{count} transactions', { count: main.count }) : void 0"
13
15
  :loading="status === 'pending' && !data"
14
- :empty="t('noRevenue', 'No payments yet.')"
15
- >
16
- <template v-if="main">
17
- <span>{{ formatMoney(main.amount, main.currency) }}</span>
18
- <span class="text-muted text-sm font-normal">{{ t("transactions", "{count} transactions").replace("{count}", String(main.count)) }}</span>
19
- </template>
20
- </AdminStatCard>
16
+ :empty="t('noRevenue', 'No payments yet')"
17
+ />
21
18
  </template>
@@ -13,7 +13,7 @@
13
13
  "tab": "Betalinger",
14
14
  "title": "Betalinger",
15
15
  "lastPayment": "Seneste betaling",
16
- "empty": "Ingen betalinger endnu.",
16
+ "empty": "Ingen betalinger endnu",
17
17
  "date": "Dato",
18
18
  "description": "Beskrivelse",
19
19
  "amount": "Beløb",
@@ -22,9 +22,10 @@
22
22
  "reference": "Reference",
23
23
  "defaultDescription": "Betaling",
24
24
  "revenue": "Omsætning, 30 dage",
25
- "noRevenue": "Ingen betalinger endnu.",
25
+ "noRevenue": "Ingen betalinger endnu",
26
26
  "transactions": "{count} transaktioner",
27
- "recent": "Seneste transaktioner"
27
+ "recent": "Seneste transaktioner",
28
+ "viewUsers": "Se brugere"
28
29
  }
29
30
  }
30
31
  }
@@ -13,7 +13,7 @@
13
13
  "tab": "Payments",
14
14
  "title": "Payments",
15
15
  "lastPayment": "Last payment",
16
- "empty": "No payments yet.",
16
+ "empty": "No payments yet",
17
17
  "date": "Date",
18
18
  "description": "Description",
19
19
  "amount": "Amount",
@@ -22,9 +22,10 @@
22
22
  "reference": "Reference",
23
23
  "defaultDescription": "Payment",
24
24
  "revenue": "Revenue, 30 days",
25
- "noRevenue": "No payments yet.",
25
+ "noRevenue": "No payments yet",
26
26
  "transactions": "{count} transactions",
27
- "recent": "Recent transactions"
27
+ "recent": "Recent transactions",
28
+ "viewUsers": "View users"
28
29
  }
29
30
  }
30
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/payments",
3
- "version": "0.0.10",
3
+ "version": "0.1.0",
4
4
  "description": "One-time payments (Stripe Checkout, refunds and payment lifecycle hooks) for the frigear ecosystem, built on @frigear-nu/stripe.",
5
5
  "homepage": "https://docs.frigear.dev/modules/payments",
6
6
  "repository": {
@@ -30,14 +30,14 @@
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.0",
34
+ "@frigear-nu/mcp": "0.1.0",
35
+ "@frigear-nu/module-kit": "1.1.0",
36
+ "@frigear-nu/stripe": "0.1.0",
37
+ "@frigear-nu/ui": "0.1.0",
38
+ "@frigear-nu/users": "1.1.0",
39
39
  "@libsql/client": "^0.18.0",
40
- "@nuxt/kit": "^4.6.0",
40
+ "@nuxt/kit": "^4.6.1",
41
41
  "drizzle-kit": "^0.31.11",
42
42
  "drizzle-orm": "^0.45.3",
43
43
  "h3": "^1.15.11",
@@ -46,8 +46,8 @@
46
46
  "zod": "^4.6.5"
47
47
  },
48
48
  "peerDependencies": {
49
- "@frigear-nu/widget": "0.0.4",
50
- "@nuxt/ui": "^4.10.0",
49
+ "@frigear-nu/widget": "0.1.0",
50
+ "@nuxt/ui": "^4.11.2",
51
51
  "@nuxthub/core": "^0.10.8"
52
52
  },
53
53
  "peerDependenciesMeta": {
@@ -59,20 +59,20 @@
59
59
  }
60
60
  },
61
61
  "devDependencies": {
62
- "@frigear-nu/widget": "0.0.4",
62
+ "@frigear-nu/widget": "0.1.0",
63
63
  "@nuxt/devtools": "^3.4.2",
64
64
  "@nuxt/eslint-config": "^1.17.0",
65
65
  "@nuxt/module-builder": "^1.0.3",
66
- "@nuxt/schema": "^4.6.0",
66
+ "@nuxt/schema": "^4.6.1",
67
67
  "@nuxt/test-utils": "^4.3.2",
68
- "@nuxt/ui": "^4.11.0",
68
+ "@nuxt/ui": "^4.11.2",
69
69
  "@nuxthub/core": "^0.10.8",
70
- "@types/node": "latest",
70
+ "@types/node": "^26.6.5",
71
71
  "changelogen": "^0.6.2",
72
- "eslint": "^10.10.0",
72
+ "eslint": "^10.12.0",
73
73
  "nuxt": "^4.6.1",
74
74
  "typescript": "^6.0.3",
75
- "vitest": "^5.0.1",
75
+ "vitest": "^5.0.2",
76
76
  "vue-tsc": "^3.3.11"
77
77
  },
78
78
  "scripts": {