@umituz/web-dashboard 2.2.0 → 2.4.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.
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Billing Types
3
+ *
4
+ * Type definitions for billing and subscription system
5
+ */
6
+
7
+ import type { ComponentType, ReactElement } from "react";
8
+
9
+ /**
10
+ * Billing cycle
11
+ */
12
+ export type BillingCycle = "monthly" | "yearly";
13
+
14
+ /**
15
+ * Subscription status
16
+ */
17
+ export type SubscriptionStatus =
18
+ | "active"
19
+ | "trialing"
20
+ | "past_due"
21
+ | "canceled"
22
+ | "unpaid"
23
+ | "incomplete";
24
+
25
+ /**
26
+ * Plan type
27
+ */
28
+ export type PlanType = "free" | "basic" | "pro" | "enterprise" | "custom";
29
+
30
+ /**
31
+ * Currency
32
+ */
33
+ export type Currency = "USD" | "EUR" | "GBP" | "TRY" | "JPY";
34
+
35
+ /**
36
+ * Plan pricing tier
37
+ */
38
+ export interface PlanTier {
39
+ /** Plan ID */
40
+ id: string;
41
+ /** Plan type */
42
+ type: PlanType;
43
+ /** Plan name */
44
+ name: string;
45
+ /** Plan description */
46
+ description: string;
47
+ /** Badge text */
48
+ badge?: string;
49
+ /** Badge color */
50
+ badgeColor?: string;
51
+ /** Monthly price */
52
+ monthlyPrice: number;
53
+ /** Yearly price */
54
+ yearlyPrice: number;
55
+ /** Currency */
56
+ currency: Currency;
57
+ /** Features list */
58
+ features: (string | { text: string; bold?: boolean; included?: boolean })[];
59
+ /** Highlight/recommend */
60
+ highlight?: boolean;
61
+ /** Maximum users/seats */
62
+ maxUsers?: number;
63
+ /** Storage limit in GB */
64
+ storageLimit?: number;
65
+ /** API call limit */
66
+ apiLimit?: number;
67
+ }
68
+
69
+ /**
70
+ * Subscription info
71
+ */
72
+ export interface Subscription {
73
+ /** Subscription ID */
74
+ id: string;
75
+ /** Plan ID */
76
+ planId: string;
77
+ /** Plan details */
78
+ plan: PlanTier;
79
+ /** Subscription status */
80
+ status: SubscriptionStatus;
81
+ /** Billing cycle */
82
+ cycle: BillingCycle;
83
+ /** Current period start */
84
+ currentPeriodStart: string;
85
+ /** Current period end */
86
+ currentPeriodEnd: string;
87
+ /** Cancel at period end */
88
+ cancelAtPeriodEnd?: boolean;
89
+ /** Trial end date */
90
+ trialEnd?: string;
91
+ /** User/seat count */
92
+ seats?: number;
93
+ }
94
+
95
+ /**
96
+ * Payment method type
97
+ */
98
+ export type PaymentMethodType = "card" | "bank_account" | "wallet";
99
+
100
+ /**
101
+ * Payment method
102
+ */
103
+ export interface PaymentMethod {
104
+ /** Payment method ID */
105
+ id: string;
106
+ /** Type */
107
+ type: PaymentMethodType;
108
+ /** Is default */
109
+ isDefault: boolean;
110
+ /** Card details */
111
+ card?: {
112
+ /** Last 4 digits */
113
+ last4: string;
114
+ /** Brand (Visa, Mastercard) */
115
+ brand: string;
116
+ /** Expiry month */
117
+ expiryMonth: number;
118
+ /** Expiry year */
119
+ expiryYear: number;
120
+ /** Cardholder name */
121
+ name?: string;
122
+ };
123
+ /** Bank account details */
124
+ bankAccount?: {
125
+ /** Last 4 digits */
126
+ last4: string;
127
+ /** Bank name */
128
+ bankName: string;
129
+ /** Account type */
130
+ accountType?: "checking" | "savings";
131
+ };
132
+ /** Created date */
133
+ createdAt: string;
134
+ }
135
+
136
+ /**
137
+ * Invoice status
138
+ */
139
+ export type InvoiceStatus = "draft" | "open" | "paid" | "void" | "uncollectible";
140
+
141
+ /**
142
+ * Invoice item
143
+ */
144
+ export interface InvoiceItem {
145
+ /** Item description */
146
+ description: string;
147
+ /** Quantity */
148
+ quantity: number;
149
+ /** Unit price */
150
+ unitPrice: number;
151
+ /** Amount */
152
+ amount: number;
153
+ }
154
+
155
+ /**
156
+ * Invoice
157
+ */
158
+ export interface Invoice {
159
+ /** Invoice ID */
160
+ id: string;
161
+ /** Invoice number */
162
+ number: string;
163
+ /** Amount */
164
+ amount: number;
165
+ /** Currency */
166
+ currency: Currency;
167
+ /** Status */
168
+ status: InvoiceStatus;
169
+ /** Invoice date */
170
+ date: string;
171
+ /** Due date */
172
+ dueDate: string;
173
+ /** Paid date */
174
+ paidAt?: string;
175
+ /** Invoice URL */
176
+ invoiceUrl?: string;
177
+ /** PDF download URL */
178
+ pdfUrl?: string;
179
+ /** Line items */
180
+ items?: InvoiceItem[];
181
+ /** Subtotal */
182
+ subtotal?: number;
183
+ /** Tax amount */
184
+ tax?: number;
185
+ /** Total amount */
186
+ total?: number;
187
+ }
188
+
189
+ /**
190
+ * Usage metric
191
+ */
192
+ export interface UsageMetric {
193
+ /** Metric ID */
194
+ id: string;
195
+ /** Metric name */
196
+ name: string;
197
+ /** Current usage */
198
+ current: number;
199
+ /** Limit */
200
+ limit: number;
201
+ /** Unit */
202
+ unit: string;
203
+ /** Reset period */
204
+ resetPeriod: "daily" | "monthly" | "yearly";
205
+ /** Reset date */
206
+ resetAt?: string;
207
+ }
208
+
209
+ /**
210
+ * Billing summary
211
+ */
212
+ export interface BillingSummary {
213
+ /** Current subscription */
214
+ subscription: Subscription;
215
+ /** Payment methods */
216
+ paymentMethods: PaymentMethod[];
217
+ /** Default payment method */
218
+ defaultPaymentMethod?: PaymentMethod;
219
+ /** Upcoming invoice */
220
+ upcomingInvoice?: {
221
+ amount: number;
222
+ currency: Currency;
223
+ date: string;
224
+ };
225
+ /** Usage metrics */
226
+ usage: UsageMetric[];
227
+ /** Recent invoices */
228
+ recentInvoices: Invoice[];
229
+ }
230
+
231
+ /**
232
+ * Plan comparison props
233
+ */
234
+ export interface PlanComparisonProps {
235
+ /** Available plans */
236
+ plans: PlanTier[];
237
+ /** Selected plan ID */
238
+ selectedPlan?: string;
239
+ /** Billing cycle */
240
+ cycle: BillingCycle;
241
+ /** Show yearly toggle */
242
+ showCycleToggle?: boolean;
243
+ /** Show features */
244
+ showFeatures?: boolean;
245
+ /** On plan select */
246
+ onPlanSelect?: (planId: string) => void;
247
+ /** On cycle change */
248
+ onCycleChange?: (cycle: BillingCycle) => void;
249
+ /** Loading state */
250
+ loading?: boolean;
251
+ }
252
+
253
+ /**
254
+ * Payment methods list props
255
+ */
256
+ export interface PaymentMethodsListProps {
257
+ /** Payment methods */
258
+ paymentMethods: PaymentMethod[];
259
+ /** Loading state */
260
+ loading?: boolean;
261
+ /** On set default */
262
+ onSetDefault?: (methodId: string) => void;
263
+ /** On remove */
264
+ onRemove?: (methodId: string) => void;
265
+ /** On add new */
266
+ onAddNew?: () => void;
267
+ }
268
+
269
+ /**
270
+ * Invoice card props
271
+ */
272
+ export interface InvoiceCardProps {
273
+ /** Invoice data */
274
+ invoice: Invoice;
275
+ /** Compact view */
276
+ compact?: boolean;
277
+ /** On click */
278
+ onClick?: (invoice: Invoice) => void;
279
+ }
280
+
281
+ /**
282
+ * Usage card props
283
+ */
284
+ export interface UsageCardProps {
285
+ /** Usage metric */
286
+ metric: UsageMetric;
287
+ /** Show progress bar */
288
+ showProgress?: boolean;
289
+ /** Show limit */
290
+ showLimit?: boolean;
291
+ }
292
+
293
+ /**
294
+ * Billing portal props
295
+ */
296
+ export interface BillingPortalProps {
297
+ /** Billing summary */
298
+ billing: BillingSummary;
299
+ /** Loading state */
300
+ loading?: boolean;
301
+ /** Error message */
302
+ error?: string;
303
+ /** Show tabs */
304
+ showTabs?: boolean;
305
+ /** Active tab */
306
+ activeTab?: string;
307
+ /** On tab change */
308
+ onTabChange?: (tab: string) => void;
309
+ }
310
+
311
+ /**
312
+ * Billing layout props
313
+ */
314
+ export interface BillingLayoutProps {
315
+ /** Billing configuration */
316
+ config: BillingConfig;
317
+ /** Children content */
318
+ children?: React.ReactNode;
319
+ }
320
+
321
+ /**
322
+ * Billing configuration
323
+ */
324
+ export interface BillingConfig {
325
+ /** Brand name */
326
+ brandName: string;
327
+ /** Available plans */
328
+ plans: PlanTier[];
329
+ /** Supported currencies */
330
+ currencies?: Currency[];
331
+ /** Default currency */
332
+ defaultCurrency?: Currency;
333
+ /** Enable yearly discounts */
334
+ enableYearlyDiscount?: boolean;
335
+ /** Yearly discount percentage */
336
+ yearlyDiscount?: number;
337
+ /** Tax rate */
338
+ taxRate?: number;
339
+ /** Support email */
340
+ supportEmail?: string;
341
+ /** Cancel subscription route */
342
+ cancelRoute?: string;
343
+ /** Update payment route */
344
+ updatePaymentRoute?: string;
345
+ /** Invoice history route */
346
+ invoiceHistoryRoute?: string;
347
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Billing Types
3
+ *
4
+ * Export all billing-related types
5
+ */
6
+
7
+ export type {
8
+ BillingCycle,
9
+ SubscriptionStatus,
10
+ PlanType,
11
+ Currency,
12
+ PlanTier,
13
+ Subscription,
14
+ PaymentMethodType,
15
+ PaymentMethod,
16
+ InvoiceStatus,
17
+ InvoiceItem,
18
+ Invoice,
19
+ UsageMetric,
20
+ BillingSummary,
21
+ PlanComparisonProps,
22
+ PaymentMethodsListProps,
23
+ InvoiceCardProps,
24
+ UsageCardProps,
25
+ BillingPortalProps,
26
+ BillingLayoutProps,
27
+ BillingConfig,
28
+ } from "./billing";
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Billing Utilities
3
+ *
4
+ * Helper functions for billing operations
5
+ */
6
+
7
+ import type {
8
+ BillingCycle,
9
+ Currency,
10
+ PlanTier,
11
+ Subscription,
12
+ PaymentMethod,
13
+ Invoice,
14
+ InvoiceStatus,
15
+ UsageMetric,
16
+ } from "../types/billing";
17
+
18
+ /**
19
+ * Format price with currency
20
+ *
21
+ * @param amount - Amount to format
22
+ * @param currency - Currency code (default: USD)
23
+ * @returns Formatted price string
24
+ */
25
+ export function formatPrice(amount: number, currency: Currency = "USD"): string {
26
+ const localeMap: Record<Currency, string> = {
27
+ USD: "en-US",
28
+ EUR: "de-DE",
29
+ GBP: "en-GB",
30
+ TRY: "tr-TR",
31
+ JPY: "ja-JP",
32
+ };
33
+
34
+ return new Intl.NumberFormat(localeMap[currency], {
35
+ style: "currency",
36
+ currency,
37
+ }).format(amount);
38
+ }
39
+
40
+ /**
41
+ * Calculate yearly discount
42
+ *
43
+ * @param monthlyPrice - Monthly price
44
+ * @param yearlyPrice - Yearly price
45
+ * @returns Discount percentage
46
+ */
47
+ export function calculateDiscount(monthlyPrice: number, yearlyPrice: number): number {
48
+ const yearlyMonthly = yearlyPrice / 12;
49
+ const discount = ((monthlyPrice - yearlyMonthly) / monthlyPrice) * 100;
50
+ return Math.round(discount);
51
+ }
52
+
53
+ /**
54
+ * Calculate prorated amount
55
+ *
56
+ * @param amount - Full amount
57
+ * @param daysUsed - Days used in period
58
+ * @param totalDays - Total days in period
59
+ * @returns Prorated amount
60
+ */
61
+ export function calculateProratedAmount(
62
+ amount: number,
63
+ daysUsed: number,
64
+ totalDays: number
65
+ ): number {
66
+ if (totalDays === 0) return 0;
67
+ return (amount / totalDays) * daysUsed;
68
+ }
69
+
70
+ /**
71
+ * Get plan price by cycle
72
+ *
73
+ * @param plan - Plan tier
74
+ * @param cycle - Billing cycle
75
+ * @returns Price for cycle
76
+ */
77
+ export function getPlanPrice(plan: PlanTier, cycle: BillingCycle): number {
78
+ return cycle === "monthly" ? plan.monthlyPrice : plan.yearlyPrice;
79
+ }
80
+
81
+ /**
82
+ * Calculate usage percentage
83
+ *
84
+ * @param metric - Usage metric
85
+ * @returns Usage percentage (0-100)
86
+ */
87
+ export function calculateUsagePercentage(metric: UsageMetric): number {
88
+ if (metric.limit === 0) return 0;
89
+ return Math.min((metric.current / metric.limit) * 100, 100);
90
+ }
91
+
92
+ /**
93
+ * Check if usage is near limit
94
+ *
95
+ * @param metric - Usage metric
96
+ * @param threshold - Warning threshold (default: 80)
97
+ * @returns Whether near limit
98
+ */
99
+ export function isNearLimit(metric: UsageMetric, threshold: number = 80): boolean {
100
+ return calculateUsagePercentage(metric) >= threshold;
101
+ }
102
+
103
+ /**
104
+ * Get subscription status color
105
+ *
106
+ * @param status - Subscription status
107
+ * @returns Color class
108
+ */
109
+ export function getStatusColor(status: SubscriptionStatus): string {
110
+ const colorMap: Record<SubscriptionStatus, string> = {
111
+ active: "text-green-600 dark:text-green-500",
112
+ trialing: "text-blue-600 dark:text-blue-500",
113
+ past_due: "text-orange-600 dark:text-orange-500",
114
+ canceled: "text-gray-600 dark:text-gray-500",
115
+ unpaid: "text-red-600 dark:text-red-500",
116
+ incomplete: "text-yellow-600 dark:text-yellow-500",
117
+ };
118
+
119
+ return colorMap[status] || "text-gray-600";
120
+ }
121
+
122
+ /**
123
+ * Get subscription status label
124
+ *
125
+ * @param status - Subscription status
126
+ * @returns Human readable label
127
+ */
128
+ export function getStatusLabel(status: SubscriptionStatus): string {
129
+ const labelMap: Record<SubscriptionStatus, string> = {
130
+ active: "Active",
131
+ trialing: "Trial",
132
+ past_due: "Past Due",
133
+ canceled: "Canceled",
134
+ unpaid: "Unpaid",
135
+ incomplete: "Incomplete",
136
+ };
137
+
138
+ return labelMap[status] || status;
139
+ }
140
+
141
+ /**
142
+ * Get invoice status color
143
+ *
144
+ * @param status - Invoice status
145
+ * @returns Color class
146
+ */
147
+ export function getInvoiceStatusColor(status: InvoiceStatus): string {
148
+ const colorMap: Record<InvoiceStatus, string> = {
149
+ draft: "text-gray-600 dark:text-gray-500",
150
+ open: "text-orange-600 dark:text-orange-500",
151
+ paid: "text-green-600 dark:text-green-500",
152
+ void: "text-gray-600 dark:text-gray-500",
153
+ uncollectible: "text-red-600 dark:text-red-500",
154
+ };
155
+
156
+ return colorMap[status] || "text-gray-600";
157
+ }
158
+
159
+ /**
160
+ * Get invoice status label
161
+ *
162
+ * @param status - Invoice status
163
+ * @returns Human readable label
164
+ */
165
+ export function getInvoiceStatusLabel(status: InvoiceStatus): string {
166
+ const labelMap: Record<InvoiceStatus, string> = {
167
+ draft: "Draft",
168
+ open: "Open",
169
+ paid: "Paid",
170
+ void: "Void",
171
+ uncollectible: "Uncollectible",
172
+ };
173
+
174
+ return labelMap[status] || status;
175
+ }
176
+
177
+ /**
178
+ * Format card number
179
+ *
180
+ * @param last4 - Last 4 digits
181
+ * @param brand - Card brand
182
+ * @returns Formatted card display
183
+ */
184
+ export function formatCardNumber(last4: string, brand: string): string {
185
+ return `${brand.toUpperCase()} •••• ${last4}`;
186
+ }
187
+
188
+ /**
189
+ * Format expiry date
190
+ *
191
+ * @param month - Expiry month
192
+ * @param year - Expiry year
193
+ * @returns Formatted expiry (MM/YY)
194
+ */
195
+ export function formatExpiry(month: number, year: number): string {
196
+ return `${String(month).padStart(2, "0")}/${String(year).slice(-2)}`;
197
+ }
198
+
199
+ /**
200
+ * Calculate remaining days
201
+ *
202
+ * @param endDate - End date string
203
+ * @returns Days remaining
204
+ */
205
+ export function getDaysRemaining(endDate: string): number {
206
+ const end = new Date(endDate);
207
+ const now = new Date();
208
+ const diff = end.getTime() - now.getTime();
209
+ return Math.max(0, Math.ceil(diff / (1000 * 60 * 60 * 24)));
210
+ }
211
+
212
+ /**
213
+ * Check if trial is expiring soon
214
+ *
215
+ * @param trialEnd - Trial end date
216
+ * @param daysThreshold - Days threshold (default: 7)
217
+ * @returns Whether trial is expiring soon
218
+ */
219
+ export function isTrialExpiringSoon(trialEnd: string, daysThreshold: number = 7): boolean {
220
+ const daysRemaining = getDaysRemaining(trialEnd);
221
+ return daysRemaining > 0 && daysRemaining <= daysThreshold;
222
+ }
223
+
224
+ /**
225
+ * Calculate next billing date
226
+ *
227
+ * @param currentPeriodEnd - Current period end
228
+ * @param cycle - Billing cycle
229
+ * @returns Next billing date
230
+ */
231
+ export function getNextBillingDate(
232
+ currentPeriodEnd: string,
233
+ cycle: BillingCycle
234
+ ): Date {
235
+ const nextDate = new Date(currentPeriodEnd);
236
+
237
+ if (cycle === "monthly") {
238
+ nextDate.setMonth(nextDate.getMonth() + 1);
239
+ } else {
240
+ nextDate.setFullYear(nextDate.getFullYear() + 1);
241
+ }
242
+
243
+ return nextDate;
244
+ }
245
+
246
+ /**
247
+ * Group invoices by status
248
+ *
249
+ * @param invoices - Array of invoices
250
+ * @returns Grouped invoices map
251
+ */
252
+ export function groupInvoicesByStatus(
253
+ invoices: Invoice[]
254
+ ): Record<InvoiceStatus, Invoice[]> {
255
+ const grouped: Record<string, Invoice[]> = {
256
+ draft: [],
257
+ open: [],
258
+ paid: [],
259
+ void: [],
260
+ uncollectible: [],
261
+ };
262
+
263
+ invoices.forEach((invoice) => {
264
+ if (grouped[invoice.status]) {
265
+ grouped[invoice.status].push(invoice);
266
+ }
267
+ });
268
+
269
+ return grouped as Record<InvoiceStatus, Invoice[]>;
270
+ }
271
+
272
+ /**
273
+ * Calculate total invoice amount
274
+ *
275
+ * @param invoices - Array of invoices
276
+ * @param status - Optional status filter
277
+ * @returns Total amount
278
+ */
279
+ export function calculateInvoiceTotal(
280
+ invoices: Invoice[],
281
+ status?: InvoiceStatus
282
+ ): number {
283
+ const filtered = status ? invoices.filter((inv) => inv.status === status) : invoices;
284
+ return filtered.reduce((sum, inv) => sum + inv.amount, 0);
285
+ }
286
+
287
+ /**
288
+ * Sort invoices by date
289
+ *
290
+ * @param invoices - Array of invoices
291
+ * @param order - Sort order (default: desc)
292
+ * @returns Sorted invoices
293
+ */
294
+ export function sortInvoicesByDate(
295
+ invoices: Invoice[],
296
+ order: "asc" | "desc" = "desc"
297
+ ): Invoice[] {
298
+ return [...invoices].sort((a, b) => {
299
+ const dateA = new Date(a.date).getTime();
300
+ const dateB = new Date(b.date).getTime();
301
+ return order === "asc" ? dateA - dateB : dateB - dateB;
302
+ });
303
+ }
304
+
305
+ /**
306
+ * Format feature list item
307
+ *
308
+ * @param feature - Feature item (string or object)
309
+ * @returns Formatted feature
310
+ */
311
+ export function formatFeature(
312
+ feature: string | { text: string; bold?: boolean; included?: boolean }
313
+ ): { text: string; bold?: boolean; included?: boolean } {
314
+ if (typeof feature === "string") {
315
+ return { text: feature, included: true };
316
+ }
317
+ return feature;
318
+ }
319
+
320
+ /**
321
+ * Check if plan is popular
322
+ *
323
+ * @param plan - Plan tier
324
+ * @param plans - All available plans
325
+ * @returns Whether plan is popular (middle tier)
326
+ */
327
+ export function isPopularPlan(plan: PlanTier, plans: PlanTier[]): boolean {
328
+ const middleIndex = Math.floor(plans.length / 2);
329
+ return plans[middleIndex]?.id === plan.id;
330
+ }
331
+
332
+ /**
333
+ * Generate trial days text
334
+ *
335
+ * @param trialEnd - Trial end date
336
+ * @returns Days remaining text
337
+ */
338
+ export function getTrialDaysText(trialEnd: string): string {
339
+ const days = getDaysRemaining(trialEnd);
340
+
341
+ if (days === 0) return "Trial ends today";
342
+ if (days === 1) return "1 day left";
343
+ return `${days} days left`;
344
+ }