@hed-hog/billing 0.0.2 → 0.0.285

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 (51) hide show
  1. package/README.md +420 -0
  2. package/dist/billing-contracts.controller.d.ts +85 -4
  3. package/dist/billing-contracts.controller.d.ts.map +1 -1
  4. package/dist/billing-coupons.controller.d.ts +60 -4
  5. package/dist/billing-coupons.controller.d.ts.map +1 -1
  6. package/dist/billing-dashboard.controller.d.ts +20 -5
  7. package/dist/billing-dashboard.controller.d.ts.map +1 -1
  8. package/dist/billing-entitlements.controller.d.ts +28 -2
  9. package/dist/billing-entitlements.controller.d.ts.map +1 -1
  10. package/dist/billing-gateways.controller.d.ts +18 -2
  11. package/dist/billing-gateways.controller.d.ts.map +1 -1
  12. package/dist/billing-invoices.controller.d.ts +56 -2
  13. package/dist/billing-invoices.controller.d.ts.map +1 -1
  14. package/dist/billing-offers.controller.d.ts +61 -4
  15. package/dist/billing-offers.controller.d.ts.map +1 -1
  16. package/dist/billing-orders.controller.d.ts +39 -2
  17. package/dist/billing-orders.controller.d.ts.map +1 -1
  18. package/dist/billing-payments.controller.d.ts +16 -1
  19. package/dist/billing-payments.controller.d.ts.map +1 -1
  20. package/dist/billing-prices.controller.d.ts +80 -4
  21. package/dist/billing-prices.controller.d.ts.map +1 -1
  22. package/dist/billing-products.controller.d.ts +62 -4
  23. package/dist/billing-products.controller.d.ts.map +1 -1
  24. package/dist/billing-subscriptions.controller.d.ts +138 -5
  25. package/dist/billing-subscriptions.controller.d.ts.map +1 -1
  26. package/dist/billing.service.d.ts +663 -39
  27. package/dist/billing.service.d.ts.map +1 -1
  28. package/dist/billing.service.js +135 -16
  29. package/dist/billing.service.js.map +1 -1
  30. package/hedhog/data/menu.yaml +1 -1
  31. package/hedhog/frontend/app/contracts/page.tsx.ejs +68 -64
  32. package/hedhog/frontend/app/coupons/page.tsx.ejs +81 -77
  33. package/hedhog/frontend/app/entitlements/page.tsx.ejs +59 -58
  34. package/hedhog/frontend/app/gateways/page.tsx.ejs +125 -57
  35. package/hedhog/frontend/app/invoices/page.tsx.ejs +49 -43
  36. package/hedhog/frontend/app/offers/page.tsx.ejs +68 -64
  37. package/hedhog/frontend/app/orders/page.tsx.ejs +47 -46
  38. package/hedhog/frontend/app/page.tsx.ejs +186 -186
  39. package/hedhog/frontend/app/payments/page.tsx.ejs +51 -45
  40. package/hedhog/frontend/app/prices/page.tsx.ejs +81 -75
  41. package/hedhog/frontend/app/products/page.tsx.ejs +79 -73
  42. package/hedhog/frontend/app/refunds/page.tsx.ejs +50 -44
  43. package/hedhog/frontend/app/reports/page.tsx.ejs +1 -1
  44. package/hedhog/frontend/app/seats/page.tsx.ejs +826 -0
  45. package/hedhog/frontend/app/subscriptions/page.tsx.ejs +95 -90
  46. package/hedhog/frontend/app/webhooks/page.tsx.ejs +47 -39
  47. package/hedhog/frontend/messages/en.json +640 -551
  48. package/hedhog/frontend/messages/pt.json +652 -563
  49. package/hedhog/table/billing_payment_method.yaml +1 -1
  50. package/package.json +4 -3
  51. package/src/billing.service.ts +299 -17
