@wakastellar/ui 2.3.4 → 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.
- package/dist/blocks/dashboard/index.d.ts +4 -1
- package/dist/blocks/empty-states/index.d.ts +4 -1
- package/dist/blocks/error-pages/index.d.ts +4 -1
- package/dist/blocks/index.d.ts +1 -1
- package/dist/blocks/landing/index.d.ts +4 -1
- package/dist/blocks/pricing/index.d.ts +5 -1
- package/dist/blocks/sidebar/index.d.ts +5 -1
- package/dist/index.cjs.js +130 -130
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +7905 -7856
- package/dist/stories/Button.d.ts +14 -0
- package/dist/stories/Button.stories.d.ts +8 -0
- package/dist/stories/Header.d.ts +11 -0
- package/dist/stories/Header.stories.d.ts +6 -0
- package/dist/stories/Page.d.ts +2 -0
- package/dist/stories/Page.stories.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/link.d.ts +23 -0
- package/package.json +11 -3
- package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
- package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
- package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
- package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
- package/src/blocks/chat/Chat.stories.tsx +466 -0
- package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
- package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
- package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
- package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
- package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
- package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
- package/src/blocks/dashboard/index.tsx +68 -27
- package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
- package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
- package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
- package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
- package/src/blocks/empty-states/index.tsx +26 -8
- package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
- package/src/blocks/error-pages/index.tsx +26 -8
- package/src/blocks/faq/FAQ.stories.tsx +416 -0
- package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
- package/src/blocks/footer/Footer.stories.tsx +328 -0
- package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
- package/src/blocks/header/WakaHeader.stories.tsx +455 -0
- package/src/blocks/headtab/Headtab.stories.tsx +369 -0
- package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
- package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
- package/src/blocks/index.ts +1 -1
- package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
- package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
- package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
- package/src/blocks/landing/index.tsx +125 -66
- package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
- package/src/blocks/layout/Layout.stories.tsx +373 -0
- package/src/blocks/login/Login.stories.tsx +443 -0
- package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
- package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
- package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
- package/src/blocks/pricing/index.tsx +38 -4
- package/src/blocks/profile/Profile.stories.tsx +371 -0
- package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
- package/src/blocks/settings/Settings.stories.tsx +520 -0
- package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
- package/src/blocks/sidebar/index.tsx +49 -20
- package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
- package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
- package/src/blocks/wizard/Wizard.stories.tsx +683 -0
- package/src/components/accordion/Accordion.stories.tsx +93 -0
- package/src/components/alert/Alert.stories.tsx +95 -0
- package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
- package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
- package/src/components/avatar/Avatar.stories.tsx +104 -0
- package/src/components/button/Button.stories.tsx +12 -1
- package/src/components/calendar/Calendar.stories.tsx +102 -0
- package/src/components/card/Card.stories.tsx +125 -0
- package/src/components/checkbox/Checkbox.stories.tsx +100 -0
- package/src/components/code/Code.stories.tsx +402 -0
- package/src/components/collapsible/Collapsible.stories.tsx +123 -0
- package/src/components/command/Command.stories.tsx +207 -0
- package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
- package/src/components/dialog/Dialog.stories.tsx +157 -0
- package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
- package/src/components/form/Form.stories.tsx +413 -0
- package/src/components/hover-card/HoverCard.stories.tsx +148 -0
- package/src/components/input-otp/InputOTP.stories.tsx +255 -0
- package/src/components/label/Label.stories.tsx +68 -0
- package/src/components/menubar/Menubar.stories.tsx +278 -0
- package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
- package/src/components/popover/Popover.stories.tsx +199 -0
- package/src/components/progress/Progress.stories.tsx +104 -0
- package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
- package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
- package/src/components/select/Select.stories.tsx +146 -0
- package/src/components/separator/Separator.stories.tsx +117 -0
- package/src/components/sheet/Sheet.stories.tsx +195 -0
- package/src/components/skeleton/Skeleton.stories.tsx +114 -0
- package/src/components/slider/Slider.stories.tsx +157 -0
- package/src/components/switch/Switch.stories.tsx +114 -0
- package/src/components/table/Table.stories.tsx +153 -0
- package/src/components/tabs/Tabs.stories.tsx +155 -0
- package/src/components/textarea/Textarea.stories.tsx +116 -0
- package/src/components/toast/Toast.stories.tsx +160 -0
- package/src/components/toggle/Toggle.stories.tsx +125 -0
- package/src/components/tooltip/Tooltip.stories.tsx +133 -0
- package/src/components/typography/Typography.stories.tsx +207 -0
- package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
- package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
- package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
- package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
- package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
- package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
- package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
- package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
- package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
- package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
- package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
- package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
- package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
- package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
- package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
- package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
- package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
- package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
- package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
- package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
- package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
- package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
- package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
- package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
- package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
- package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
- package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
- package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
- package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
- package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
- package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
- package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
- package/src/components/waka-image/WakaImage.stories.tsx +255 -0
- package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
- package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
- package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
- package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
- package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
- package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
- package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
- package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
- package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
- package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
- package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
- package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
- package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
- package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
- package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
- package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
- package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
- package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
- package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
- package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
- package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
- package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
- package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
- package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
- package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
- package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
- package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
- package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
- package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
- package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
- package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
- package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
- package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
- package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
- package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
- package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
- package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
- package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
- package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
- package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
- package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
- package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
- package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
- package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
- package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
- package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
- package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
- package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
- package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
- package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
- package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
- package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
- package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
- package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
- package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
- package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
- package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaPricing, defaultPricingPlans, defaultPricingFAQ } from './index'
|
|
3
|
+
import type { PricingPlan, PricingFAQ } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
const MockLink = ({ href, children, ...props }: any) => (
|
|
7
|
+
<a href={href} {...props} onClick={(e) => e.preventDefault()}>
|
|
8
|
+
{children}
|
|
9
|
+
</a>
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof WakaPricing> = {
|
|
13
|
+
title: 'Blocks/Pricing',
|
|
14
|
+
component: WakaPricing,
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: 'fullscreen',
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component:
|
|
20
|
+
'A pricing section with plan cards, comparison table, billing toggle, and FAQ. Supports monthly/yearly pricing.',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
tags: ['autodocs'],
|
|
25
|
+
argTypes: {
|
|
26
|
+
layout: {
|
|
27
|
+
control: 'select',
|
|
28
|
+
options: ['cards', 'table'],
|
|
29
|
+
},
|
|
30
|
+
columns: {
|
|
31
|
+
control: 'select',
|
|
32
|
+
options: [2, 3, 4],
|
|
33
|
+
},
|
|
34
|
+
showBillingToggle: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
},
|
|
37
|
+
showComparison: {
|
|
38
|
+
control: 'boolean',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default meta
|
|
44
|
+
type Story = StoryObj<typeof WakaPricing>
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<WakaPricing
|
|
49
|
+
title="Choose your plan"
|
|
50
|
+
description="Start free and scale as you grow. No hidden fees."
|
|
51
|
+
plans={defaultPricingPlans}
|
|
52
|
+
yearlyDiscount="20%"
|
|
53
|
+
faq={defaultPricingFAQ}
|
|
54
|
+
LinkComponent={MockLink}
|
|
55
|
+
/>
|
|
56
|
+
),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const WithoutBillingToggle: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<WakaPricing
|
|
62
|
+
title="Simple Pricing"
|
|
63
|
+
description="One price, all features included."
|
|
64
|
+
plans={defaultPricingPlans}
|
|
65
|
+
showBillingToggle={false}
|
|
66
|
+
LinkComponent={MockLink}
|
|
67
|
+
/>
|
|
68
|
+
),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const TwoPlans: Story = {
|
|
72
|
+
render: () => {
|
|
73
|
+
const twoPlans: PricingPlan[] = [
|
|
74
|
+
{
|
|
75
|
+
id: 'starter',
|
|
76
|
+
name: 'Starter',
|
|
77
|
+
description: 'For individuals',
|
|
78
|
+
price: 9,
|
|
79
|
+
priceYearly: 7,
|
|
80
|
+
features: [
|
|
81
|
+
{ name: '1 user', included: true },
|
|
82
|
+
{ name: '10 projects', included: true },
|
|
83
|
+
{ name: 'Basic support', included: true },
|
|
84
|
+
{ name: 'API access', included: false },
|
|
85
|
+
],
|
|
86
|
+
buttonText: 'Get Started',
|
|
87
|
+
buttonVariant: 'outline',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'pro',
|
|
91
|
+
name: 'Pro',
|
|
92
|
+
description: 'For teams',
|
|
93
|
+
price: 29,
|
|
94
|
+
priceYearly: 24,
|
|
95
|
+
popular: true,
|
|
96
|
+
features: [
|
|
97
|
+
{ name: 'Unlimited users', included: true },
|
|
98
|
+
{ name: 'Unlimited projects', included: true },
|
|
99
|
+
{ name: 'Priority support', included: true },
|
|
100
|
+
{ name: 'API access', included: true },
|
|
101
|
+
],
|
|
102
|
+
buttonText: 'Start Free Trial',
|
|
103
|
+
},
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<WakaPricing
|
|
108
|
+
title="Pick a plan"
|
|
109
|
+
plans={twoPlans}
|
|
110
|
+
columns={2}
|
|
111
|
+
yearlyDiscount="20%"
|
|
112
|
+
LinkComponent={MockLink}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const FourPlans: Story = {
|
|
119
|
+
render: () => {
|
|
120
|
+
const fourPlans: PricingPlan[] = [
|
|
121
|
+
{
|
|
122
|
+
id: 'hobby',
|
|
123
|
+
name: 'Hobby',
|
|
124
|
+
price: 0,
|
|
125
|
+
features: [
|
|
126
|
+
{ name: '1 user', included: true },
|
|
127
|
+
{ name: '3 projects', included: true },
|
|
128
|
+
{ name: 'Community support', included: true },
|
|
129
|
+
],
|
|
130
|
+
buttonText: 'Start Free',
|
|
131
|
+
buttonVariant: 'outline',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'starter',
|
|
135
|
+
name: 'Starter',
|
|
136
|
+
price: 15,
|
|
137
|
+
priceYearly: 12,
|
|
138
|
+
features: [
|
|
139
|
+
{ name: '3 users', included: true },
|
|
140
|
+
{ name: '10 projects', included: true },
|
|
141
|
+
{ name: 'Email support', included: true },
|
|
142
|
+
],
|
|
143
|
+
buttonText: 'Get Started',
|
|
144
|
+
buttonVariant: 'outline',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'pro',
|
|
148
|
+
name: 'Pro',
|
|
149
|
+
price: 49,
|
|
150
|
+
priceYearly: 39,
|
|
151
|
+
popular: true,
|
|
152
|
+
features: [
|
|
153
|
+
{ name: '10 users', included: true },
|
|
154
|
+
{ name: 'Unlimited projects', included: true },
|
|
155
|
+
{ name: 'Priority support', included: true },
|
|
156
|
+
],
|
|
157
|
+
buttonText: 'Start Trial',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 'enterprise',
|
|
161
|
+
name: 'Enterprise',
|
|
162
|
+
price: 'Custom',
|
|
163
|
+
features: [
|
|
164
|
+
{ name: 'Unlimited users', included: true },
|
|
165
|
+
{ name: 'Unlimited everything', included: true },
|
|
166
|
+
{ name: 'Dedicated support', included: true },
|
|
167
|
+
],
|
|
168
|
+
buttonText: 'Contact Sales',
|
|
169
|
+
buttonVariant: 'outline',
|
|
170
|
+
},
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<WakaPricing
|
|
175
|
+
title="Plans for every team"
|
|
176
|
+
plans={fourPlans}
|
|
177
|
+
columns={4}
|
|
178
|
+
yearlyDiscount="20%"
|
|
179
|
+
LinkComponent={MockLink}
|
|
180
|
+
/>
|
|
181
|
+
)
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export const TableLayout: Story = {
|
|
186
|
+
render: () => (
|
|
187
|
+
<WakaPricing
|
|
188
|
+
title="Compare Plans"
|
|
189
|
+
description="See all features side by side"
|
|
190
|
+
plans={defaultPricingPlans}
|
|
191
|
+
layout="table"
|
|
192
|
+
showComparison
|
|
193
|
+
yearlyDiscount="20%"
|
|
194
|
+
LinkComponent={MockLink}
|
|
195
|
+
/>
|
|
196
|
+
),
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const WithFAQ: Story = {
|
|
200
|
+
render: () => {
|
|
201
|
+
const customFAQ: PricingFAQ[] = [
|
|
202
|
+
{
|
|
203
|
+
question: 'What payment methods do you accept?',
|
|
204
|
+
answer: 'We accept all major credit cards (Visa, Mastercard, Amex), PayPal, and wire transfers for enterprise customers.',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
question: 'Can I change plans later?',
|
|
208
|
+
answer: 'Yes! You can upgrade or downgrade your plan at any time. Changes take effect immediately.',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
question: 'Is there a free trial?',
|
|
212
|
+
answer: 'Yes, all paid plans include a 14-day free trial. No credit card required.',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
question: 'What happens if I exceed my limits?',
|
|
216
|
+
answer: "We'll notify you when you're approaching your limits. You can upgrade or we'll help you optimize your usage.",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
question: 'Do you offer refunds?',
|
|
220
|
+
answer: 'Yes, we offer a 30-day money-back guarantee for all paid plans.',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
question: 'Is there a discount for nonprofits?',
|
|
224
|
+
answer: 'Yes! Nonprofits and educational institutions get 50% off all plans. Contact us for details.',
|
|
225
|
+
},
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<WakaPricing
|
|
230
|
+
title="Pricing"
|
|
231
|
+
plans={defaultPricingPlans}
|
|
232
|
+
yearlyDiscount="20%"
|
|
233
|
+
faq={customFAQ}
|
|
234
|
+
faqTitle="Frequently Asked Questions"
|
|
235
|
+
LinkComponent={MockLink}
|
|
236
|
+
/>
|
|
237
|
+
)
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const ControlledBillingPeriod: Story = {
|
|
242
|
+
render: () => {
|
|
243
|
+
const [period, setPeriod] = React.useState<'monthly' | 'yearly'>('monthly')
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<div>
|
|
247
|
+
<div className="text-center mb-4">
|
|
248
|
+
<p className="text-sm text-muted-foreground">
|
|
249
|
+
Current period: <strong>{period}</strong>
|
|
250
|
+
</p>
|
|
251
|
+
</div>
|
|
252
|
+
<WakaPricing
|
|
253
|
+
title="Pricing"
|
|
254
|
+
plans={defaultPricingPlans}
|
|
255
|
+
billingPeriod={period}
|
|
256
|
+
onBillingPeriodChange={setPeriod}
|
|
257
|
+
yearlyDiscount="20%"
|
|
258
|
+
LinkComponent={MockLink}
|
|
259
|
+
/>
|
|
260
|
+
</div>
|
|
261
|
+
)
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export const WithTooltips: Story = {
|
|
266
|
+
render: () => {
|
|
267
|
+
const plansWithTooltips: PricingPlan[] = [
|
|
268
|
+
{
|
|
269
|
+
id: 'basic',
|
|
270
|
+
name: 'Basic',
|
|
271
|
+
price: 0,
|
|
272
|
+
features: [
|
|
273
|
+
{ name: 'Storage', included: true, value: '5 GB', tooltip: 'Cloud storage for your files' },
|
|
274
|
+
{ name: 'API calls', included: 'limited', value: '1K/month', tooltip: 'Number of API requests per month' },
|
|
275
|
+
{ name: 'Support', included: true, tooltip: 'Community forum support' },
|
|
276
|
+
{ name: 'Analytics', included: false, tooltip: 'Advanced analytics and reporting' },
|
|
277
|
+
],
|
|
278
|
+
buttonText: 'Start Free',
|
|
279
|
+
buttonVariant: 'outline',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: 'pro',
|
|
283
|
+
name: 'Pro',
|
|
284
|
+
price: 29,
|
|
285
|
+
priceYearly: 24,
|
|
286
|
+
popular: true,
|
|
287
|
+
features: [
|
|
288
|
+
{ name: 'Storage', included: true, value: '100 GB', tooltip: 'Cloud storage for your files' },
|
|
289
|
+
{ name: 'API calls', included: true, value: '100K/month', tooltip: 'Number of API requests per month' },
|
|
290
|
+
{ name: 'Support', included: true, tooltip: 'Priority email support' },
|
|
291
|
+
{ name: 'Analytics', included: true, tooltip: 'Advanced analytics and reporting' },
|
|
292
|
+
],
|
|
293
|
+
buttonText: 'Start Trial',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'enterprise',
|
|
297
|
+
name: 'Enterprise',
|
|
298
|
+
price: 'Custom',
|
|
299
|
+
features: [
|
|
300
|
+
{ name: 'Storage', included: true, value: 'Unlimited', tooltip: 'No storage limits' },
|
|
301
|
+
{ name: 'API calls', included: true, value: 'Unlimited', tooltip: 'No API rate limits' },
|
|
302
|
+
{ name: 'Support', included: true, tooltip: 'Dedicated account manager + SLA' },
|
|
303
|
+
{ name: 'Analytics', included: true, tooltip: 'Custom analytics and white-label reports' },
|
|
304
|
+
],
|
|
305
|
+
buttonText: 'Contact Sales',
|
|
306
|
+
buttonVariant: 'outline',
|
|
307
|
+
},
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<WakaPricing
|
|
312
|
+
title="Compare Features"
|
|
313
|
+
description="Hover over features to learn more"
|
|
314
|
+
plans={plansWithTooltips}
|
|
315
|
+
yearlyDiscount="20%"
|
|
316
|
+
LinkComponent={MockLink}
|
|
317
|
+
/>
|
|
318
|
+
)
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export const CustomCurrency: Story = {
|
|
323
|
+
render: () => {
|
|
324
|
+
const euroPlans: PricingPlan[] = [
|
|
325
|
+
{
|
|
326
|
+
id: 'basic',
|
|
327
|
+
name: 'Basic',
|
|
328
|
+
price: 0,
|
|
329
|
+
currency: '€',
|
|
330
|
+
features: [
|
|
331
|
+
{ name: '1 utilisateur', included: true },
|
|
332
|
+
{ name: '5 projets', included: true },
|
|
333
|
+
],
|
|
334
|
+
buttonText: 'Commencer',
|
|
335
|
+
buttonVariant: 'outline',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 'pro',
|
|
339
|
+
name: 'Pro',
|
|
340
|
+
price: 25,
|
|
341
|
+
priceYearly: 20,
|
|
342
|
+
currency: '€',
|
|
343
|
+
period: '/mois',
|
|
344
|
+
periodYearly: '/mois (facturé annuellement)',
|
|
345
|
+
popular: true,
|
|
346
|
+
features: [
|
|
347
|
+
{ name: '5 utilisateurs', included: true },
|
|
348
|
+
{ name: 'Projets illimités', included: true },
|
|
349
|
+
],
|
|
350
|
+
buttonText: 'Essai gratuit',
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: 'enterprise',
|
|
354
|
+
name: 'Enterprise',
|
|
355
|
+
price: 'Sur devis',
|
|
356
|
+
currency: '',
|
|
357
|
+
features: [
|
|
358
|
+
{ name: 'Utilisateurs illimités', included: true },
|
|
359
|
+
{ name: 'Support dédié', included: true },
|
|
360
|
+
],
|
|
361
|
+
buttonText: 'Contacter',
|
|
362
|
+
buttonVariant: 'outline',
|
|
363
|
+
},
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<WakaPricing
|
|
368
|
+
title="Tarification"
|
|
369
|
+
description="Choisissez le plan qui vous convient"
|
|
370
|
+
plans={euroPlans}
|
|
371
|
+
yearlyDiscount="20%"
|
|
372
|
+
LinkComponent={MockLink}
|
|
373
|
+
/>
|
|
374
|
+
)
|
|
375
|
+
},
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export const Interactive: Story = {
|
|
379
|
+
render: () => {
|
|
380
|
+
const [selectedPlan, setSelectedPlan] = React.useState<string | null>(null)
|
|
381
|
+
|
|
382
|
+
const interactivePlans: PricingPlan[] = defaultPricingPlans.map((plan) => ({
|
|
383
|
+
...plan,
|
|
384
|
+
onSelect: () => {
|
|
385
|
+
setSelectedPlan(plan.id)
|
|
386
|
+
console.log('Selected plan:', plan.name)
|
|
387
|
+
},
|
|
388
|
+
}))
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<div>
|
|
392
|
+
{selectedPlan && (
|
|
393
|
+
<div className="bg-primary/10 p-4 text-center mb-4 rounded-lg">
|
|
394
|
+
<p className="text-sm">
|
|
395
|
+
You selected: <strong className="capitalize">{selectedPlan}</strong>
|
|
396
|
+
</p>
|
|
397
|
+
</div>
|
|
398
|
+
)}
|
|
399
|
+
<WakaPricing
|
|
400
|
+
title="Select a Plan"
|
|
401
|
+
plans={interactivePlans}
|
|
402
|
+
yearlyDiscount="20%"
|
|
403
|
+
LinkComponent={MockLink}
|
|
404
|
+
/>
|
|
405
|
+
</div>
|
|
406
|
+
)
|
|
407
|
+
},
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export const PartialFeatures: Story = {
|
|
411
|
+
render: () => {
|
|
412
|
+
const plansWithPartial: PricingPlan[] = [
|
|
413
|
+
{
|
|
414
|
+
id: 'starter',
|
|
415
|
+
name: 'Starter',
|
|
416
|
+
price: 9,
|
|
417
|
+
features: [
|
|
418
|
+
{ name: 'Projects', included: true, value: '5' },
|
|
419
|
+
{ name: 'Storage', included: 'limited', value: '1 GB' },
|
|
420
|
+
{ name: 'API Access', included: 'partial', value: 'Read-only' },
|
|
421
|
+
{ name: 'Team members', included: false },
|
|
422
|
+
{ name: 'Analytics', included: false },
|
|
423
|
+
],
|
|
424
|
+
buttonText: 'Get Started',
|
|
425
|
+
buttonVariant: 'outline',
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
id: 'growth',
|
|
429
|
+
name: 'Growth',
|
|
430
|
+
price: 29,
|
|
431
|
+
popular: true,
|
|
432
|
+
features: [
|
|
433
|
+
{ name: 'Projects', included: true, value: '25' },
|
|
434
|
+
{ name: 'Storage', included: true, value: '25 GB' },
|
|
435
|
+
{ name: 'API Access', included: true, value: 'Full access' },
|
|
436
|
+
{ name: 'Team members', included: 'limited', value: 'Up to 5' },
|
|
437
|
+
{ name: 'Analytics', included: 'partial', value: 'Basic' },
|
|
438
|
+
],
|
|
439
|
+
buttonText: 'Start Trial',
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: 'scale',
|
|
443
|
+
name: 'Scale',
|
|
444
|
+
price: 99,
|
|
445
|
+
features: [
|
|
446
|
+
{ name: 'Projects', included: true, value: 'Unlimited' },
|
|
447
|
+
{ name: 'Storage', included: true, value: '100 GB' },
|
|
448
|
+
{ name: 'API Access', included: true, value: 'Full access' },
|
|
449
|
+
{ name: 'Team members', included: true, value: 'Unlimited' },
|
|
450
|
+
{ name: 'Analytics', included: true, value: 'Advanced' },
|
|
451
|
+
],
|
|
452
|
+
buttonText: 'Contact Sales',
|
|
453
|
+
},
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
return (
|
|
457
|
+
<WakaPricing
|
|
458
|
+
title="Feature Comparison"
|
|
459
|
+
description="Yellow checkmarks indicate limited access"
|
|
460
|
+
plans={plansWithPartial}
|
|
461
|
+
showBillingToggle={false}
|
|
462
|
+
LinkComponent={MockLink}
|
|
463
|
+
/>
|
|
464
|
+
)
|
|
465
|
+
},
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export const DisabledPlans: Story = {
|
|
469
|
+
render: () => {
|
|
470
|
+
const plansWithDisabled: PricingPlan[] = [
|
|
471
|
+
{
|
|
472
|
+
...defaultPricingPlans[0],
|
|
473
|
+
disabled: true,
|
|
474
|
+
buttonText: 'Coming Soon',
|
|
475
|
+
},
|
|
476
|
+
defaultPricingPlans[1],
|
|
477
|
+
{
|
|
478
|
+
...defaultPricingPlans[2],
|
|
479
|
+
badge: 'Limited',
|
|
480
|
+
buttonText: 'Waitlist',
|
|
481
|
+
},
|
|
482
|
+
]
|
|
483
|
+
|
|
484
|
+
return (
|
|
485
|
+
<WakaPricing
|
|
486
|
+
title="Available Plans"
|
|
487
|
+
plans={plansWithDisabled}
|
|
488
|
+
showBillingToggle={false}
|
|
489
|
+
LinkComponent={MockLink}
|
|
490
|
+
/>
|
|
491
|
+
)
|
|
492
|
+
},
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export const CompletePricingPage: Story = {
|
|
496
|
+
render: () => (
|
|
497
|
+
<div className="min-h-screen">
|
|
498
|
+
<header className="border-b py-4">
|
|
499
|
+
<div className="container flex items-center justify-between">
|
|
500
|
+
<span className="font-bold text-xl">SaaSApp</span>
|
|
501
|
+
<nav className="flex gap-6 text-sm">
|
|
502
|
+
<a href="#" className="text-muted-foreground hover:text-foreground">Features</a>
|
|
503
|
+
<a href="#" className="text-foreground font-medium">Pricing</a>
|
|
504
|
+
<a href="#" className="text-muted-foreground hover:text-foreground">Docs</a>
|
|
505
|
+
<a href="#" className="text-muted-foreground hover:text-foreground">Blog</a>
|
|
506
|
+
</nav>
|
|
507
|
+
<button className="px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm">
|
|
508
|
+
Sign In
|
|
509
|
+
</button>
|
|
510
|
+
</div>
|
|
511
|
+
</header>
|
|
512
|
+
|
|
513
|
+
<WakaPricing
|
|
514
|
+
title="Simple, transparent pricing"
|
|
515
|
+
description="No hidden fees. No surprises. Start free and scale as you grow."
|
|
516
|
+
plans={defaultPricingPlans}
|
|
517
|
+
yearlyDiscount="20%"
|
|
518
|
+
faq={defaultPricingFAQ}
|
|
519
|
+
faqTitle="Common Questions"
|
|
520
|
+
LinkComponent={MockLink}
|
|
521
|
+
/>
|
|
522
|
+
|
|
523
|
+
<footer className="border-t py-8 bg-muted/30">
|
|
524
|
+
<div className="container text-center text-sm text-muted-foreground">
|
|
525
|
+
© 2024 SaaSApp Inc. All rights reserved.
|
|
526
|
+
</div>
|
|
527
|
+
</footer>
|
|
528
|
+
</div>
|
|
529
|
+
),
|
|
530
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { cn } from "../../utils"
|
|
5
|
+
import type { LinkComponentProps } from "../../types/link"
|
|
5
6
|
import { Button } from "../../components/button"
|
|
6
7
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../components/card"
|
|
7
8
|
import { Badge } from "../../components/badge"
|
|
@@ -82,6 +83,22 @@ export interface WakaPricingProps {
|
|
|
82
83
|
showComparison?: boolean
|
|
83
84
|
/** Classes CSS additionnelles */
|
|
84
85
|
className?: string
|
|
86
|
+
/** Composant de lien personnalisé (ex: next/link) pour navigation SPA */
|
|
87
|
+
LinkComponent?: React.ComponentType<LinkComponentProps>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================
|
|
91
|
+
// CONTEXT
|
|
92
|
+
// ============================================
|
|
93
|
+
|
|
94
|
+
interface PricingContextValue {
|
|
95
|
+
LinkComponent?: React.ComponentType<LinkComponentProps>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const PricingContext = React.createContext<PricingContextValue>({})
|
|
99
|
+
|
|
100
|
+
function usePricingContext() {
|
|
101
|
+
return React.useContext(PricingContext)
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
// ============================================
|
|
@@ -135,6 +152,8 @@ interface PricingCardProps {
|
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
function PricingCard({ plan, billingPeriod }: PricingCardProps) {
|
|
155
|
+
const { LinkComponent } = usePricingContext()
|
|
156
|
+
|
|
138
157
|
const price = billingPeriod === "yearly" && plan.priceYearly !== undefined
|
|
139
158
|
? plan.priceYearly
|
|
140
159
|
: plan.price
|
|
@@ -236,7 +255,11 @@ function PricingCard({ plan, billingPeriod }: PricingCardProps) {
|
|
|
236
255
|
asChild={!!plan.href}
|
|
237
256
|
>
|
|
238
257
|
{plan.href ? (
|
|
239
|
-
|
|
258
|
+
LinkComponent ? (
|
|
259
|
+
<LinkComponent href={plan.href}>{plan.buttonText || "Choisir"}</LinkComponent>
|
|
260
|
+
) : (
|
|
261
|
+
<a href={plan.href}>{plan.buttonText || "Choisir"}</a>
|
|
262
|
+
)
|
|
240
263
|
) : (
|
|
241
264
|
plan.buttonText || "Choisir"
|
|
242
265
|
)}
|
|
@@ -252,6 +275,8 @@ interface PricingTableProps {
|
|
|
252
275
|
}
|
|
253
276
|
|
|
254
277
|
function PricingTable({ plans, billingPeriod }: PricingTableProps) {
|
|
278
|
+
const { LinkComponent } = usePricingContext()
|
|
279
|
+
|
|
255
280
|
// Get all unique features
|
|
256
281
|
const allFeatures = Array.from(
|
|
257
282
|
new Set(plans.flatMap((plan) => plan.features.map((f) => f.name)))
|
|
@@ -367,7 +392,11 @@ function PricingTable({ plans, billingPeriod }: PricingTableProps) {
|
|
|
367
392
|
asChild={!!plan.href}
|
|
368
393
|
>
|
|
369
394
|
{plan.href ? (
|
|
370
|
-
|
|
395
|
+
LinkComponent ? (
|
|
396
|
+
<LinkComponent href={plan.href}>{plan.buttonText || "Choisir"}</LinkComponent>
|
|
397
|
+
) : (
|
|
398
|
+
<a href={plan.href}>{plan.buttonText || "Choisir"}</a>
|
|
399
|
+
)
|
|
371
400
|
) : (
|
|
372
401
|
plan.buttonText || "Choisir"
|
|
373
402
|
)}
|
|
@@ -420,6 +449,7 @@ export function WakaPricing({
|
|
|
420
449
|
columns = 3,
|
|
421
450
|
showComparison = false,
|
|
422
451
|
className,
|
|
452
|
+
LinkComponent,
|
|
423
453
|
}: WakaPricingProps) {
|
|
424
454
|
const [internalBillingPeriod, setInternalBillingPeriod] = React.useState<"monthly" | "yearly">("monthly")
|
|
425
455
|
|
|
@@ -439,8 +469,11 @@ export function WakaPricing({
|
|
|
439
469
|
4: "md:grid-cols-2 lg:grid-cols-4",
|
|
440
470
|
}
|
|
441
471
|
|
|
472
|
+
const contextValue = React.useMemo(() => ({ LinkComponent }), [LinkComponent])
|
|
473
|
+
|
|
442
474
|
return (
|
|
443
|
-
<
|
|
475
|
+
<PricingContext.Provider value={contextValue}>
|
|
476
|
+
<div className={cn("container py-16", className)}>
|
|
444
477
|
{/* Header */}
|
|
445
478
|
<div className="text-center mb-12">
|
|
446
479
|
<h1 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
|
|
@@ -477,7 +510,8 @@ export function WakaPricing({
|
|
|
477
510
|
{faq && faq.length > 0 && (
|
|
478
511
|
<PricingFAQSection title={faqTitle} items={faq} />
|
|
479
512
|
)}
|
|
480
|
-
|
|
513
|
+
</div>
|
|
514
|
+
</PricingContext.Provider>
|
|
481
515
|
)
|
|
482
516
|
}
|
|
483
517
|
|