myoperator-ui 0.0.210 → 0.0.211
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.js +237 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11201,6 +11201,7 @@ export type {
|
|
|
11201
11201
|
import { cn } from "../../../lib/utils";
|
|
11202
11202
|
import { Button } from "../button";
|
|
11203
11203
|
import { Badge } from "../badge";
|
|
11204
|
+
import { CircleCheck } from "lucide-react";
|
|
11204
11205
|
import type { LetUsDriveCardProps } from "./types";
|
|
11205
11206
|
|
|
11206
11207
|
/**
|
|
@@ -11208,8 +11209,9 @@ import type { LetUsDriveCardProps } from "./types";
|
|
|
11208
11209
|
* frequency badge, and a CTA. Used in the "Let us drive \u2014 Full-service
|
|
11209
11210
|
* management" section of the pricing page.
|
|
11210
11211
|
*
|
|
11211
|
-
* Supports
|
|
11212
|
-
*
|
|
11212
|
+
* Supports expandable "Show details" / "Hide details" with an "Includes:"
|
|
11213
|
+
* checklist when detailsContent is provided. Supports controlled expanded
|
|
11214
|
+
* state (expanded / onExpandedChange) for accordion behavior on PricingPage.
|
|
11213
11215
|
*
|
|
11214
11216
|
* @example
|
|
11215
11217
|
* \`\`\`tsx
|
|
@@ -11219,6 +11221,7 @@ import type { LetUsDriveCardProps } from "./types";
|
|
|
11219
11221
|
* period="/month"
|
|
11220
11222
|
* billingBadge="Annually"
|
|
11221
11223
|
* description="One expert who knows your business. And moves it forward."
|
|
11224
|
+
* detailsContent={{ heading: "Includes:", items: [{ title: "Start Your Channels", description: "Get help setting up." }] }}
|
|
11222
11225
|
* onShowDetails={() => console.log("details")}
|
|
11223
11226
|
* onCtaClick={() => console.log("talk")}
|
|
11224
11227
|
* />
|
|
@@ -11235,7 +11238,11 @@ const LetUsDriveCard = React.forwardRef<HTMLDivElement, LetUsDriveCardProps>(
|
|
|
11235
11238
|
description,
|
|
11236
11239
|
freeLabel,
|
|
11237
11240
|
showDetailsLabel = "Show details",
|
|
11241
|
+
hideDetailsLabel = "Hide details",
|
|
11238
11242
|
ctaLabel = "Talk to us",
|
|
11243
|
+
detailsContent,
|
|
11244
|
+
expanded: controlledExpanded,
|
|
11245
|
+
onExpandedChange,
|
|
11239
11246
|
onShowDetails,
|
|
11240
11247
|
onCtaClick,
|
|
11241
11248
|
className,
|
|
@@ -11243,11 +11250,29 @@ const LetUsDriveCard = React.forwardRef<HTMLDivElement, LetUsDriveCardProps>(
|
|
|
11243
11250
|
},
|
|
11244
11251
|
ref
|
|
11245
11252
|
) => {
|
|
11253
|
+
const [internalExpanded, setInternalExpanded] = React.useState(false);
|
|
11254
|
+
const isControlled = controlledExpanded !== undefined;
|
|
11255
|
+
const expanded = isControlled ? controlledExpanded : internalExpanded;
|
|
11256
|
+
|
|
11257
|
+
const hasExpandableDetails = detailsContent && detailsContent.items.length > 0;
|
|
11258
|
+
const showDetailsLink = hasExpandableDetails || onShowDetails;
|
|
11259
|
+
|
|
11260
|
+
const handleDetailsClick = () => {
|
|
11261
|
+
if (hasExpandableDetails) {
|
|
11262
|
+
const next = !expanded;
|
|
11263
|
+
if (!isControlled) setInternalExpanded(next);
|
|
11264
|
+
onExpandedChange?.(next);
|
|
11265
|
+
if (next) onShowDetails?.();
|
|
11266
|
+
} else {
|
|
11267
|
+
onShowDetails?.();
|
|
11268
|
+
}
|
|
11269
|
+
};
|
|
11270
|
+
|
|
11246
11271
|
return (
|
|
11247
11272
|
<div
|
|
11248
11273
|
ref={ref}
|
|
11249
11274
|
className={cn(
|
|
11250
|
-
"flex flex-col gap-6 rounded-[14px] border border-semantic-border-layout bg-card p-5",
|
|
11275
|
+
"flex h-full min-h-0 flex-col gap-6 rounded-[14px] border border-semantic-border-layout bg-card p-5 shadow-sm",
|
|
11251
11276
|
className
|
|
11252
11277
|
)}
|
|
11253
11278
|
{...props}
|
|
@@ -11267,8 +11292,8 @@ const LetUsDriveCard = React.forwardRef<HTMLDivElement, LetUsDriveCardProps>(
|
|
|
11267
11292
|
)}
|
|
11268
11293
|
</div>
|
|
11269
11294
|
|
|
11270
|
-
{/* Price section */}
|
|
11271
|
-
<div className="flex flex-col gap-2.5">
|
|
11295
|
+
{/* Price section \u2014 min-height so "Includes:" starts at same vertical position across cards when details are expanded */}
|
|
11296
|
+
<div className="flex min-h-[7rem] flex-col gap-2.5">
|
|
11272
11297
|
{startsAt && (
|
|
11273
11298
|
<span className="text-xs text-semantic-text-muted tracking-[0.048px]">
|
|
11274
11299
|
Starts at
|
|
@@ -11302,18 +11327,61 @@ const LetUsDriveCard = React.forwardRef<HTMLDivElement, LetUsDriveCardProps>(
|
|
|
11302
11327
|
</p>
|
|
11303
11328
|
</div>
|
|
11304
11329
|
|
|
11305
|
-
{/*
|
|
11306
|
-
<div className="flex flex-col gap-3 w-full">
|
|
11307
|
-
{
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11330
|
+
{/* Bottom section: flex-1 fills space so "Hide details" and button align across cards; flex column, button at bottom */}
|
|
11331
|
+
<div className="mt-auto flex min-h-0 flex-1 flex-col gap-3 w-full">
|
|
11332
|
+
{showDetailsLink && !(hasExpandableDetails && expanded) && (
|
|
11333
|
+
<>
|
|
11334
|
+
<div className="min-h-0 flex-1" aria-hidden />
|
|
11335
|
+
<Button
|
|
11336
|
+
variant="link"
|
|
11337
|
+
className="text-semantic-text-link p-0 h-auto min-w-0 justify-start shrink-0"
|
|
11338
|
+
onClick={handleDetailsClick}
|
|
11339
|
+
>
|
|
11340
|
+
{showDetailsLabel}
|
|
11341
|
+
</Button>
|
|
11342
|
+
</>
|
|
11343
|
+
)}
|
|
11344
|
+
{!showDetailsLink && <div className="min-h-0 flex-1" aria-hidden />}
|
|
11345
|
+
{hasExpandableDetails && expanded && (
|
|
11346
|
+
<>
|
|
11347
|
+
<div
|
|
11348
|
+
className="flex min-h-0 flex-1 flex-col gap-3 w-full border-t border-semantic-border-layout pt-4"
|
|
11349
|
+
data-testid="let-us-drive-details-block"
|
|
11350
|
+
>
|
|
11351
|
+
<p className="text-sm font-semibold text-semantic-text-primary tracking-[0.014px] m-0">
|
|
11352
|
+
{detailsContent.heading ?? "Includes:"}
|
|
11353
|
+
</p>
|
|
11354
|
+
<ul className="flex flex-col gap-3 list-none m-0 p-0" aria-label="Included features">
|
|
11355
|
+
{detailsContent.items.map((item, index) => (
|
|
11356
|
+
<li key={index} className="flex items-start gap-3">
|
|
11357
|
+
<span className="flex w-4 shrink-0 items-start" aria-hidden>
|
|
11358
|
+
<CircleCheck className="size-4 text-semantic-success-primary" />
|
|
11359
|
+
</span>
|
|
11360
|
+
<span className="min-w-0 flex-1 text-left text-sm text-semantic-text-secondary tracking-[0.035px] leading-[20px]">
|
|
11361
|
+
<strong className="font-semibold text-semantic-text-primary">
|
|
11362
|
+
{item.title}
|
|
11363
|
+
</strong>
|
|
11364
|
+
{" "}
|
|
11365
|
+
{item.description}
|
|
11366
|
+
</span>
|
|
11367
|
+
</li>
|
|
11368
|
+
))}
|
|
11369
|
+
</ul>
|
|
11370
|
+
</div>
|
|
11371
|
+
<Button
|
|
11372
|
+
variant="link"
|
|
11373
|
+
className="text-semantic-text-link p-0 h-auto min-w-0 justify-start shrink-0"
|
|
11374
|
+
onClick={handleDetailsClick}
|
|
11375
|
+
>
|
|
11376
|
+
{hideDetailsLabel}
|
|
11377
|
+
</Button>
|
|
11378
|
+
</>
|
|
11315
11379
|
)}
|
|
11316
|
-
<Button
|
|
11380
|
+
<Button
|
|
11381
|
+
variant="outline"
|
|
11382
|
+
className="min-h-[44px] w-full shrink-0 rounded-[4px]"
|
|
11383
|
+
onClick={onCtaClick}
|
|
11384
|
+
>
|
|
11317
11385
|
{ctaLabel}
|
|
11318
11386
|
</Button>
|
|
11319
11387
|
</div>
|
|
@@ -11332,7 +11400,28 @@ export { LetUsDriveCard };
|
|
|
11332
11400
|
content: prefixTailwindClasses(`import * as React from "react";
|
|
11333
11401
|
|
|
11334
11402
|
/**
|
|
11335
|
-
*
|
|
11403
|
+
* A single item in the expandable "Includes" details block (bold title + description).
|
|
11404
|
+
*/
|
|
11405
|
+
export interface LetUsDriveDetailsItem {
|
|
11406
|
+
/** Bold title (e.g., "Start Your Channels") */
|
|
11407
|
+
title: string;
|
|
11408
|
+
/** Description text (e.g., "Get help setting up your Call and WhatsApp channels.") */
|
|
11409
|
+
description: string;
|
|
11410
|
+
}
|
|
11411
|
+
|
|
11412
|
+
/**
|
|
11413
|
+
* Content for the expandable "Show details" / "Hide details" section.
|
|
11414
|
+
* When provided, the card shows an "Includes:"-style block with checklist items.
|
|
11415
|
+
*/
|
|
11416
|
+
export interface LetUsDriveDetailsContent {
|
|
11417
|
+
/** Heading above the list (default: "Includes:") */
|
|
11418
|
+
heading?: string;
|
|
11419
|
+
/** Checklist items (title in bold, description in regular weight) */
|
|
11420
|
+
items: LetUsDriveDetailsItem[];
|
|
11421
|
+
}
|
|
11422
|
+
|
|
11423
|
+
/**
|
|
11424
|
+
* Props for the LetUsDriveCard component. Modular and reusable across screens (e.g. managed services, add-on offerings, or any service card with pricing and expandable details).
|
|
11336
11425
|
*/
|
|
11337
11426
|
export interface LetUsDriveCardProps
|
|
11338
11427
|
extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -11350,11 +11439,22 @@ export interface LetUsDriveCardProps
|
|
|
11350
11439
|
description: string;
|
|
11351
11440
|
/** When provided, price is shown with strikethrough and this label (e.g., "FREE") is displayed in green */
|
|
11352
11441
|
freeLabel?: string;
|
|
11353
|
-
/** Text for the details link (default: "Show details") */
|
|
11442
|
+
/** Text for the details link when collapsed (default: "Show details") */
|
|
11354
11443
|
showDetailsLabel?: string;
|
|
11444
|
+
/** Text for the details link when expanded (default: "Hide details") */
|
|
11445
|
+
hideDetailsLabel?: string;
|
|
11355
11446
|
/** CTA button text (default: "Talk to us") */
|
|
11356
11447
|
ctaLabel?: string;
|
|
11357
|
-
/**
|
|
11448
|
+
/**
|
|
11449
|
+
* Expandable details content. When provided, the card shows "Show details" / "Hide details"
|
|
11450
|
+
* and an expandable "Includes:"-style block. Omit for link-only (onShowDetails callback only).
|
|
11451
|
+
*/
|
|
11452
|
+
detailsContent?: LetUsDriveDetailsContent;
|
|
11453
|
+
/** Controlled expanded state for the details block (use with onExpandedChange) */
|
|
11454
|
+
expanded?: boolean;
|
|
11455
|
+
/** Callback when expanded state changes (for controlled mode / PricingPage accordion) */
|
|
11456
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
11457
|
+
/** Callback when "Show details" link is clicked (still fired when using detailsContent) */
|
|
11358
11458
|
onShowDetails?: () => void;
|
|
11359
11459
|
/** Callback when CTA button is clicked */
|
|
11360
11460
|
onCtaClick?: () => void;
|
|
@@ -11364,7 +11464,11 @@ export interface LetUsDriveCardProps
|
|
|
11364
11464
|
{
|
|
11365
11465
|
name: "index.ts",
|
|
11366
11466
|
content: prefixTailwindClasses(`export { LetUsDriveCard } from "./let-us-drive-card";
|
|
11367
|
-
export type {
|
|
11467
|
+
export type {
|
|
11468
|
+
LetUsDriveCardProps,
|
|
11469
|
+
LetUsDriveDetailsContent,
|
|
11470
|
+
LetUsDriveDetailsItem,
|
|
11471
|
+
} from "./types";
|
|
11368
11472
|
`, prefix)
|
|
11369
11473
|
}
|
|
11370
11474
|
]
|
|
@@ -11474,7 +11578,7 @@ export { PowerUpCard };
|
|
|
11474
11578
|
content: prefixTailwindClasses(`import * as React from "react";
|
|
11475
11579
|
|
|
11476
11580
|
/**
|
|
11477
|
-
* Props for the PowerUpCard component.
|
|
11581
|
+
* Props for the PowerUpCard component. Modular and reusable across screens (e.g. pricing page power-ups, add-ons, or any feature card with icon, price, description, and CTA).
|
|
11478
11582
|
*/
|
|
11479
11583
|
export interface PowerUpCardProps
|
|
11480
11584
|
extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -11561,6 +11665,8 @@ const PricingCard = React.forwardRef<HTMLDivElement, PricingCardProps>(
|
|
|
11561
11665
|
showPopularBadge = false,
|
|
11562
11666
|
badgeText = "MOST POPULAR",
|
|
11563
11667
|
ctaText,
|
|
11668
|
+
ctaLoading = false,
|
|
11669
|
+
ctaDisabled = false,
|
|
11564
11670
|
onCtaClick,
|
|
11565
11671
|
onFeatureDetails,
|
|
11566
11672
|
addon,
|
|
@@ -11654,6 +11760,8 @@ const PricingCard = React.forwardRef<HTMLDivElement, PricingCardProps>(
|
|
|
11654
11760
|
variant={isCurrentPlan ? "outline" : "default"}
|
|
11655
11761
|
className="w-full"
|
|
11656
11762
|
onClick={onCtaClick}
|
|
11763
|
+
loading={ctaLoading}
|
|
11764
|
+
disabled={ctaDisabled}
|
|
11657
11765
|
>
|
|
11658
11766
|
{buttonText}
|
|
11659
11767
|
</Button>
|
|
@@ -11923,7 +12031,19 @@ export interface UsageDetail {
|
|
|
11923
12031
|
export type PricingCardFeature = string | { text: string; bold?: boolean };
|
|
11924
12032
|
|
|
11925
12033
|
/**
|
|
11926
|
-
*
|
|
12034
|
+
* Reusable CTA state for a single plan card (loading/disabled).
|
|
12035
|
+
* Use on PricingCard via ctaLoading/ctaDisabled, or in arrays for
|
|
12036
|
+
* screens that render multiple plan cards (e.g. planCardCtaStates on PricingPage).
|
|
12037
|
+
*/
|
|
12038
|
+
export interface PlanCardCtaState {
|
|
12039
|
+
/** Show loading spinner on the CTA and make it non-interactive */
|
|
12040
|
+
loading?: boolean;
|
|
12041
|
+
/** Disable the CTA button (e.g. current plan or pending action) */
|
|
12042
|
+
disabled?: boolean;
|
|
12043
|
+
}
|
|
12044
|
+
|
|
12045
|
+
/**
|
|
12046
|
+
* Props for the PricingCard component. Modular and reusable across screens (e.g. plan selection grid, comparison view, or any plan card with features and CTA).
|
|
11927
12047
|
*/
|
|
11928
12048
|
export interface PricingCardProps
|
|
11929
12049
|
extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -11951,6 +12071,10 @@ export interface PricingCardProps
|
|
|
11951
12071
|
badgeText?: string;
|
|
11952
12072
|
/** Custom CTA button text (overrides default "Select plan" / "Current plan") */
|
|
11953
12073
|
ctaText?: string;
|
|
12074
|
+
/** Show loading spinner on CTA button and make it non-interactive. Reusable on any screen that renders PricingCard. */
|
|
12075
|
+
ctaLoading?: boolean;
|
|
12076
|
+
/** Disable the CTA button (e.g. current plan or pending action). Reusable on any screen that renders PricingCard. */
|
|
12077
|
+
ctaDisabled?: boolean;
|
|
11954
12078
|
/** Callback when CTA button is clicked */
|
|
11955
12079
|
onCtaClick?: () => void;
|
|
11956
12080
|
/** Callback when "Feature details" link is clicked */
|
|
@@ -11969,6 +12093,7 @@ export interface PricingCardProps
|
|
|
11969
12093
|
content: prefixTailwindClasses(`export { PricingCard } from "./pricing-card";
|
|
11970
12094
|
export { CompactCarIcon, SedanCarIcon, SuvCarIcon } from "./plan-icons";
|
|
11971
12095
|
export type {
|
|
12096
|
+
PlanCardCtaState,
|
|
11972
12097
|
PricingCardProps,
|
|
11973
12098
|
PricingCardAddon,
|
|
11974
12099
|
PricingCardFeature,
|
|
@@ -12038,18 +12163,21 @@ const PricingPage = React.forwardRef<HTMLDivElement, PricingPageProps>(
|
|
|
12038
12163
|
title = "Select business plan",
|
|
12039
12164
|
headerActions,
|
|
12040
12165
|
tabs = [],
|
|
12166
|
+
showCategoryToggle = true,
|
|
12041
12167
|
activeTab: controlledTab,
|
|
12042
12168
|
onTabChange,
|
|
12043
12169
|
showBillingToggle = false,
|
|
12044
12170
|
billingPeriod: controlledBilling,
|
|
12045
12171
|
onBillingPeriodChange,
|
|
12046
12172
|
planCards = [],
|
|
12173
|
+
planCardCtaStates,
|
|
12047
12174
|
powerUpCards = [],
|
|
12048
12175
|
powerUpsTitle = "Power-ups and charges",
|
|
12049
12176
|
featureComparisonText = "See full feature comparison",
|
|
12050
12177
|
onFeatureComparisonClick,
|
|
12051
12178
|
letUsDriveCards = [],
|
|
12052
12179
|
letUsDriveTitle = "Let us drive \u2014 Full-service management",
|
|
12180
|
+
letUsDriveExpandMode,
|
|
12053
12181
|
className,
|
|
12054
12182
|
...props
|
|
12055
12183
|
},
|
|
@@ -12062,6 +12190,8 @@ const PricingPage = React.forwardRef<HTMLDivElement, PricingPageProps>(
|
|
|
12062
12190
|
const [internalBilling, setInternalBilling] = React.useState<
|
|
12063
12191
|
"monthly" | "yearly"
|
|
12064
12192
|
>("monthly");
|
|
12193
|
+
const [expandedLetUsDriveIndices, setExpandedLetUsDriveIndices] =
|
|
12194
|
+
React.useState<number[]>([]);
|
|
12065
12195
|
|
|
12066
12196
|
const currentTab = controlledTab ?? internalTab;
|
|
12067
12197
|
const currentBilling = controlledBilling ?? internalBilling;
|
|
@@ -12076,6 +12206,30 @@ const PricingPage = React.forwardRef<HTMLDivElement, PricingPageProps>(
|
|
|
12076
12206
|
onBillingPeriodChange?.(period);
|
|
12077
12207
|
};
|
|
12078
12208
|
|
|
12209
|
+
const cardCount = letUsDriveCards.length;
|
|
12210
|
+
|
|
12211
|
+
const handleLetUsDriveExpandedChange = (index: number, expanded: boolean) => {
|
|
12212
|
+
if (letUsDriveExpandMode === "all") {
|
|
12213
|
+
if (expanded) {
|
|
12214
|
+
setExpandedLetUsDriveIndices(
|
|
12215
|
+
Array.from({ length: cardCount }, (_, i) => i)
|
|
12216
|
+
);
|
|
12217
|
+
} else {
|
|
12218
|
+
setExpandedLetUsDriveIndices((prev) =>
|
|
12219
|
+
prev.filter((i) => i !== index)
|
|
12220
|
+
);
|
|
12221
|
+
}
|
|
12222
|
+
} else {
|
|
12223
|
+
if (expanded) {
|
|
12224
|
+
setExpandedLetUsDriveIndices([index]);
|
|
12225
|
+
} else {
|
|
12226
|
+
setExpandedLetUsDriveIndices((prev) =>
|
|
12227
|
+
prev.filter((i) => i !== index)
|
|
12228
|
+
);
|
|
12229
|
+
}
|
|
12230
|
+
}
|
|
12231
|
+
};
|
|
12232
|
+
|
|
12079
12233
|
const hasPowerUps = powerUpCards.length > 0;
|
|
12080
12234
|
const hasLetUsDrive = letUsDriveCards.length > 0;
|
|
12081
12235
|
|
|
@@ -12095,7 +12249,7 @@ const PricingPage = React.forwardRef<HTMLDivElement, PricingPageProps>(
|
|
|
12095
12249
|
{/* \u2500\u2500\u2500\u2500\u2500 Plan Selection Area \u2500\u2500\u2500\u2500\u2500 */}
|
|
12096
12250
|
<div className="flex flex-col gap-6 px-6 py-6">
|
|
12097
12251
|
{/* Tabs + billing toggle */}
|
|
12098
|
-
{tabs.length > 0 && (
|
|
12252
|
+
{tabs.length > 0 && showCategoryToggle && (
|
|
12099
12253
|
<PricingToggle
|
|
12100
12254
|
tabs={tabs}
|
|
12101
12255
|
activeTab={currentTab}
|
|
@@ -12116,9 +12270,17 @@ const PricingPage = React.forwardRef<HTMLDivElement, PricingPageProps>(
|
|
|
12116
12270
|
: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
12117
12271
|
)}
|
|
12118
12272
|
>
|
|
12119
|
-
{planCards.map((cardProps, index) =>
|
|
12120
|
-
|
|
12121
|
-
|
|
12273
|
+
{planCards.map((cardProps, index) => {
|
|
12274
|
+
const ctaState = planCardCtaStates?.[index];
|
|
12275
|
+
const merged = { ...cardProps };
|
|
12276
|
+
if (ctaState) {
|
|
12277
|
+
if (ctaState.loading !== undefined)
|
|
12278
|
+
merged.ctaLoading = ctaState.loading;
|
|
12279
|
+
if (ctaState.disabled !== undefined)
|
|
12280
|
+
merged.ctaDisabled = ctaState.disabled;
|
|
12281
|
+
}
|
|
12282
|
+
return <PricingCard key={index} {...merged} />;
|
|
12283
|
+
})}
|
|
12122
12284
|
</div>
|
|
12123
12285
|
)}
|
|
12124
12286
|
</div>
|
|
@@ -12163,11 +12325,22 @@ const PricingPage = React.forwardRef<HTMLDivElement, PricingPageProps>(
|
|
|
12163
12325
|
{letUsDriveTitle}
|
|
12164
12326
|
</h2>
|
|
12165
12327
|
|
|
12166
|
-
{/* Service cards */}
|
|
12167
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
12168
|
-
{letUsDriveCards.map((cardProps, index) =>
|
|
12169
|
-
|
|
12170
|
-
|
|
12328
|
+
{/* Service cards \u2014 items-stretch + card h-full + mt-auto on actions align Talk to us buttons */}
|
|
12329
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch">
|
|
12330
|
+
{letUsDriveCards.map((cardProps, index) => {
|
|
12331
|
+
const hasDetailsContent =
|
|
12332
|
+
cardProps.detailsContent &&
|
|
12333
|
+
cardProps.detailsContent.items.length > 0;
|
|
12334
|
+
const useControlledExpand =
|
|
12335
|
+
letUsDriveExpandMode && hasDetailsContent;
|
|
12336
|
+
const merged = { ...cardProps };
|
|
12337
|
+
if (useControlledExpand) {
|
|
12338
|
+
merged.expanded = expandedLetUsDriveIndices.includes(index);
|
|
12339
|
+
merged.onExpandedChange = (expanded: boolean) =>
|
|
12340
|
+
handleLetUsDriveExpandedChange(index, expanded);
|
|
12341
|
+
}
|
|
12342
|
+
return <LetUsDriveCard key={index} {...merged} />;
|
|
12343
|
+
})}
|
|
12171
12344
|
</div>
|
|
12172
12345
|
</div>
|
|
12173
12346
|
</div>
|
|
@@ -12185,19 +12358,22 @@ export { PricingPage };
|
|
|
12185
12358
|
{
|
|
12186
12359
|
name: "types.ts",
|
|
12187
12360
|
content: prefixTailwindClasses(`import * as React from "react";
|
|
12188
|
-
import type { PricingCardProps } from "../pricing-card/types";
|
|
12361
|
+
import type { PlanCardCtaState, PricingCardProps } from "../pricing-card/types";
|
|
12189
12362
|
import type { PowerUpCardProps } from "../power-up-card/types";
|
|
12190
12363
|
import type { LetUsDriveCardProps } from "../let-us-drive-card/types";
|
|
12191
12364
|
import type { PricingToggleTab } from "../pricing-toggle/types";
|
|
12192
12365
|
|
|
12193
12366
|
export type { PricingToggleTab };
|
|
12194
12367
|
|
|
12368
|
+
|
|
12195
12369
|
/**
|
|
12196
12370
|
* Props for the PricingPage component.
|
|
12197
12371
|
*
|
|
12198
12372
|
* PricingPage is a layout compositor that orchestrates PricingToggle,
|
|
12199
12373
|
* PricingCard, PowerUpCard, LetUsDriveCard, and PageHeader into
|
|
12200
|
-
* the full plan selection page.
|
|
12374
|
+
* the full plan selection page. Modular and reusable across screens:
|
|
12375
|
+
* use the full page layout, or compose sections elsewhere with the same
|
|
12376
|
+
* sub-components (PricingCard, PowerUpCard, LetUsDriveCard, etc.).
|
|
12201
12377
|
*/
|
|
12202
12378
|
export interface PricingPageProps
|
|
12203
12379
|
extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -12212,6 +12388,8 @@ export interface PricingPageProps
|
|
|
12212
12388
|
|
|
12213
12389
|
/** Plan type tabs shown in the pill selector */
|
|
12214
12390
|
tabs?: PricingToggleTab[];
|
|
12391
|
+
/** When false, the category toggle (e.g. Team-Led Plans / Go-AI First) is hidden. Default true. */
|
|
12392
|
+
showCategoryToggle?: boolean;
|
|
12215
12393
|
/** Currently active tab value (controlled). Falls back to first tab when unset. */
|
|
12216
12394
|
activeTab?: string;
|
|
12217
12395
|
/** Callback when the active tab changes */
|
|
@@ -12227,6 +12405,12 @@ export interface PricingPageProps
|
|
|
12227
12405
|
|
|
12228
12406
|
/** Array of plan card props to render in the main pricing grid */
|
|
12229
12407
|
planCards?: PricingCardProps[];
|
|
12408
|
+
/**
|
|
12409
|
+
* Optional CTA state per plan card (loading/disabled). Reusable across any screen that renders plan cards.
|
|
12410
|
+
* Index matches planCards: [0] = first card CTA, [1] = second, [2] = third.
|
|
12411
|
+
* Overrides ctaLoading/ctaDisabled on the card when provided.
|
|
12412
|
+
*/
|
|
12413
|
+
planCardCtaStates?: PlanCardCtaState[];
|
|
12230
12414
|
|
|
12231
12415
|
/* \u2500\u2500\u2500\u2500\u2500 Power-ups Section \u2500\u2500\u2500\u2500\u2500 */
|
|
12232
12416
|
|
|
@@ -12245,6 +12429,13 @@ export interface PricingPageProps
|
|
|
12245
12429
|
letUsDriveCards?: LetUsDriveCardProps[];
|
|
12246
12430
|
/** Let-us-drive section heading (default: "Let us drive \u2014 Full-service management") */
|
|
12247
12431
|
letUsDriveTitle?: string;
|
|
12432
|
+
/**
|
|
12433
|
+
* When set, controls how "Show details" expands across cards.
|
|
12434
|
+
* - "single": only the clicked card expands (accordion).
|
|
12435
|
+
* - "all": clicking "Show details" on any card expands all cards that have detailsContent.
|
|
12436
|
+
* Ignored when cards are used without detailsContent or without controlled expanded state.
|
|
12437
|
+
*/
|
|
12438
|
+
letUsDriveExpandMode?: "single" | "all";
|
|
12248
12439
|
}
|
|
12249
12440
|
`, prefix)
|
|
12250
12441
|
},
|
|
@@ -12252,6 +12443,7 @@ export interface PricingPageProps
|
|
|
12252
12443
|
name: "index.ts",
|
|
12253
12444
|
content: prefixTailwindClasses(`export { PricingPage } from "./pricing-page";
|
|
12254
12445
|
export type { PricingPageProps, PricingToggleTab } from "./types";
|
|
12446
|
+
export type { PlanCardCtaState } from "../pricing-card/types";
|
|
12255
12447
|
`, prefix)
|
|
12256
12448
|
}
|
|
12257
12449
|
]
|
|
@@ -12393,7 +12585,7 @@ export { PricingToggle };
|
|
|
12393
12585
|
},
|
|
12394
12586
|
{
|
|
12395
12587
|
name: "types.ts",
|
|
12396
|
-
content: prefixTailwindClasses(`/** A single tab option in the plan tab selector */
|
|
12588
|
+
content: prefixTailwindClasses(`/** A single tab option in the plan tab selector. Reusable for any tabbed plan/category selector. */
|
|
12397
12589
|
export interface PricingToggleTab {
|
|
12398
12590
|
/** Display label for the tab */
|
|
12399
12591
|
label: string;
|
|
@@ -12401,6 +12593,7 @@ export interface PricingToggleTab {
|
|
|
12401
12593
|
value: string;
|
|
12402
12594
|
}
|
|
12403
12595
|
|
|
12596
|
+
/** Props for the PricingToggle component. Modular and reusable across screens (e.g. plan-type selector, billing toggle). */
|
|
12404
12597
|
export interface PricingToggleProps
|
|
12405
12598
|
extends React.HTMLAttributes<HTMLDivElement> {
|
|
12406
12599
|
/** Array of tab options for the plan type selector */
|
|
@@ -12488,6 +12681,7 @@ const TalkToUsModal: React.FC<TalkToUsModalProps> = ({
|
|
|
12488
12681
|
description = "Please contact our team for more details. We're here to help you choose the right plan.",
|
|
12489
12682
|
icon,
|
|
12490
12683
|
primaryActionLabel = "Contact support",
|
|
12684
|
+
primaryActionLoading = false,
|
|
12491
12685
|
secondaryActionLabel = "Cancel",
|
|
12492
12686
|
onPrimaryAction,
|
|
12493
12687
|
onSecondaryAction,
|
|
@@ -12524,7 +12718,12 @@ const TalkToUsModal: React.FC<TalkToUsModalProps> = ({
|
|
|
12524
12718
|
<Button variant="outline" onClick={handleSecondaryAction}>
|
|
12525
12719
|
{secondaryActionLabel}
|
|
12526
12720
|
</Button>
|
|
12527
|
-
<Button
|
|
12721
|
+
<Button
|
|
12722
|
+
loading={primaryActionLoading}
|
|
12723
|
+
onClick={onPrimaryAction}
|
|
12724
|
+
>
|
|
12725
|
+
{primaryActionLabel}
|
|
12726
|
+
</Button>
|
|
12528
12727
|
</div>
|
|
12529
12728
|
</div>
|
|
12530
12729
|
</DialogContent>
|
|
@@ -12594,7 +12793,7 @@ export type { BrandIconProps };
|
|
|
12594
12793
|
content: prefixTailwindClasses(`import * as React from "react";
|
|
12595
12794
|
|
|
12596
12795
|
/**
|
|
12597
|
-
* Props for the TalkToUsModal component.
|
|
12796
|
+
* Props for the TalkToUsModal component. Modular and reusable across screens (e.g. triggered from PowerUpCard, pricing CTAs, or any "contact support" flow).
|
|
12598
12797
|
*/
|
|
12599
12798
|
export interface TalkToUsModalProps {
|
|
12600
12799
|
/** Whether the modal is open */
|
|
@@ -12609,6 +12808,8 @@ export interface TalkToUsModalProps {
|
|
|
12609
12808
|
icon?: React.ReactNode;
|
|
12610
12809
|
/** Label for the primary action button (default: "Contact support") */
|
|
12611
12810
|
primaryActionLabel?: string;
|
|
12811
|
+
/** Show loading spinner on the primary CTA and make it non-interactive. Reusable across screens. */
|
|
12812
|
+
primaryActionLoading?: boolean;
|
|
12612
12813
|
/** Label for the secondary action button (default: "Cancel") */
|
|
12613
12814
|
secondaryActionLabel?: string;
|
|
12614
12815
|
/** Callback when primary action button is clicked */
|