@ttoss/react-billing 0.5.12 → 0.5.14

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/index.cjs ADDED
@@ -0,0 +1,834 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ Object.defineProperty(exports, Symbol.toStringTag, {
3
+ value: 'Module'
4
+ });
5
+ //#region \0rolldown/runtime.js
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
15
+ key = keys[i];
16
+ if (!__hasOwnProp.call(to, key) && key !== except) {
17
+ __defProp(to, key, {
18
+ get: (k => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ }
23
+ }
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
+ value: mod,
28
+ enumerable: true
29
+ }) : target, mod));
30
+
31
+ //#endregion
32
+ let _ttoss_ui = require("@ttoss/ui");
33
+ let react_jsx_runtime = require("react/jsx-runtime");
34
+ let _ttoss_react_icons = require("@ttoss/react-icons");
35
+ let react = require("react");
36
+ react = __toESM(react);
37
+ let _ttoss_components_EnhancedTitle = require("@ttoss/components/EnhancedTitle");
38
+ let _ttoss_components_MetricCard = require("@ttoss/components/MetricCard");
39
+
40
+ //#region src/components/planCard/PlanCardCtaSlot.tsx
41
+ const PlanCardCtaSlot = ({
42
+ buttonProps
43
+ }) => {
44
+ const {
45
+ label: ctaLabel = "Assine agora",
46
+ sx: buttonSx,
47
+ leftIcon,
48
+ variant: buttonVariant,
49
+ ...restButtonProps
50
+ } = buttonProps ?? {};
51
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
52
+ sx: {
53
+ marginTop: "auto",
54
+ paddingY: "2",
55
+ paddingX: "6",
56
+ width: "full",
57
+ justifyContent: "center"
58
+ },
59
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Button, {
60
+ ...restButtonProps,
61
+ variant: buttonVariant ?? "accent",
62
+ leftIcon,
63
+ sx: {
64
+ width: "full",
65
+ justifyContent: "center",
66
+ fontWeight: "semibold",
67
+ ...buttonSx
68
+ },
69
+ children: ctaLabel
70
+ })
71
+ });
72
+ };
73
+
74
+ //#endregion
75
+ //#region src/components/planCard/PlanCardVariants.ts
76
+ const normalizeVariant = variantType => {
77
+ if (variantType === "default") return "primary";
78
+ if (variantType === "enterprise") return "secondary";
79
+ return variantType ?? "primary";
80
+ };
81
+ /**
82
+ * PlanCard variants follow the same pattern as `Tooltip` variants:
83
+ * a simple mapping from `variantType` to a small set of style tokens.
84
+ */
85
+ const getPlanCardVariantStyles = variantType => {
86
+ const variant = normalizeVariant(variantType);
87
+ const variants = {
88
+ primary: {
89
+ backgroundColor: "display.background.primary.default",
90
+ color: "display.text.primary.default",
91
+ secondaryColor: "display.text.secondary.default",
92
+ borderColor: "display.border.muted.default",
93
+ positiveColor: "feedback.text.positive.default"
94
+ },
95
+ secondary: {
96
+ backgroundColor: "display.background.primary.active",
97
+ color: "action.text.primary.default",
98
+ secondaryColor: "action.text.primary.default",
99
+ borderColor: "display.border.muted.default",
100
+ positiveColor: "action.text.primary.default"
101
+ }
102
+ };
103
+ return variants[variant] ?? variants.primary;
104
+ };
105
+
106
+ //#endregion
107
+ //#region src/components/planCard/PlanCardFeaturesSlot.tsx
108
+ const featuresTitle = "RECURSOS";
109
+ const PlanCardFeaturesSlot = ({
110
+ features,
111
+ variant = "primary"
112
+ }) => {
113
+ const variantStyles = getPlanCardVariantStyles(variant);
114
+ const featurePositiveColor = variantStyles.positiveColor;
115
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Box, {
116
+ sx: {
117
+ paddingY: "4",
118
+ paddingX: "6",
119
+ width: "full",
120
+ borderTop: "md",
121
+ borderBottom: "md",
122
+ borderColor: "display.border.muted.default"
123
+ },
124
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Stack, {
125
+ sx: {
126
+ gap: "5",
127
+ paddingY: "3"
128
+ },
129
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
130
+ sx: {
131
+ letterSpacing: "widest",
132
+ color: variantStyles.color
133
+ },
134
+ children: featuresTitle
135
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
136
+ sx: {
137
+ flexDirection: "column",
138
+ gap: "3"
139
+ },
140
+ children: features.map((feature, index) => {
141
+ if (react.isValidElement(feature)) return /* @__PURE__ */(0, react_jsx_runtime.jsx)(react.Fragment, {
142
+ children: feature
143
+ }, index);
144
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
145
+ sx: {
146
+ fontSize: "sm",
147
+ color: featurePositiveColor,
148
+ alignItems: "center",
149
+ gap: "3"
150
+ },
151
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_react_icons.Icon, {
152
+ icon: "fluent:checkmark-24-filled"
153
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
154
+ sx: {
155
+ fontSize: "sm",
156
+ color: featurePositiveColor,
157
+ alignItems: "center"
158
+ },
159
+ children: String(feature)
160
+ })]
161
+ }, index);
162
+ })
163
+ })]
164
+ })
165
+ });
166
+ };
167
+
168
+ //#endregion
169
+ //#region src/components/planCard/PlanCardHeaderSlot.tsx
170
+ const PlanCardHeaderSlot = ({
171
+ title,
172
+ subtitle,
173
+ hasTopTag,
174
+ variant = "primary"
175
+ }) => {
176
+ const variantStyles = getPlanCardVariantStyles(variant);
177
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Box, {
178
+ sx: {
179
+ paddingY: hasTopTag ? "4" : "8",
180
+ paddingX: "8",
181
+ width: "full"
182
+ },
183
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
184
+ sx: {
185
+ flexDirection: "column",
186
+ gap: "2",
187
+ width: "full",
188
+ alignItems: "center"
189
+ },
190
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Heading, {
191
+ sx: {
192
+ fontSize: "3xl",
193
+ color: variantStyles.color
194
+ },
195
+ children: title
196
+ }), subtitle && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
197
+ sx: {
198
+ fontSize: "sm",
199
+ color: variantStyles.secondaryColor
200
+ },
201
+ children: subtitle
202
+ })]
203
+ })
204
+ });
205
+ };
206
+
207
+ //#endregion
208
+ //#region src/components/planCard/PlanCardMetadataSlot.tsx
209
+ const PlanCardMetadataSlot = ({
210
+ metadata,
211
+ variant = "primary"
212
+ }) => {
213
+ const variantStyles = getPlanCardVariantStyles(variant);
214
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Stack, {
215
+ sx: {
216
+ width: "full",
217
+ gap: "6"
218
+ },
219
+ children: metadata.map(service => {
220
+ const serviceKey = service.id ?? service.key ?? service.label;
221
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
222
+ sx: {
223
+ gap: "3",
224
+ flexDirection: "column",
225
+ width: "full"
226
+ },
227
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
228
+ sx: {
229
+ gap: "4",
230
+ alignItems: "center",
231
+ color: variantStyles.secondaryColor,
232
+ width: "full"
233
+ },
234
+ children: [service.icon && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_react_icons.Icon, {
235
+ icon: service.icon
236
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
237
+ sx: {
238
+ color: variantStyles.secondaryColor
239
+ },
240
+ children: service.label + String.fromCharCode(58)
241
+ })]
242
+ }), service.parameters.map(parameter => {
243
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
244
+ sx: {
245
+ paddingX: "6",
246
+ paddingY: "4",
247
+ backgroundColor: "display.background.muted.default",
248
+ borderRadius: "lg",
249
+ width: "full"
250
+ },
251
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
252
+ sx: {
253
+ width: "full",
254
+ display: "flex",
255
+ alignItems: "center",
256
+ justifyContent: "space-between",
257
+ gap: "2"
258
+ },
259
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
260
+ sx: {
261
+ fontSize: "sm",
262
+ textAlign: "center"
263
+ },
264
+ children: parameter.name
265
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
266
+ sx: {
267
+ fontWeight: "semibold",
268
+ fontSize: "lg"
269
+ },
270
+ children: parameter.value
271
+ })]
272
+ })
273
+ }, parameter.name);
274
+ })]
275
+ }, serviceKey);
276
+ })
277
+ });
278
+ };
279
+
280
+ //#endregion
281
+ //#region src/components/planCard/PlanCardPriceSlot.tsx
282
+ const PlanCardPriceSlot = ({
283
+ price,
284
+ variant = "primary"
285
+ }) => {
286
+ const variantStyles = getPlanCardVariantStyles(variant);
287
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
288
+ sx: {
289
+ paddingY: "6",
290
+ paddingX: "8",
291
+ width: "full",
292
+ alignItems: "center",
293
+ flexDirection: "column",
294
+ gap: "3"
295
+ },
296
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
297
+ sx: {
298
+ alignItems: "baseline",
299
+ gap: "4",
300
+ justifyContent: "center"
301
+ },
302
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
303
+ sx: {
304
+ fontSize: "4xl",
305
+ fontWeight: "bold",
306
+ color: variantStyles.color
307
+ },
308
+ children: price.value
309
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
310
+ sx: {
311
+ fontSize: "sm",
312
+ color: variantStyles.secondaryColor
313
+ },
314
+ children: price.interval
315
+ })]
316
+ }), price.description && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
317
+ sx: {
318
+ fontSize: "md",
319
+ color: variantStyles.secondaryColor
320
+ },
321
+ children: price.description
322
+ })]
323
+ });
324
+ };
325
+
326
+ //#endregion
327
+ //#region src/components/planCard/PlanCardTopTagSlot.tsx
328
+ const PlanCardTopTagSlot = ({
329
+ children
330
+ }) => {
331
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Box, {
332
+ sx: {
333
+ padding: "4"
334
+ },
335
+ children
336
+ });
337
+ };
338
+
339
+ //#endregion
340
+ //#region src/components/planCard/PlanCard.tsx
341
+ const PlanCard = props => {
342
+ const {
343
+ variant = "primary",
344
+ topTag,
345
+ title,
346
+ subtitle,
347
+ metadata = [],
348
+ metadataVariant,
349
+ price,
350
+ features = [],
351
+ buttonProps,
352
+ ...cardProps
353
+ } = props;
354
+ const effectiveMetadataVariant = metadataVariant ?? variant;
355
+ const variantStyles = getPlanCardVariantStyles(variant);
356
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Card, {
357
+ ...cardProps,
358
+ sx: {
359
+ display: "flex",
360
+ flexDirection: "column",
361
+ width: "full",
362
+ maxWidth: "410px",
363
+ backgroundColor: variantStyles.backgroundColor,
364
+ borderColor: variantStyles.borderColor,
365
+ ...cardProps.sx
366
+ },
367
+ children: [topTag && /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCardTopTagSlot, {
368
+ variant,
369
+ children: topTag
370
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCardHeaderSlot, {
371
+ title,
372
+ subtitle,
373
+ hasTopTag: Boolean(topTag),
374
+ variant
375
+ }), metadata.length > 0 && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Box, {
376
+ sx: {
377
+ paddingY: "4",
378
+ paddingX: "8",
379
+ width: "full"
380
+ },
381
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCardMetadataSlot, {
382
+ metadata,
383
+ variant: effectiveMetadataVariant
384
+ })
385
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCardPriceSlot, {
386
+ price,
387
+ variant
388
+ }), features.length > 0 && /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCardFeaturesSlot, {
389
+ features,
390
+ variant
391
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCardCtaSlot, {
392
+ buttonProps,
393
+ variant
394
+ })]
395
+ });
396
+ };
397
+
398
+ //#endregion
399
+ //#region src/components/plansPanel/PlansPanel.tsx
400
+ /**
401
+ * PlansPanel renders a grid of PlanCard components with optional multi-dimensional
402
+ * SegmentedControl filters above them.
403
+ *
404
+ * Each filter dimension independently narrows the visible set of plan cards.
405
+ * The component supports both uncontrolled mode (internal state from `defaultValue`)
406
+ * and controlled mode (via `filterValues` + `onFilterChange`).
407
+ *
408
+ * @example
409
+ * ```tsx
410
+ * // Uncontrolled — component manages filter state internally
411
+ * <PlansPanel
412
+ * filters={[
413
+ * {
414
+ * id: 'interval',
415
+ * label: 'Billing Frequency',
416
+ * options: [
417
+ * { label: 'Monthly', value: 'monthly' },
418
+ * { label: 'Yearly', value: 'yearly' },
419
+ * ],
420
+ * defaultValue: 'monthly',
421
+ * },
422
+ * ]}
423
+ * plans={plans}
424
+ * activePlanId="plan_basic_monthly"
425
+ * onSelectPlan={(planId) => console.log(planId)}
426
+ * />
427
+ * ```
428
+ *
429
+ * @example
430
+ * ```tsx
431
+ * // Controlled — consumer owns filter state
432
+ * <PlansPanel
433
+ * filters={filters}
434
+ * filterValues={{ interval: 'yearly' }}
435
+ * onFilterChange={(id, value) =>
436
+ * setFilterValues((prev) => ({ ...prev, [id]: value }))
437
+ * }
438
+ * plans={plans}
439
+ * onSelectPlan={(planId) => checkout(planId)}
440
+ * />
441
+ * ```
442
+ */
443
+ const PlansPanel = ({
444
+ filters = [],
445
+ plans,
446
+ activePlanId,
447
+ onSelectPlan,
448
+ filterValues: controlledFilterValues,
449
+ onFilterChange
450
+ }) => {
451
+ const [internalFilterValues, setInternalFilterValues] = react.useState(() => {
452
+ const initial = {};
453
+ for (const filter of filters) if (filter.defaultValue !== void 0) initial[filter.id] = filter.defaultValue;
454
+ return initial;
455
+ });
456
+ const isControlled = controlledFilterValues !== void 0;
457
+ const activeFilterValues = isControlled ? controlledFilterValues : internalFilterValues;
458
+ const handleFilterChange = (id, value) => {
459
+ const strValue = String(value);
460
+ if (isControlled) onFilterChange?.(id, strValue);else setInternalFilterValues(prev => {
461
+ return {
462
+ ...prev,
463
+ [id]: strValue
464
+ };
465
+ });
466
+ };
467
+ const visiblePlans = plans.filter(plan => {
468
+ if (!plan.filterValues || Object.keys(plan.filterValues).length === 0) return true;
469
+ return Object.entries(activeFilterValues).every(([filterId, filterValue]) => {
470
+ if (!plan.filterValues || !(filterId in plan.filterValues)) return true;
471
+ return plan.filterValues[filterId] === filterValue;
472
+ });
473
+ });
474
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Stack, {
475
+ sx: {
476
+ gap: "6",
477
+ width: "full",
478
+ alignItems: "center"
479
+ },
480
+ children: [filters.length > 0 && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Stack, {
481
+ sx: {
482
+ gap: "4",
483
+ alignItems: "center"
484
+ },
485
+ children: filters.map(filter => {
486
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Stack, {
487
+ sx: {
488
+ gap: "2",
489
+ alignItems: "center"
490
+ },
491
+ children: [filter.label && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Text, {
492
+ sx: {
493
+ fontWeight: "semibold",
494
+ color: "display.text.primary.default"
495
+ },
496
+ children: filter.label
497
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.SegmentedControl, {
498
+ options: filter.options,
499
+ value: isControlled ? controlledFilterValues[filter.id] ?? filter.defaultValue : activeFilterValues[filter.id],
500
+ defaultValue: isControlled ? void 0 : filter.defaultValue,
501
+ onChange: value => {
502
+ return handleFilterChange(filter.id, value);
503
+ }
504
+ })]
505
+ }, filter.id);
506
+ })
507
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
508
+ sx: {
509
+ flexWrap: "wrap",
510
+ gap: "4",
511
+ width: "full",
512
+ justifyContent: "center"
513
+ },
514
+ children: visiblePlans.map(plan => {
515
+ const effectiveVariant = plan.id === activePlanId ? "secondary" : plan.variant ?? "primary";
516
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(PlanCard, {
517
+ title: plan.title,
518
+ subtitle: plan.subtitle,
519
+ price: plan.price,
520
+ features: plan.features,
521
+ metadata: plan.metadata,
522
+ variant: effectiveVariant,
523
+ topTag: plan.topTag,
524
+ buttonProps: {
525
+ ...plan.buttonProps,
526
+ onClick: e => {
527
+ onSelectPlan?.(plan.id);
528
+ plan.buttonProps?.onClick?.(e);
529
+ }
530
+ }
531
+ }, plan.id);
532
+ })
533
+ })]
534
+ });
535
+ };
536
+
537
+ //#endregion
538
+ //#region src/components/subscriptionPanel/SubscriptionPanel.styles.ts
539
+ const gradientFlow = (0, _ttoss_ui.keyframes)({
540
+ "0%": {
541
+ backgroundPosition: "0% 50%"
542
+ },
543
+ "50%": {
544
+ backgroundPosition: "100% 50%"
545
+ },
546
+ "100%": {
547
+ backgroundPosition: "0% 50%"
548
+ }
549
+ });
550
+ const getAccentGradientBackground = t => {
551
+ const theme = t;
552
+ const start = theme.colors?.action?.background?.accent?.default || theme.colors?.input?.background?.accent?.default;
553
+ if (!start) return void 0;
554
+ return `linear-gradient(270deg, ${start}, ${theme.colors?.action?.background?.accent?.active || theme.colors?.input?.background?.accent?.active || start}, ${start})`;
555
+ };
556
+ const getPrimaryGradientBackground = t => {
557
+ const theme = t;
558
+ const start = theme.colors?.action?.background?.primary?.default;
559
+ if (!start) return void 0;
560
+ return `linear-gradient(270deg, ${start}, ${theme.colors?.action?.background?.secondary?.default || start}, ${start})`;
561
+ };
562
+ const getVariantStyles = variantType => {
563
+ const variants = {
564
+ "spotlight-accent": {
565
+ backgroundColor: "input.background.accent.default",
566
+ color: "action.text.accent.default",
567
+ borderColor: "display.border.muted.default",
568
+ gradientBackground: getAccentGradientBackground
569
+ },
570
+ "spotlight-primary": {
571
+ backgroundColor: "action.background.primary.default",
572
+ color: "display.text.accent.default",
573
+ borderColor: "display.border.muted.default",
574
+ gradientBackground: getPrimaryGradientBackground
575
+ },
576
+ primary: {
577
+ backgroundColor: "action.background.primary.default",
578
+ color: "action.text.primary.default",
579
+ borderColor: "display.border.muted.default",
580
+ gradientBackground: void 0
581
+ },
582
+ secondary: {
583
+ backgroundColor: "action.background.secondary.default",
584
+ color: "action.text.primary.default",
585
+ borderColor: "display.border.muted.default",
586
+ gradientBackground: void 0
587
+ },
588
+ accent: {
589
+ backgroundColor: "action.background.accent.default",
590
+ color: "action.text.accent.default",
591
+ borderColor: "display.border.muted.default",
592
+ gradientBackground: void 0
593
+ }
594
+ };
595
+ return variants[variantType] || variants["spotlight-accent"];
596
+ };
597
+ const getSubscriptionPanelAccentBarSx = (variant = "spotlight-accent") => {
598
+ const variantStyles = getVariantStyles(variant);
599
+ const isSpotlight = variant.startsWith("spotlight-");
600
+ const gradientBg = variant === "spotlight-accent" ? getAccentGradientBackground : getPrimaryGradientBackground;
601
+ return {
602
+ height: "12px",
603
+ width: "full",
604
+ borderTopLeftRadius: "lg",
605
+ borderTopRightRadius: "lg",
606
+ backgroundColor: variantStyles.backgroundColor,
607
+ color: variantStyles.color,
608
+ borderColor: variantStyles.borderColor,
609
+ ...(isSpotlight ? {
610
+ background: gradientBg,
611
+ backgroundSize: "400% 400%",
612
+ animation: `${gradientFlow} 6s ease infinite`
613
+ } : {})
614
+ };
615
+ };
616
+ const SubscriptionPanelAccentBarSx = getSubscriptionPanelAccentBarSx("spotlight-accent");
617
+
618
+ //#endregion
619
+ //#region src/components/subscriptionPanel/SubscriptionPanelActionsSlot.tsx
620
+ /**
621
+ * Renders action buttons for the subscription card.
622
+ */
623
+ const SubscriptionPanelActionsSlot = ({
624
+ actions
625
+ }) => {
626
+ if (actions.length === 0) return null;
627
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
628
+ sx: {
629
+ gap: "3",
630
+ flexDirection: ["column", "row"],
631
+ flexShrink: 0,
632
+ paddingX: "6",
633
+ paddingBottom: "6"
634
+ },
635
+ children: actions.map((action, index) => {
636
+ const {
637
+ label,
638
+ onClick,
639
+ leftIcon,
640
+ isLoading,
641
+ variant = "secondary",
642
+ disabled,
643
+ ...buttonProps
644
+ } = action;
645
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Button, {
646
+ onClick,
647
+ variant,
648
+ disabled: disabled || isLoading,
649
+ leftIcon: isLoading ? void 0 : leftIcon,
650
+ sx: {
651
+ gap: "2",
652
+ justifyContent: "center",
653
+ minWidth: "fit-content"
654
+ },
655
+ ...buttonProps,
656
+ children: isLoading ? /* @__PURE__ */(0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, {
657
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_react_icons.Icon, {
658
+ icon: "fluent:spinner-ios-20-regular"
659
+ }), "Processando..."]
660
+ }) : label
661
+ }, index);
662
+ })
663
+ });
664
+ };
665
+
666
+ //#endregion
667
+ //#region src/components/subscriptionPanel/SubscriptionPanel.tsx
668
+ /**
669
+ * Renders a metric card based on its type.
670
+ */
671
+ const renderMetricCard = params => {
672
+ const {
673
+ metric,
674
+ index,
675
+ isLoading
676
+ } = params;
677
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_components_MetricCard.MetricCard, {
678
+ metric,
679
+ isLoading
680
+ }, index);
681
+ };
682
+ /**
683
+ * SubscriptionPanel displays comprehensive subscription information including
684
+ * plan details, status, actions, and various metrics.
685
+ *
686
+ * It supports three types of metrics:
687
+ * - **Date**: For displaying dates like expiration or renewal
688
+ * - **Percentage**: For usage-based metrics with progress bars
689
+ * - **Number**: For count-based metrics
690
+ *
691
+ * @example
692
+ * ```tsx
693
+ * <SubscriptionPanel
694
+ * planName="Premium Plan"
695
+ * price={{ value: "R$ 99,00", interval: "mês" }}
696
+ * status={{ status: "active", interval: "Mensal" }}
697
+ * features={[{ label: "Feature 1" }, { label: "Feature 2" }]}
698
+ * actions={[
699
+ * { label: "Upgrade", onClick: () => {}, variant: "accent" },
700
+ * { label: "Cancel", onClick: () => {}, variant: "danger" },
701
+ * ]}
702
+ * metrics={[
703
+ * { type: "date", label: "Expira em", date: "15/01/2025", icon: "fluent:calendar-24-regular" },
704
+ * { type: "percentage", label: "Uso", current: 75, max: 100, icon: "fluent:data-usage-24-regular" },
705
+ * ]}
706
+ * />
707
+ * ```
708
+ *
709
+ * @example
710
+ * ```tsx
711
+ * // Compact mode for smaller spaces
712
+ * <SubscriptionPanel
713
+ * planName="Basic Plan"
714
+ * price={{ value: "R$ 29,00", interval: "mês" }}
715
+ * status={{ status: "active" }}
716
+ * compact
717
+ * />
718
+ * ```
719
+ */
720
+ const SubscriptionPanel = ({
721
+ variant = "accent",
722
+ icon,
723
+ planName,
724
+ price,
725
+ status,
726
+ features = [],
727
+ actions = [],
728
+ metrics = [],
729
+ isLoading = false
730
+ }) => {
731
+ if (isLoading)
732
+ /**
733
+ * use skeleton loader in the future here
734
+ */
735
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Card, {
736
+ sx: {
737
+ width: "full",
738
+ height: "400px",
739
+ alignItems: "center",
740
+ justifyContent: "center"
741
+ },
742
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Spinner, {})
743
+ });
744
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Card, {
745
+ sx: {
746
+ width: "full"
747
+ },
748
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
749
+ sx: getSubscriptionPanelAccentBarSx(variant)
750
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
751
+ sx: {
752
+ width: "full",
753
+ paddingX: "6",
754
+ paddingY: "6",
755
+ flexDirection: "column"
756
+ },
757
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
758
+ sx: {
759
+ paddingY: "10",
760
+ paddingX: "3",
761
+ width: "full",
762
+ flexDirection: ["column", "column", "row"],
763
+ alignItems: ["stretch", "stretch", "center"],
764
+ justifyContent: "space-between",
765
+ gap: "6",
766
+ borderBottom: "sm",
767
+ borderBottomColor: "display.border.muted.default"
768
+ },
769
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_components_EnhancedTitle.EnhancedTitle, {
770
+ variant,
771
+ icon,
772
+ title: planName,
773
+ frontTitle: price.interval ? `${price.value}/${price.interval}` : price.value,
774
+ topBadges: [...(status.status === "active" ? [{
775
+ label: "Ativo",
776
+ variant: "positive",
777
+ icon: "fluent:checkmark-circle-24-filled"
778
+ }] : status.status === "inactive" ? [{
779
+ label: "Inativo",
780
+ variant: "muted",
781
+ icon: "fluent:dismiss-circle-24-filled"
782
+ }] : [{
783
+ label: "Cancelado",
784
+ variant: "negative",
785
+ icon: "fluent:error-circle-24-filled"
786
+ }]), ...(status.interval ? [{
787
+ label: status.interval,
788
+ variant: "informative"
789
+ }] : []), ...(status.hasScheduledUpdate ? [{
790
+ label: "Alteração Agendada",
791
+ variant: "informative",
792
+ icon: "fluent:clock-24-regular"
793
+ }] : []), ...(status.hasCancellation ? [{
794
+ label: "Renovação Cancelada",
795
+ variant: "negative",
796
+ icon: "fluent:dismiss-circle-24-regular"
797
+ }] : [])],
798
+ bottomBadges: features.map(feature => {
799
+ return {
800
+ label: feature.label,
801
+ icon: typeof feature.icon === "string" ? feature.icon : "fluent:checkmark-24-filled",
802
+ variant: "muted"
803
+ };
804
+ })
805
+ }), actions.length > 0 && /* @__PURE__ */(0, react_jsx_runtime.jsx)(SubscriptionPanelActionsSlot, {
806
+ actions
807
+ })]
808
+ }), metrics.length > 0 && /* @__PURE__ */(0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {
809
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
810
+ sx: {
811
+ paddingY: "10",
812
+ paddingX: "3",
813
+ width: "full",
814
+ flexWrap: "wrap",
815
+ gap: "10",
816
+ justifyContent: "center"
817
+ },
818
+ children: metrics.map((metric, index) => {
819
+ return renderMetricCard({
820
+ metric,
821
+ index,
822
+ isLoading
823
+ });
824
+ })
825
+ })
826
+ })]
827
+ })]
828
+ });
829
+ };
830
+
831
+ //#endregion
832
+ exports.PlanCard = PlanCard;
833
+ exports.PlansPanel = PlansPanel;
834
+ exports.SubscriptionPanel = SubscriptionPanel;