@umituz/web-dashboard 3.2.2 → 4.0.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/README.md +202 -14
- package/package.json +21 -29
- package/src/domains/analytics/components/AnalyticsCard.tsx +23 -21
- package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
- package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
- package/src/domains/analytics/components/MetricCard.tsx +42 -64
- package/src/domains/analytics/hooks/index.ts +7 -1
- package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
- package/src/domains/analytics/index.ts +11 -0
- package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
- package/src/domains/analytics/services/PerformanceService.ts +117 -69
- package/src/domains/analytics/types/analytics.ts +20 -6
- package/src/domains/analytics/types/index.ts +3 -0
- package/src/domains/analytics/utils/analytics.test.ts +183 -0
- package/src/domains/analytics/utils/analytics.ts +26 -9
- package/src/domains/analytics/utils/i18nKeys.ts +40 -0
- package/src/domains/analytics/utils/index.ts +2 -0
- package/src/domains/auth/components/AuthLayout.tsx +63 -54
- package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
- package/src/domains/auth/components/LoginForm.tsx +167 -226
- package/src/domains/auth/components/RegisterForm.tsx +202 -272
- package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
- package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
- package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
- package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
- package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
- package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
- package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
- package/src/domains/auth/components/shared/index.ts +20 -0
- package/src/domains/auth/hooks/index.ts +1 -0
- package/src/domains/auth/hooks/useAuth.ts +151 -232
- package/src/domains/auth/index.ts +8 -4
- package/src/domains/auth/types/auth.ts +26 -5
- package/src/domains/auth/types/index.ts +1 -0
- package/src/domains/auth/utils/auth.test.ts +182 -0
- package/src/domains/auth/utils/auth.ts +71 -130
- package/src/domains/auth/utils/i18nKeys.ts +127 -0
- package/src/domains/auth/utils/index.ts +7 -1
- package/src/domains/auth/utils/secureToken.ts +33 -0
- package/src/domains/auth/utils/validationKeys.ts +23 -0
- package/src/domains/calendar/hooks/useCalendar.ts +145 -151
- package/src/domains/calendar/index.ts +0 -1
- package/src/domains/calendar/services/CalendarService.ts +122 -179
- package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
- package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
- package/src/domains/calendar/utils/index.ts +33 -30
- package/src/domains/layouts/components/BrandLogo.tsx +12 -9
- package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
- package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
- package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
- package/src/domains/layouts/types/layout.ts +7 -1
- package/src/domains/layouts/types/user.ts +3 -1
- package/src/domains/modules/build.test.tsx +97 -0
- package/src/domains/modules/build.tsx +71 -0
- package/src/domains/modules/index.ts +2 -0
- package/src/domains/modules/types.ts +29 -0
- package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
- package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
- package/src/domains/onboarding/components/PlanStep.tsx +156 -123
- package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
- package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
- package/src/domains/onboarding/components/StepProgress.tsx +62 -0
- package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
- package/src/domains/onboarding/components/index.ts +4 -0
- package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
- package/src/domains/onboarding/index.ts +1 -0
- package/src/domains/onboarding/types/onboarding.ts +57 -17
- package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
- package/src/domains/onboarding/utils/index.ts +2 -0
- package/src/domains/permissions/PermissionsContext.tsx +103 -0
- package/src/domains/permissions/hasPermission.test.ts +50 -0
- package/src/domains/permissions/hasPermission.ts +38 -0
- package/src/domains/permissions/index.ts +19 -0
- package/src/domains/permissions/types.ts +35 -0
- package/src/domains/permissions/utils/i18nKeys.ts +11 -0
- package/src/domains/session/DemoLogin.tsx +50 -0
- package/src/domains/session/OrgSwitcher.tsx +110 -0
- package/src/domains/session/SessionContext.tsx +150 -0
- package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
- package/src/domains/session/index.ts +18 -0
- package/src/domains/session/types.ts +47 -0
- package/src/domains/session/utils/i18nKeys.ts +12 -0
- package/src/domains/settings/components/AuditLogList.tsx +59 -0
- package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
- package/src/domains/settings/components/SettingsLayout.tsx +75 -60
- package/src/domains/settings/components/SettingsSection.tsx +35 -22
- package/src/domains/settings/components/UserManagementTable.tsx +87 -0
- package/src/domains/settings/components/index.ts +10 -0
- package/src/domains/settings/hooks/useSettings.ts +77 -52
- package/src/domains/settings/types/settings.ts +2 -0
- package/src/domains/settings/utils/i18nKeys.ts +18 -0
- package/src/domains/settings/utils/settings.ts +11 -4
- package/src/i18n/I18nContext.tsx +33 -0
- package/src/i18n/defaultT.test.ts +43 -0
- package/src/i18n/defaultT.ts +34 -0
- package/src/i18n/index.ts +7 -0
- package/src/i18n/types.ts +13 -0
- package/src/index.ts +1 -7
- package/src/primitives/Button.tsx +30 -0
- package/src/primitives/Checkbox.tsx +40 -0
- package/src/primitives/Input.tsx +19 -0
- package/src/primitives/Skeleton.tsx +26 -0
- package/src/primitives/cn.ts +15 -0
- package/src/primitives/index.ts +23 -0
- package/src/styles/dashboard.css +3045 -0
- package/src/styles/tokens.contract.test.ts +66 -0
- package/src/styles/tokens.css +124 -0
- package/src/domains/billing/components/BillingLayout.tsx +0 -63
- package/src/domains/billing/components/BillingPage.tsx +0 -49
- package/src/domains/billing/components/BillingPortal.tsx +0 -198
- package/src/domains/billing/components/InvoiceCard.tsx +0 -143
- package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
- package/src/domains/billing/components/PlanComparison.tsx +0 -199
- package/src/domains/billing/components/UsageCard.tsx +0 -103
- package/src/domains/billing/components/index.ts +0 -13
- package/src/domains/billing/constants/billing.ts +0 -69
- package/src/domains/billing/hooks/index.ts +0 -7
- package/src/domains/billing/hooks/useBilling.ts +0 -385
- package/src/domains/billing/index.ts +0 -73
- package/src/domains/billing/types/billing.ts +0 -342
- package/src/domains/billing/types/index.ts +0 -30
- package/src/domains/billing/utils/billing.ts +0 -360
- package/src/domains/billing/utils/index.ts +0 -31
- package/src/domains/billing/utils/polar.ts +0 -63
- package/src/domains/calendar/components/index.ts +0 -7
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Payment Methods List Component
|
|
3
|
-
*
|
|
4
|
-
* Display and manage payment methods
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { CreditCard, Trash2, Check, Plus, Loader2 } from "lucide-react";
|
|
8
|
-
import { cn } from "@umituz/web-design-system/utils";
|
|
9
|
-
import { Button } from "@umituz/web-design-system/atoms";
|
|
10
|
-
import type { PaymentMethodsListProps } from "../types/billing";
|
|
11
|
-
import { formatCardNumber, formatExpiry } from "../utils/billing";
|
|
12
|
-
|
|
13
|
-
export const PaymentMethodsList = ({
|
|
14
|
-
paymentMethods,
|
|
15
|
-
loading = false,
|
|
16
|
-
onSetDefault,
|
|
17
|
-
onRemove,
|
|
18
|
-
onAddNew,
|
|
19
|
-
}: PaymentMethodsListProps) => {
|
|
20
|
-
if (loading) {
|
|
21
|
-
return (
|
|
22
|
-
<div className="flex items-center justify-center py-12">
|
|
23
|
-
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
24
|
-
</div>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (paymentMethods.length === 0) {
|
|
29
|
-
return (
|
|
30
|
-
<div className="text-center py-12">
|
|
31
|
-
<CreditCard className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
|
|
32
|
-
<p className="text-muted-foreground mb-6">No payment methods yet</p>
|
|
33
|
-
<Button onClick={onAddNew} variant="outline">
|
|
34
|
-
<Plus className="h-4 w-4 mr-2" />
|
|
35
|
-
Add Payment Method
|
|
36
|
-
</Button>
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div className="space-y-4">
|
|
43
|
-
{paymentMethods.map((method) => (
|
|
44
|
-
<div
|
|
45
|
-
key={method.id}
|
|
46
|
-
className={cn(
|
|
47
|
-
"flex items-center justify-between p-4 rounded-xl border",
|
|
48
|
-
method.isDefault
|
|
49
|
-
? "border-primary bg-primary/5"
|
|
50
|
-
: "border-border bg-background"
|
|
51
|
-
)}
|
|
52
|
-
>
|
|
53
|
-
<div className="flex items-center gap-4">
|
|
54
|
-
{/* Card Icon */}
|
|
55
|
-
<div className="w-12 h-8 rounded bg-gradient-to-br from-blue-600 to-purple-600 flex items-center justify-center">
|
|
56
|
-
{method.card && (
|
|
57
|
-
<CreditCard className="h-5 w-5 text-white" />
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
{/* Card Details */}
|
|
62
|
-
<div>
|
|
63
|
-
<p className="font-medium text-foreground">
|
|
64
|
-
{method.card && formatCardNumber(method.card.last4, method.card.brand)}
|
|
65
|
-
</p>
|
|
66
|
-
<p className="text-sm text-muted-foreground">
|
|
67
|
-
Expires {method.card && formatExpiry(method.card.expiryMonth, method.card.expiryYear)}
|
|
68
|
-
</p>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
{/* Default Badge */}
|
|
72
|
-
{method.isDefault && (
|
|
73
|
-
<span className="text-xs bg-primary text-primary-foreground px-2 py-1 rounded-full font-medium">
|
|
74
|
-
Default
|
|
75
|
-
</span>
|
|
76
|
-
)}
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
{/* Actions */}
|
|
80
|
-
<div className="flex items-center gap-2">
|
|
81
|
-
{!method.isDefault && onSetDefault && (
|
|
82
|
-
<Button
|
|
83
|
-
variant="ghost"
|
|
84
|
-
size="sm"
|
|
85
|
-
onClick={() => onSetDefault(method.id)}
|
|
86
|
-
className="text-muted-foreground hover:text-foreground"
|
|
87
|
-
>
|
|
88
|
-
<Check className="h-4 w-4" />
|
|
89
|
-
</Button>
|
|
90
|
-
)}
|
|
91
|
-
{onRemove && paymentMethods.length > 1 && (
|
|
92
|
-
<Button
|
|
93
|
-
variant="ghost"
|
|
94
|
-
size="sm"
|
|
95
|
-
onClick={() => onRemove(method.id)}
|
|
96
|
-
className="text-muted-foreground hover:text-destructive"
|
|
97
|
-
>
|
|
98
|
-
<Trash2 className="h-4 w-4" />
|
|
99
|
-
</Button>
|
|
100
|
-
)}
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
))}
|
|
104
|
-
|
|
105
|
-
{/* Add New Button */}
|
|
106
|
-
{onAddNew && (
|
|
107
|
-
<Button onClick={onAddNew} variant="outline" className="w-full">
|
|
108
|
-
<Plus className="h-4 w-4 mr-2" />
|
|
109
|
-
Add Payment Method
|
|
110
|
-
</Button>
|
|
111
|
-
)}
|
|
112
|
-
</div>
|
|
113
|
-
);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export default PaymentMethodsList;
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plan Comparison Component
|
|
3
|
-
*
|
|
4
|
-
* Configurable plan selection and comparison
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Check, Loader2 } from "lucide-react";
|
|
8
|
-
import { cn } from "@umituz/web-design-system/utils";
|
|
9
|
-
import { Button } from "@umituz/web-design-system/atoms";
|
|
10
|
-
import type { PlanComparisonProps } from "../types/billing";
|
|
11
|
-
import { formatPrice, getPlanPrice, calculateDiscount, isPopularPlan, formatFeature } from "../utils/billing";
|
|
12
|
-
|
|
13
|
-
export const PlanComparison = ({
|
|
14
|
-
plans,
|
|
15
|
-
selectedPlan,
|
|
16
|
-
cycle = "monthly",
|
|
17
|
-
showCycleToggle = true,
|
|
18
|
-
showFeatures = true,
|
|
19
|
-
onPlanSelect,
|
|
20
|
-
onCycleChange,
|
|
21
|
-
loading = false,
|
|
22
|
-
}: PlanComparisonProps) => {
|
|
23
|
-
const handleCycleChange = (newCycle: "monthly" | "yearly") => {
|
|
24
|
-
onCycleChange?.(newCycle);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const handleSelectPlan = (planId: string) => {
|
|
28
|
-
if (!loading) {
|
|
29
|
-
onPlanSelect?.(planId);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div className="w-full space-y-8">
|
|
35
|
-
{/* Billing Cycle Toggle */}
|
|
36
|
-
{showCycleToggle && (
|
|
37
|
-
<div className="flex items-center justify-center">
|
|
38
|
-
<div className="inline-flex items-center bg-muted rounded-full p-1">
|
|
39
|
-
<button
|
|
40
|
-
onClick={() => handleCycleChange("monthly")}
|
|
41
|
-
className={cn(
|
|
42
|
-
"px-6 py-2 rounded-full text-sm font-medium transition-all",
|
|
43
|
-
cycle === "monthly"
|
|
44
|
-
? "bg-background text-foreground shadow-sm"
|
|
45
|
-
: "text-muted-foreground hover:text-foreground"
|
|
46
|
-
)}
|
|
47
|
-
>
|
|
48
|
-
Monthly
|
|
49
|
-
</button>
|
|
50
|
-
<button
|
|
51
|
-
onClick={() => handleCycleChange("yearly")}
|
|
52
|
-
className={cn(
|
|
53
|
-
"px-6 py-2 rounded-full text-sm font-medium transition-all relative",
|
|
54
|
-
cycle === "yearly"
|
|
55
|
-
? "bg-background text-foreground shadow-sm"
|
|
56
|
-
: "text-muted-foreground hover:text-foreground"
|
|
57
|
-
)}
|
|
58
|
-
>
|
|
59
|
-
Yearly
|
|
60
|
-
<span className="ml-1 text-xs bg-primary text-primary-foreground px-2 py-0.5 rounded-full">
|
|
61
|
-
Save 17%
|
|
62
|
-
</span>
|
|
63
|
-
</button>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
)}
|
|
67
|
-
|
|
68
|
-
{/* Plans Grid */}
|
|
69
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
70
|
-
{plans.map((plan) => {
|
|
71
|
-
const isSelected = selectedPlan === plan.id;
|
|
72
|
-
const price = getPlanPrice(plan, cycle);
|
|
73
|
-
const isPopular = isPopularPlan(plan, plans);
|
|
74
|
-
const discount = calculateDiscount(plan.monthlyPrice, plan.yearlyPrice);
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<div
|
|
78
|
-
key={plan.id}
|
|
79
|
-
className={cn(
|
|
80
|
-
"relative bg-background border-2 rounded-2xl p-6 transition-all",
|
|
81
|
-
isSelected
|
|
82
|
-
? "border-primary shadow-lg shadow-primary/20"
|
|
83
|
-
: "border-border hover:border-primary/50",
|
|
84
|
-
isPopular && "border-primary shadow-lg shadow-primary/20"
|
|
85
|
-
)}
|
|
86
|
-
>
|
|
87
|
-
{/* Popular Badge */}
|
|
88
|
-
{isPopular && (
|
|
89
|
-
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
90
|
-
<span className="bg-primary text-primary-foreground text-xs font-bold px-4 py-1 rounded-full">
|
|
91
|
-
Most Popular
|
|
92
|
-
</span>
|
|
93
|
-
</div>
|
|
94
|
-
)}
|
|
95
|
-
|
|
96
|
-
{/* Plan Badge */}
|
|
97
|
-
{plan.badge && (
|
|
98
|
-
<div className="mb-4">
|
|
99
|
-
<span
|
|
100
|
-
className={cn(
|
|
101
|
-
"text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full",
|
|
102
|
-
plan.badgeColor || "bg-primary/10 text-primary"
|
|
103
|
-
)}
|
|
104
|
-
>
|
|
105
|
-
{plan.badge}
|
|
106
|
-
</span>
|
|
107
|
-
</div>
|
|
108
|
-
)}
|
|
109
|
-
|
|
110
|
-
{/* Plan Name */}
|
|
111
|
-
<h3 className="text-2xl font-bold text-foreground mb-2">
|
|
112
|
-
{plan.name}
|
|
113
|
-
</h3>
|
|
114
|
-
|
|
115
|
-
{/* Description */}
|
|
116
|
-
<p className="text-muted-foreground text-sm mb-6">
|
|
117
|
-
{plan.description}
|
|
118
|
-
</p>
|
|
119
|
-
|
|
120
|
-
{/* Price */}
|
|
121
|
-
<div className="mb-6">
|
|
122
|
-
<div className="flex items-baseline gap-1">
|
|
123
|
-
<span className="text-4xl font-extrabold text-foreground">
|
|
124
|
-
{formatPrice(price, plan.currency)}
|
|
125
|
-
</span>
|
|
126
|
-
<span className="text-muted-foreground">
|
|
127
|
-
/{cycle === "monthly" ? "mo" : "yr"}
|
|
128
|
-
</span>
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
{cycle === "yearly" && (
|
|
132
|
-
<p className="text-sm text-green-600 dark:text-green-500 mt-1">
|
|
133
|
-
Save {discount}% with yearly billing
|
|
134
|
-
</p>
|
|
135
|
-
)}
|
|
136
|
-
</div>
|
|
137
|
-
|
|
138
|
-
{/* Features */}
|
|
139
|
-
{showFeatures && plan.features.length > 0 && (
|
|
140
|
-
<ul className="space-y-3 mb-6 flex-1">
|
|
141
|
-
{plan.features.map((feature, index) => {
|
|
142
|
-
const { text, bold, included } = formatFeature(feature);
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<li
|
|
146
|
-
key={index}
|
|
147
|
-
className={cn(
|
|
148
|
-
"flex items-start gap-3 text-sm",
|
|
149
|
-
included === false && "opacity-50 line-through"
|
|
150
|
-
)}
|
|
151
|
-
>
|
|
152
|
-
<Check
|
|
153
|
-
className={cn(
|
|
154
|
-
"h-5 w-5 shrink-0 mt-0.5",
|
|
155
|
-
included === false
|
|
156
|
-
? "text-muted-foreground"
|
|
157
|
-
: "text-primary"
|
|
158
|
-
)}
|
|
159
|
-
/>
|
|
160
|
-
<span className={cn(bold && "font-semibold", "text-foreground")}>
|
|
161
|
-
{text}
|
|
162
|
-
</span>
|
|
163
|
-
</li>
|
|
164
|
-
);
|
|
165
|
-
})}
|
|
166
|
-
</ul>
|
|
167
|
-
)}
|
|
168
|
-
|
|
169
|
-
{/* Select Button */}
|
|
170
|
-
<Button
|
|
171
|
-
onClick={() => handleSelectPlan(plan.id)}
|
|
172
|
-
disabled={loading}
|
|
173
|
-
className={cn(
|
|
174
|
-
"w-full rounded-full py-6",
|
|
175
|
-
isSelected || isPopular
|
|
176
|
-
? "bg-primary text-primary-foreground hover:bg-primary/90"
|
|
177
|
-
: "bg-muted text-foreground hover:bg-muted/80"
|
|
178
|
-
)}
|
|
179
|
-
>
|
|
180
|
-
{loading ? (
|
|
181
|
-
<>
|
|
182
|
-
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
183
|
-
Processing...
|
|
184
|
-
</>
|
|
185
|
-
) : isSelected ? (
|
|
186
|
-
"Current Plan"
|
|
187
|
-
) : (
|
|
188
|
-
"Select Plan"
|
|
189
|
-
)}
|
|
190
|
-
</Button>
|
|
191
|
-
</div>
|
|
192
|
-
);
|
|
193
|
-
})}
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export default PlanComparison;
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Usage Card Component
|
|
3
|
-
*
|
|
4
|
-
* Display usage metrics with progress bar
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { AlertTriangle, TrendingUp } from "lucide-react";
|
|
8
|
-
import { cn } from "@umituz/web-design-system/utils";
|
|
9
|
-
import type { UsageCardProps } from "../types/billing";
|
|
10
|
-
import { calculateUsagePercentage, isNearLimit, formatNumber } from "../utils/billing";
|
|
11
|
-
|
|
12
|
-
export const UsageCard = ({
|
|
13
|
-
metric,
|
|
14
|
-
showProgress = true,
|
|
15
|
-
showLimit = true,
|
|
16
|
-
}: UsageCardProps) => {
|
|
17
|
-
const percentage = calculateUsagePercentage(metric);
|
|
18
|
-
const nearLimit = isNearLimit(metric);
|
|
19
|
-
const isOverLimit = metric.current > metric.limit;
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<div className="p-6 rounded-xl border border-border bg-background">
|
|
23
|
-
{/* Header */}
|
|
24
|
-
<div className="flex items-start justify-between mb-4">
|
|
25
|
-
<div>
|
|
26
|
-
<p className="text-sm text-muted-foreground mb-1">{metric.name}</p>
|
|
27
|
-
<p className="text-3xl font-bold text-foreground">
|
|
28
|
-
{formatNumber(metric.current)}
|
|
29
|
-
<span className="text-base font-normal text-muted-foreground ml-1">
|
|
30
|
-
{metric.unit}
|
|
31
|
-
</span>
|
|
32
|
-
</p>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
{/* Warning Icon */}
|
|
36
|
-
{(nearLimit || isOverLimit) && (
|
|
37
|
-
<div
|
|
38
|
-
className={cn(
|
|
39
|
-
"w-10 h-10 rounded-lg flex items-center justify-center",
|
|
40
|
-
isOverLimit
|
|
41
|
-
? "bg-destructive/10 text-destructive"
|
|
42
|
-
: "bg-orange-500/10 text-orange-600 dark:text-orange-500"
|
|
43
|
-
)}
|
|
44
|
-
>
|
|
45
|
-
{isOverLimit ? (
|
|
46
|
-
<AlertTriangle className="h-5 w-5" />
|
|
47
|
-
) : (
|
|
48
|
-
<TrendingUp className="h-5 w-5" />
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
)}
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
{/* Progress Bar */}
|
|
55
|
-
{showProgress && (
|
|
56
|
-
<div className="space-y-2">
|
|
57
|
-
<div className="flex items-center justify-between text-xs">
|
|
58
|
-
{showLimit && (
|
|
59
|
-
<span className="text-muted-foreground">
|
|
60
|
-
{formatNumber(metric.limit)} {metric.unit} limit
|
|
61
|
-
</span>
|
|
62
|
-
)}
|
|
63
|
-
<span
|
|
64
|
-
className={cn(
|
|
65
|
-
"font-medium",
|
|
66
|
-
isOverLimit
|
|
67
|
-
? "text-destructive"
|
|
68
|
-
: nearLimit
|
|
69
|
-
? "text-orange-600 dark:text-orange-500"
|
|
70
|
-
: "text-foreground"
|
|
71
|
-
)}
|
|
72
|
-
>
|
|
73
|
-
{percentage.toFixed(0)}%
|
|
74
|
-
</span>
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<div className="w-full h-2 bg-muted rounded-full overflow-hidden">
|
|
78
|
-
<div
|
|
79
|
-
className={cn(
|
|
80
|
-
"h-full transition-all duration-500",
|
|
81
|
-
isOverLimit
|
|
82
|
-
? "bg-destructive"
|
|
83
|
-
: nearLimit
|
|
84
|
-
? "bg-orange-500"
|
|
85
|
-
: "bg-primary"
|
|
86
|
-
)}
|
|
87
|
-
style={{ width: `${Math.min(percentage, 100)}%` }}
|
|
88
|
-
/>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
{/* Reset Info */}
|
|
92
|
-
{metric.resetAt && (
|
|
93
|
-
<p className="text-xs text-muted-foreground mt-2">
|
|
94
|
-
Resets {new Date(metric.resetAt).toLocaleDateString()}
|
|
95
|
-
</p>
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
98
|
-
)}
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export default UsageCard;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Billing Components
|
|
3
|
-
*
|
|
4
|
-
* Export all billing components
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export { PlanComparison } from "./PlanComparison";
|
|
8
|
-
export { PaymentMethodsList } from "./PaymentMethodsList";
|
|
9
|
-
export { InvoiceCard } from "./InvoiceCard";
|
|
10
|
-
export { UsageCard } from "./UsageCard";
|
|
11
|
-
export { BillingPortal } from "./BillingPortal";
|
|
12
|
-
export { BillingLayout } from "./BillingLayout";
|
|
13
|
-
export { BillingPage } from "./BillingPage";
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Billing Constants
|
|
3
|
-
*
|
|
4
|
-
* Centralized constants for billing domain to avoid hardcoded strings
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Subscription status constants
|
|
9
|
-
*/
|
|
10
|
-
export const SUBSCRIPTION_STATUS = {
|
|
11
|
-
ACTIVE: "active",
|
|
12
|
-
TRIALING: "trialing",
|
|
13
|
-
PAST_DUE: "past_due",
|
|
14
|
-
CANCELED: "canceled",
|
|
15
|
-
UNPAID: "unpaid",
|
|
16
|
-
INCOMPLETE: "incomplete",
|
|
17
|
-
REVOKED: "revoked",
|
|
18
|
-
} as const;
|
|
19
|
-
|
|
20
|
-
export type SubscriptionStatus = typeof SUBSCRIPTION_STATUS[keyof typeof SUBSCRIPTION_STATUS];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Invoice status constants
|
|
24
|
-
*/
|
|
25
|
-
export const INVOICE_STATUS = {
|
|
26
|
-
DRAFT: "draft",
|
|
27
|
-
OPEN: "open",
|
|
28
|
-
PAID: "paid",
|
|
29
|
-
VOID: "void",
|
|
30
|
-
UNCOLLECTIBLE: "uncollectible",
|
|
31
|
-
REFUNDED: "refunded",
|
|
32
|
-
} as const;
|
|
33
|
-
|
|
34
|
-
export type InvoiceStatus = typeof INVOICE_STATUS[keyof typeof INVOICE_STATUS];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Billing cycle constants
|
|
38
|
-
*/
|
|
39
|
-
export const BILLING_CYCLE = {
|
|
40
|
-
MONTHLY: "monthly",
|
|
41
|
-
YEARLY: "yearly",
|
|
42
|
-
} as const;
|
|
43
|
-
|
|
44
|
-
export type BillingCycle = typeof BILLING_CYCLE[keyof typeof BILLING_CYCLE];
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Currency constants
|
|
48
|
-
*/
|
|
49
|
-
export const CURRENCY = {
|
|
50
|
-
USD: "USD",
|
|
51
|
-
EUR: "EUR",
|
|
52
|
-
GBP: "GBP",
|
|
53
|
-
TRY: "TRY",
|
|
54
|
-
JPY: "JPY",
|
|
55
|
-
} as const;
|
|
56
|
-
|
|
57
|
-
export type Currency = typeof CURRENCY[keyof typeof CURRENCY];
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Plan type constants
|
|
61
|
-
*/
|
|
62
|
-
export const PLAN_TYPE = {
|
|
63
|
-
STANDARD: "standard",
|
|
64
|
-
PRO: "pro",
|
|
65
|
-
BUSINESS: "business",
|
|
66
|
-
ENTERPRISE: "enterprise",
|
|
67
|
-
} as const;
|
|
68
|
-
|
|
69
|
-
export type PlanType = typeof PLAN_TYPE[keyof typeof PLAN_TYPE];
|