@@ -1,186 +1,186 @@
1
- 'use client';
2
-
3
- import {
4
- Page,
5
- PageHeader,
6
- StatsCards,
7
- type StatCardConfig,
8
- } from '@/components/entity-list';
9
- import {
10
- Card,
11
- CardContent,
12
- CardDescription,
13
- CardHeader,
14
- CardTitle,
15
- } from '@/components/ui/card';
16
- import {
17
- Table,
18
- TableBody,
19
- TableCell,
20
- TableHead,
21
- TableHeader,
22
- TableRow,
23
- } from '@/components/ui/table';
24
- import {
25
- AlertTriangle,
26
- CreditCard,
27
- FileWarning,
28
- RefreshCw,
29
- } from 'lucide-react';
30
- import { useTranslations } from 'next-intl';
31
- import {
32
- CartesianGrid,
33
- Legend,
34
- Line,
35
- LineChart,
36
- ResponsiveContainer,
37
- Tooltip,
38
- XAxis,
39
- YAxis,
40
- } from 'recharts';
41
- import {
42
- MOCK_DASHBOARD_STATS,
43
- MOCK_PAYMENTS,
44
- MOCK_REVENUE_CHART,
45
- } from './_lib/billing-mocks';
46
-
47
- const formatCurrency = (cents: number, currency = 'BRL') =>
48
- new Intl.NumberFormat('pt-BR', { style: 'currency', currency }).format(
49
- cents / 100
50
- );
51
-
52
- const formatDate = (value: string | null | undefined) => {
53
- if (!value) {
54
- return '-';
55
- }
56
-
57
- return new Intl.DateTimeFormat('pt-BR', {
58
- dateStyle: 'short',
59
- timeStyle: 'short',
60
- }).format(new Date(value));
61
- };
62
-
63
- export default function BillingDashboardPage() {
64
- const t = useTranslations('BillingDashboardPage');
65
-
66
- const stats: StatCardConfig[] = [
67
- {
68
- title: t('stats.mrr'),
69
- value: formatCurrency(MOCK_DASHBOARD_STATS.mrr),
70
- icon: <RefreshCw className="size-5" />,
71
- iconBgColor: 'bg-blue-50',
72
- iconColor: 'text-blue-600',
73
- },
74
- {
75
- title: t('stats.activeSubscriptions'),
76
- value: MOCK_DASHBOARD_STATS.activeSubscriptions,
77
- icon: <CreditCard className="size-5" />,
78
- iconBgColor: 'bg-green-50',
79
- iconColor: 'text-green-600',
80
- },
81
- {
82
- title: t('stats.failedPayments'),
83
- value: MOCK_DASHBOARD_STATS.failedPayments,
84
- icon: <AlertTriangle className="size-5" />,
85
- iconBgColor: 'bg-yellow-50',
86
- iconColor: 'text-yellow-600',
87
- },
88
- {
89
- title: t('stats.overdueInvoices'),
90
- value: MOCK_DASHBOARD_STATS.overdueInvoices,
91
- icon: <FileWarning className="size-5" />,
92
- iconBgColor: 'bg-red-50',
93
- iconColor: 'text-red-600',
94
- },
95
- ];
96
-
97
- return (
98
- <Page>
99
- <PageHeader
100
- title={t('title')}
101
- description={t('description')}
102
- breadcrumbs={[
103
- { label: t('breadcrumbs.home'), href: '/' },
104
- { label: t('breadcrumbs.billing'), href: '/billing' },
105
- { label: t('breadcrumbs.dashboard') },
106
- ]}
107
- />
108
-
109
- <StatsCards stats={stats} />
110
-
111
- <Card>
112
- <CardHeader>
113
- <CardTitle>{t('revenueTrend.title')}</CardTitle>
114
- <CardDescription>{t('revenueTrend.description')}</CardDescription>
115
- </CardHeader>
116
- <CardContent>
117
- <ResponsiveContainer width="100%" height={320}>
118
- <LineChart data={MOCK_REVENUE_CHART}>
119
- <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
120
- <XAxis dataKey="month" tickLine={false} axisLine={false} />
121
- <YAxis
122
- tickLine={false}
123
- axisLine={false}
124
- tickFormatter={(value) => `R$ ${(value / 100000).toFixed(0)}k`}
125
- />
126
- <Tooltip
127
- formatter={(value: number) => formatCurrency(value)}
128
- contentStyle={{
129
- backgroundColor: 'hsl(var(--background))',
130
- border: '1px solid hsl(var(--border))',
131
- borderRadius: '8px',
132
- }}
133
- />
134
- <Legend />
135
- <Line
136
- type="monotone"
137
- dataKey="revenue_cents"
138
- name={t('revenueTrend.legend')}
139
- stroke="#2563eb"
140
- strokeWidth={3}
141
- dot={{ r: 4, fill: '#2563eb' }}
142
- />
143
- </LineChart>
144
- </ResponsiveContainer>
145
- </CardContent>
146
- </Card>
147
-
148
- <Card>
149
- <CardHeader>
150
- <CardTitle>{t('recentPayments.title')}</CardTitle>
151
- <CardDescription>{t('recentPayments.description')}</CardDescription>
152
- </CardHeader>
153
- <CardContent>
154
- <div className="overflow-x-auto rounded-md border">
155
- <Table>
156
- <TableHeader>
157
- <TableRow>
158
- <TableHead>{t('recentPayments.columns.provider')}</TableHead>
159
- <TableHead>{t('recentPayments.columns.method')}</TableHead>
160
- <TableHead>{t('recentPayments.columns.status')}</TableHead>
161
- <TableHead>{t('recentPayments.columns.amount')}</TableHead>
162
- <TableHead>{t('recentPayments.columns.paidAt')}</TableHead>
163
- </TableRow>
164
- </TableHeader>
165
- <TableBody>
166
- {MOCK_PAYMENTS.slice(0, 10).map((payment) => (
167
- <TableRow key={payment.id}>
168
- <TableCell className="font-medium">
169
- {payment.provider}
170
- </TableCell>
171
- <TableCell>{payment.method_type}</TableCell>
172
- <TableCell>{payment.status}</TableCell>
173
- <TableCell>
174
- {formatCurrency(payment.amount_cents, payment.currency)}
175
- </TableCell>
176
- <TableCell>{formatDate(payment.paid_at)}</TableCell>
177
- </TableRow>
178
- ))}
179
- </TableBody>
180
- </Table>
181
- </div>
182
- </CardContent>
183
- </Card>
184
- </Page>
185
- );
186
- }
1
+ 'use client';
2
+
3
+ import {
4
+ Page,
5
+ PageHeader,
6
+ StatsCards,
7
+ type StatCardConfig,
8
+ } from '@/components/entity-list';
9
+ import {
10
+ Card,
11
+ CardContent,
12
+ CardDescription,
13
+ CardHeader,
14
+ CardTitle,
15
+ } from '@/components/ui/card';
16
+ import {
17
+ Table,
18
+ TableBody,
19
+ TableCell,
20
+ TableHead,
21
+ TableHeader,
22
+ TableRow,
23
+ } from '@/components/ui/table';
24
+ import {
25
+ AlertTriangle,
26
+ CreditCard,
27
+ FileWarning,
28
+ RefreshCw,
29
+ } from 'lucide-react';
30
+ import { useTranslations } from 'next-intl';
31
+ import {
32
+ CartesianGrid,
33
+ Legend,
34
+ Line,
35
+ LineChart,
36
+ ResponsiveContainer,
37
+ Tooltip,
38
+ XAxis,
39
+ YAxis,
40
+ } from 'recharts';
41
+ import {
42
+ MOCK_DASHBOARD_STATS,
43
+ MOCK_PAYMENTS,
44
+ MOCK_REVENUE_CHART,
45
+ } from './_lib/billing-mocks';
46
+
47
+ const formatCurrency = (cents: number, currency = 'BRL') =>
48
+ new Intl.NumberFormat('pt-BR', { style: 'currency', currency }).format(
49
+ cents / 100
50
+ );
51
+
52
+ const formatDate = (value: string | null | undefined) => {
53
+ if (!value) {
54
+ return '-';
55
+ }
56
+
57
+ return new Intl.DateTimeFormat('pt-BR', {
58
+ dateStyle: 'short',
59
+ timeStyle: 'short',
60
+ }).format(new Date(value));
61
+ };
62
+
63
+ export default function BillingDashboardPage() {
64
+ const t = useTranslations('billing.BillingDashboardPage');
65
+
66
+ const stats: StatCardConfig[] = [
67
+ {
68
+ title: t('stats.mrr'),
69
+ value: formatCurrency(MOCK_DASHBOARD_STATS.mrr),
70
+ icon: <RefreshCw className="size-5" />,
71
+ iconBgColor: 'bg-blue-50',
72
+ iconColor: 'text-blue-600',
73
+ },
74
+ {
75
+ title: t('stats.activeSubscriptions'),
76
+ value: MOCK_DASHBOARD_STATS.activeSubscriptions,
77
+ icon: <CreditCard className="size-5" />,
78
+ iconBgColor: 'bg-green-50',
79
+ iconColor: 'text-green-600',
80
+ },
81
+ {
82
+ title: t('stats.failedPayments'),
83
+ value: MOCK_DASHBOARD_STATS.failedPayments,
84
+ icon: <AlertTriangle className="size-5" />,
85
+ iconBgColor: 'bg-yellow-50',
86
+ iconColor: 'text-yellow-600',
87
+ },
88
+ {
89
+ title: t('stats.overdueInvoices'),
90
+ value: MOCK_DASHBOARD_STATS.overdueInvoices,
91
+ icon: <FileWarning className="size-5" />,
92
+ iconBgColor: 'bg-red-50',
93
+ iconColor: 'text-red-600',
94
+ },
95
+ ];
96
+
97
+ return (
98
+ <Page>
99
+ <PageHeader
100
+ title={t('title')}
101
+ description={t('description')}
102
+ breadcrumbs={[
103
+ { label: t('breadcrumbs.home'), href: '/' },
104
+ { label: t('breadcrumbs.billing'), href: '/billing' },
105
+ { label: t('breadcrumbs.dashboard') },
106
+ ]}
107
+ />
108
+
109
+ <StatsCards stats={stats} />
110
+
111
+ <Card>
112
+ <CardHeader>
113
+ <CardTitle>{t('revenueTrend.title')}</CardTitle>
114
+ <CardDescription>{t('revenueTrend.description')}</CardDescription>
115
+ </CardHeader>
116
+ <CardContent>
117
+ <ResponsiveContainer width="100%" height={320}>
118
+ <LineChart data={MOCK_REVENUE_CHART}>
119
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
120
+ <XAxis dataKey="month" tickLine={false} axisLine={false} />
121
+ <YAxis
122
+ tickLine={false}
123
+ axisLine={false}
124
+ tickFormatter={(value) => `R$ ${(value / 100000).toFixed(0)}k`}
125
+ />
126
+ <Tooltip
127
+ formatter={(value: number) => formatCurrency(value)}
128
+ contentStyle={{
129
+ backgroundColor: 'hsl(var(--background))',
130
+ border: '1px solid hsl(var(--border))',
131
+ borderRadius: '8px',
132
+ }}
133
+ />
134
+ <Legend />
135
+ <Line
136
+ type="monotone"
137
+ dataKey="revenue_cents"
138
+ name={t('revenueTrend.legend')}
139
+ stroke="#2563eb"
140
+ strokeWidth={3}
141
+ dot={{ r: 4, fill: '#2563eb' }}
142
+ />
143
+ </LineChart>
144
+ </ResponsiveContainer>
145
+ </CardContent>
146
+ </Card>
147
+
148
+ <Card>
149
+ <CardHeader>
150
+ <CardTitle>{t('recentPayments.title')}</CardTitle>
151
+ <CardDescription>{t('recentPayments.description')}</CardDescription>
152
+ </CardHeader>
153
+ <CardContent>
154
+ <div className="overflow-x-auto rounded-md border">
155
+ <Table>
156
+ <TableHeader>
157
+ <TableRow>
158
+ <TableHead>{t('recentPayments.columns.provider')}</TableHead>
159
+ <TableHead>{t('recentPayments.columns.method')}</TableHead>
160
+ <TableHead>{t('recentPayments.columns.status')}</TableHead>
161
+ <TableHead>{t('recentPayments.columns.amount')}</TableHead>
162
+ <TableHead>{t('recentPayments.columns.paidAt')}</TableHead>
163
+ </TableRow>
164
+ </TableHeader>
165
+ <TableBody>
166
+ {MOCK_PAYMENTS.slice(0, 10).map((payment) => (
167
+ <TableRow key={payment.id}>
168
+ <TableCell className="font-medium">
169
+ {payment.provider}
170
+ </TableCell>
171
+ <TableCell>{payment.method_type}</TableCell>
172
+ <TableCell>{payment.status}</TableCell>
173
+ <TableCell>
174
+ {formatCurrency(payment.amount_cents, payment.currency)}
175
+ </TableCell>
176
+ <TableCell>{formatDate(payment.paid_at)}</TableCell>
177
+ </TableRow>
178
+ ))}
179
+ </TableBody>
180
+ </Table>
181
+ </div>
182
+ </CardContent>
183
+ </Card>
184
+ </Page>
185
+ );
186
+ }
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import {
4
+ EmptyState,
4
5
  Page,
5
6
  PageHeader,
6
7
  PaginationFooter,
@@ -16,6 +17,7 @@ import {
16
17
  TableRow,
17
18
  } from '@/components/ui/table';
18
19
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
20
+ import { FileText } from 'lucide-react';
19
21
  import { useTranslations } from 'next-intl';
20
22
  import { useState } from 'react';
21
23
 
@@ -64,7 +66,7 @@ const formatDate = (value: string | null) => {
64
66
  };
65
67
 
66
68
  export default function BillingPaymentsPage() {
67
- const t = useTranslations('BillingPaymentsPage');
69
+ const t = useTranslations('billing.BillingPaymentsPage');
68
70
  const { request, currentLocaleCode } = useApp();
69
71
  const [search, setSearch] = useState('');
70
72
  const [page, setPage] = useState(1);
@@ -125,52 +127,56 @@ export default function BillingPaymentsPage() {
125
127
  placeholder={t('filters.searchPlaceholder')}
126
128
  />
127
129
 
128
- <div className="overflow-x-auto rounded-md border">
129
- <Table>
130
- <TableHeader>
131
- <TableRow>
132
- <TableHead>{t('table.columns.provider')}</TableHead>
133
- <TableHead>{t('table.columns.methodType')}</TableHead>
134
- <TableHead>{t('table.columns.status')}</TableHead>
135
- <TableHead>{t('table.columns.amount')}</TableHead>
136
- <TableHead>{t('table.columns.currency')}</TableHead>
137
- <TableHead>{t('table.columns.paidAt')}</TableHead>
138
- </TableRow>
139
- </TableHeader>
140
- <TableBody>
141
- {items.length === 0 && (
130
+ {items.length > 0 ? (
131
+ <div className="overflow-x-auto rounded-md border">
132
+ <Table>
133
+ <TableHeader>
142
134
  <TableRow>
143
- <TableCell
144
- colSpan={6}
145
- className="text-center text-muted-foreground"
146
- >
147
- {t('table.empty')}
148
- </TableCell>
135
+ <TableHead>{t('table.columns.provider')}</TableHead>
136
+ <TableHead>{t('table.columns.methodType')}</TableHead>
137
+ <TableHead>{t('table.columns.status')}</TableHead>
138
+ <TableHead>{t('table.columns.amount')}</TableHead>
139
+ <TableHead>{t('table.columns.currency')}</TableHead>
140
+ <TableHead>{t('table.columns.paidAt')}</TableHead>
149
141
  </TableRow>
150
- )}
151
- {items.map((item) => (
152
- <TableRow key={item.id}>
153
- <TableCell className="font-medium">{item.provider}</TableCell>
154
- <TableCell>
155
- <Badge className={badgeClass(item.method_type)}>
156
- {item.method_type}
157
- </Badge>
158
- </TableCell>
159
- <TableCell>
160
- <Badge className={badgeClass(item.status)}>
161
- {item.status}
162
- </Badge>
163
- </TableCell>
164
- <TableCell>
165
- {formatCurrency(item.amount_cents, item.currency)}
166
- </TableCell>
167
- <TableCell>{item.currency}</TableCell>
168
- <TableCell>{formatDate(item.paid_at)}</TableCell>
169
- </TableRow>
170
- ))}
171
- </TableBody>
172
- </Table>
173
- </div>
142
+ </TableHeader>
143
+ <TableBody>
144
+ {items.map((item) => (
145
+ <TableRow key={item.id}>
146
+ <TableCell className="font-medium">{item.provider}</TableCell>
147
+ <TableCell>
148
+ <Badge className={badgeClass(item.method_type)}>
149
+ {item.method_type}
150
+ </Badge>
151
+ </TableCell>
152
+ <TableCell>
153
+ <Badge className={badgeClass(item.status)}>
154
+ {item.status}
155
+ </Badge>
156
+ </TableCell>
157
+ <TableCell>
158
+ {formatCurrency(item.amount_cents, item.currency)}
159
+ </TableCell>
160
+ <TableCell>{item.currency}</TableCell>
161
+ <TableCell>{formatDate(item.paid_at)}</TableCell>
162
+ </TableRow>
163
+ ))}
164
+ </TableBody>
165
+ </Table>
166
+ </div>
167
+ ) : (
168
+ <EmptyState
169
+ icon={<FileText className="h-12 w-12" />}
170
+ title={t('table.empty')}
171
+ description={t('description')}
172
+ actionLabel={t('emptyAction')}
173
+ onAction={() => {
174
+ setSearch('');
175
+ setPage(1);
176
+ void refetch();
177
+ }}
178
+ />
179
+ )}
174
180
 
175
181
  <PaginationFooter
176
182
  currentPage={page}