@agent-relay/dashboard 2.0.81 → 2.0.82

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.
Files changed (244) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/chunks/{118-4c8241b0218335de.js → 118-ae2b650136a5a5fc.js} +1 -1
  3. package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +1 -0
  4. package/out/_next/static/chunks/app/app/[[...slug]]/{page-1e81c047cff17212.js → page-f7eca1b66fb4249b.js} +1 -1
  5. package/out/_next/static/chunks/app/{page-6892fe2dd07fb48b.js → page-0ee604f7070d14c0.js} +1 -1
  6. package/out/_next/static/css/8968d98ed4c4d33f.css +1 -0
  7. package/out/about.html +2 -2
  8. package/out/about.txt +1 -1
  9. package/out/app/onboarding.html +1 -1
  10. package/out/app/onboarding.txt +1 -1
  11. package/out/app.html +1 -1
  12. package/out/app.txt +2 -2
  13. package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +2 -2
  14. package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
  15. package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
  16. package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
  17. package/out/blog.html +2 -2
  18. package/out/blog.txt +1 -1
  19. package/out/careers.html +2 -2
  20. package/out/careers.txt +1 -1
  21. package/out/changelog.html +2 -2
  22. package/out/changelog.txt +1 -1
  23. package/out/cloud/link.html +1 -1
  24. package/out/cloud/link.txt +2 -2
  25. package/out/complete-profile.html +2 -2
  26. package/out/complete-profile.txt +1 -1
  27. package/out/connect-repos.html +1 -1
  28. package/out/connect-repos.txt +1 -1
  29. package/out/contact.html +2 -2
  30. package/out/contact.txt +1 -1
  31. package/out/docs.html +2 -2
  32. package/out/docs.txt +1 -1
  33. package/out/history.html +1 -1
  34. package/out/history.txt +2 -2
  35. package/out/index.html +1 -1
  36. package/out/index.txt +2 -2
  37. package/out/login.html +2 -2
  38. package/out/login.txt +1 -1
  39. package/out/metrics.html +1 -1
  40. package/out/metrics.txt +2 -2
  41. package/out/pricing.html +2 -2
  42. package/out/pricing.txt +1 -1
  43. package/out/privacy.html +2 -2
  44. package/out/privacy.txt +1 -1
  45. package/out/providers/setup/claude.html +1 -1
  46. package/out/providers/setup/claude.txt +1 -1
  47. package/out/providers/setup/codex.html +1 -1
  48. package/out/providers/setup/codex.txt +1 -1
  49. package/out/providers/setup/cursor.html +1 -1
  50. package/out/providers/setup/cursor.txt +1 -1
  51. package/out/providers.html +1 -1
  52. package/out/providers.txt +1 -1
  53. package/out/security.html +2 -2
  54. package/out/security.txt +1 -1
  55. package/out/signup.html +2 -2
  56. package/out/signup.txt +1 -1
  57. package/out/terms.html +2 -2
  58. package/out/terms.txt +1 -1
  59. package/package.json +7 -1
  60. package/src/app/about/page.tsx +7 -0
  61. package/src/app/app/[[...slug]]/DashboardPageClient.tsx +853 -0
  62. package/src/app/app/[[...slug]]/page.tsx +23 -0
  63. package/src/app/app/onboarding/page.tsx +394 -0
  64. package/src/app/apple-icon.png +0 -0
  65. package/src/app/blog/go-to-bed-wake-up-to-a-finished-product/page.tsx +88 -0
  66. package/src/app/blog/let-them-cook-multi-agent-orchestration/page.tsx +93 -0
  67. package/src/app/blog/page.tsx +15 -0
  68. package/src/app/careers/page.tsx +7 -0
  69. package/src/app/changelog/page.tsx +7 -0
  70. package/src/app/cloud/link/page.tsx +464 -0
  71. package/src/app/complete-profile/page.tsx +204 -0
  72. package/src/app/connect-repos/page.tsx +410 -0
  73. package/src/app/contact/page.tsx +7 -0
  74. package/src/app/docs/page.tsx +7 -0
  75. package/src/app/favicon.png +0 -0
  76. package/src/app/globals.css +200 -0
  77. package/src/app/history/page.tsx +658 -0
  78. package/src/app/layout.tsx +25 -0
  79. package/src/app/login/page.tsx +424 -0
  80. package/src/app/metrics/page.tsx +781 -0
  81. package/src/app/page.tsx +59 -0
  82. package/src/app/pricing/page.tsx +7 -0
  83. package/src/app/privacy/page.tsx +7 -0
  84. package/src/app/providers/page.tsx +193 -0
  85. package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +197 -0
  86. package/src/app/providers/setup/[provider]/constants.ts +35 -0
  87. package/src/app/providers/setup/[provider]/page.tsx +42 -0
  88. package/src/app/security/page.tsx +7 -0
  89. package/src/app/signup/page.tsx +533 -0
  90. package/src/app/terms/page.tsx +7 -0
  91. package/src/components/ActivityFeed.tsx +216 -0
  92. package/src/components/AddWorkspaceModal.tsx +170 -0
  93. package/src/components/AgentCard.test.tsx +134 -0
  94. package/src/components/AgentCard.tsx +585 -0
  95. package/src/components/AgentList.test.tsx +147 -0
  96. package/src/components/AgentList.tsx +419 -0
  97. package/src/components/AgentLogPreview.tsx +173 -0
  98. package/src/components/AgentProfilePanel.tsx +569 -0
  99. package/src/components/App.tsx +3424 -0
  100. package/src/components/BillingPanel.tsx +922 -0
  101. package/src/components/BillingResult.tsx +447 -0
  102. package/src/components/BroadcastComposer.tsx +690 -0
  103. package/src/components/ChannelAdminPanel.tsx +773 -0
  104. package/src/components/ChannelBrowser.tsx +385 -0
  105. package/src/components/ChannelChat.tsx +261 -0
  106. package/src/components/ChannelSidebar.tsx +399 -0
  107. package/src/components/CloudSessionProvider.tsx +130 -0
  108. package/src/components/CommandPalette.tsx +815 -0
  109. package/src/components/ConfirmationDialog.tsx +133 -0
  110. package/src/components/ConversationHistory.tsx +518 -0
  111. package/src/components/CoordinatorPanel.tsx +956 -0
  112. package/src/components/DecisionQueue.tsx +717 -0
  113. package/src/components/DirectMessageView.tsx +164 -0
  114. package/src/components/FileAutocomplete.tsx +368 -0
  115. package/src/components/FleetOverview.tsx +278 -0
  116. package/src/components/LogViewer.tsx +310 -0
  117. package/src/components/LogViewerPanel.tsx +482 -0
  118. package/src/components/Logo.tsx +284 -0
  119. package/src/components/MentionAutocomplete.tsx +384 -0
  120. package/src/components/MessageComposer.tsx +473 -0
  121. package/src/components/MessageList.tsx +725 -0
  122. package/src/components/MessageSenderName.tsx +91 -0
  123. package/src/components/MessageStatusIndicator.tsx +142 -0
  124. package/src/components/NewConversationModal.tsx +400 -0
  125. package/src/components/NotificationToast.tsx +488 -0
  126. package/src/components/OnlineUsersIndicator.tsx +164 -0
  127. package/src/components/Pagination.tsx +124 -0
  128. package/src/components/PricingPlans.tsx +386 -0
  129. package/src/components/ProjectList.tsx +711 -0
  130. package/src/components/ProviderAuthFlow.tsx +343 -0
  131. package/src/components/ProviderConnectionList.tsx +375 -0
  132. package/src/components/ProvisioningProgress.tsx +730 -0
  133. package/src/components/ReactionChips.tsx +70 -0
  134. package/src/components/ReactionPicker.tsx +121 -0
  135. package/src/components/RepoAccessPanel.tsx +787 -0
  136. package/src/components/RepositoriesPanel.tsx +901 -0
  137. package/src/components/ServerCard.tsx +202 -0
  138. package/src/components/SessionExpiredModal.tsx +128 -0
  139. package/src/components/SpawnModal.test.tsx +190 -0
  140. package/src/components/SpawnModal.tsx +1001 -0
  141. package/src/components/TaskAssignmentUI.tsx +375 -0
  142. package/src/components/TerminalProviderSetup.tsx +517 -0
  143. package/src/components/ThemeProvider.tsx +159 -0
  144. package/src/components/ThinkingIndicator.tsx +231 -0
  145. package/src/components/ThreadList.tsx +198 -0
  146. package/src/components/ThreadPanel.tsx +405 -0
  147. package/src/components/TrajectoryViewer.tsx +698 -0
  148. package/src/components/TypingIndicator.tsx +69 -0
  149. package/src/components/UsageBanner.tsx +231 -0
  150. package/src/components/UserProfilePanel.tsx +233 -0
  151. package/src/components/WorkspaceContext.tsx +95 -0
  152. package/src/components/WorkspaceSelector.tsx +234 -0
  153. package/src/components/WorkspaceStatusIndicator.tsx +396 -0
  154. package/src/components/XTermInteractive.tsx +516 -0
  155. package/src/components/XTermLogViewer.tsx +719 -0
  156. package/src/components/channels/ChannelDialogs.tsx +1411 -0
  157. package/src/components/channels/ChannelHeader.tsx +317 -0
  158. package/src/components/channels/ChannelMessageList.tsx +463 -0
  159. package/src/components/channels/ChannelViewV1.tsx +146 -0
  160. package/src/components/channels/MessageInput.tsx +302 -0
  161. package/src/components/channels/SearchInput.tsx +172 -0
  162. package/src/components/channels/SearchResults.tsx +336 -0
  163. package/src/components/channels/api.test.ts +1527 -0
  164. package/src/components/channels/api.ts +703 -0
  165. package/src/components/channels/index.ts +76 -0
  166. package/src/components/channels/mockApi.ts +344 -0
  167. package/src/components/channels/types.ts +566 -0
  168. package/src/components/hooks/index.ts +58 -0
  169. package/src/components/hooks/useAgentLogs.ts +504 -0
  170. package/src/components/hooks/useAgents.ts +127 -0
  171. package/src/components/hooks/useBroadcastDedup.test.ts +371 -0
  172. package/src/components/hooks/useBroadcastDedup.ts +86 -0
  173. package/src/components/hooks/useChannelAdmin.ts +329 -0
  174. package/src/components/hooks/useChannelBrowser.ts +239 -0
  175. package/src/components/hooks/useChannelCommands.ts +138 -0
  176. package/src/components/hooks/useChannels.ts +367 -0
  177. package/src/components/hooks/useDebounce.ts +29 -0
  178. package/src/components/hooks/useDirectMessage.test.ts +952 -0
  179. package/src/components/hooks/useDirectMessage.ts +141 -0
  180. package/src/components/hooks/useMessages.ts +310 -0
  181. package/src/components/hooks/useOrchestrator.test.ts +165 -0
  182. package/src/components/hooks/useOrchestrator.ts +424 -0
  183. package/src/components/hooks/usePinnedAgents.test.ts +356 -0
  184. package/src/components/hooks/usePinnedAgents.ts +140 -0
  185. package/src/components/hooks/usePresence.test.ts +245 -0
  186. package/src/components/hooks/usePresence.ts +377 -0
  187. package/src/components/hooks/useRecentRepos.ts +130 -0
  188. package/src/components/hooks/useSession.ts +209 -0
  189. package/src/components/hooks/useThread.ts +138 -0
  190. package/src/components/hooks/useTrajectory.ts +265 -0
  191. package/src/components/hooks/useWebSocket.ts +290 -0
  192. package/src/components/hooks/useWorkspaceMembers.ts +132 -0
  193. package/src/components/hooks/useWorkspaceRepos.ts +73 -0
  194. package/src/components/hooks/useWorkspaceStatus.ts +237 -0
  195. package/src/components/index.ts +81 -0
  196. package/src/components/layout/Header.tsx +311 -0
  197. package/src/components/layout/RepoContextHeader.tsx +361 -0
  198. package/src/components/layout/Sidebar.archive.test.tsx +126 -0
  199. package/src/components/layout/Sidebar.test.tsx +691 -0
  200. package/src/components/layout/Sidebar.tsx +900 -0
  201. package/src/components/layout/index.ts +7 -0
  202. package/src/components/settings/BillingSettingsPanel.tsx +564 -0
  203. package/src/components/settings/SettingsPage.tsx +683 -0
  204. package/src/components/settings/TeamSettingsPanel.tsx +560 -0
  205. package/src/components/settings/WorkspaceSettingsPanel.tsx +1368 -0
  206. package/src/components/settings/index.ts +11 -0
  207. package/src/components/settings/types.ts +79 -0
  208. package/src/components/utils/messageFormatting.test.tsx +331 -0
  209. package/src/components/utils/messageFormatting.tsx +597 -0
  210. package/src/index.ts +63 -0
  211. package/src/landing/AboutPage.tsx +77 -0
  212. package/src/landing/BlogContent.tsx +187 -0
  213. package/src/landing/BlogPage.tsx +47 -0
  214. package/src/landing/CareersPage.tsx +53 -0
  215. package/src/landing/ChangelogPage.tsx +33 -0
  216. package/src/landing/ContactPage.tsx +41 -0
  217. package/src/landing/DocsPage.tsx +43 -0
  218. package/src/landing/LandingPage.tsx +702 -0
  219. package/src/landing/PricingPage.tsx +549 -0
  220. package/src/landing/PrivacyPage.tsx +117 -0
  221. package/src/landing/SecurityPage.tsx +42 -0
  222. package/src/landing/StaticPage.tsx +165 -0
  223. package/src/landing/TermsPage.tsx +125 -0
  224. package/src/landing/blogData.ts +312 -0
  225. package/src/landing/index.ts +18 -0
  226. package/src/landing/styles.css +3673 -0
  227. package/src/lib/agent-merge.test.ts +43 -0
  228. package/src/lib/agent-merge.ts +35 -0
  229. package/src/lib/api.ts +1294 -0
  230. package/src/lib/cloudApi.ts +893 -0
  231. package/src/lib/colors.test.ts +175 -0
  232. package/src/lib/colors.ts +218 -0
  233. package/src/lib/config.ts +109 -0
  234. package/src/lib/hierarchy.ts +242 -0
  235. package/src/lib/stuckDetection.ts +142 -0
  236. package/src/lib/useUrlRouting.ts +190 -0
  237. package/src/types/index.ts +317 -0
  238. package/src/types/threading.ts +7 -0
  239. package/out/_next/static/chunks/285-dc644487a8d6500d.js +0 -1
  240. package/out/_next/static/css/4c58d9cf493aa626.css +0 -1
  241. /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_buildManifest.js +0 -0
  242. /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_ssgManifest.js +0 -0
  243. /package/out/_next/static/chunks/{528-d375bc8b46912d2c.js → 528-f5f676996d613c25.js} +0 -0
  244. /package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/{page-a58308f43557b908.js → page-b194f207fbd91862.js} +0 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Pagination Component
3
+ *
4
+ * A reusable pagination control with page numbers and navigation.
5
+ */
6
+
7
+ import React, { useMemo } from 'react';
8
+
9
+ export interface PaginationProps {
10
+ currentPage: number;
11
+ totalPages: number;
12
+ onPageChange: (page: number) => void;
13
+ maxVisiblePages?: number;
14
+ }
15
+
16
+ export function Pagination({
17
+ currentPage,
18
+ totalPages,
19
+ onPageChange,
20
+ maxVisiblePages = 5,
21
+ }: PaginationProps) {
22
+ const pages = useMemo(() => {
23
+ if (totalPages <= maxVisiblePages) {
24
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
25
+ }
26
+
27
+ const half = Math.floor(maxVisiblePages / 2);
28
+ let start = Math.max(1, currentPage - half);
29
+ let end = Math.min(totalPages, start + maxVisiblePages - 1);
30
+
31
+ if (end - start + 1 < maxVisiblePages) {
32
+ start = Math.max(1, end - maxVisiblePages + 1);
33
+ }
34
+
35
+ const result: (number | 'ellipsis-start' | 'ellipsis-end')[] = [];
36
+
37
+ if (start > 1) {
38
+ result.push(1);
39
+ if (start > 2) {
40
+ result.push('ellipsis-start');
41
+ }
42
+ }
43
+
44
+ for (let i = start; i <= end; i++) {
45
+ result.push(i);
46
+ }
47
+
48
+ if (end < totalPages) {
49
+ if (end < totalPages - 1) {
50
+ result.push('ellipsis-end');
51
+ }
52
+ result.push(totalPages);
53
+ }
54
+
55
+ return result;
56
+ }, [currentPage, totalPages, maxVisiblePages]);
57
+
58
+ if (totalPages <= 1) {
59
+ return null;
60
+ }
61
+
62
+ return (
63
+ <nav className="flex items-center justify-center gap-1" aria-label="Pagination">
64
+ {/* Previous button */}
65
+ <button
66
+ onClick={() => onPageChange(currentPage - 1)}
67
+ disabled={currentPage === 1}
68
+ className="px-2 py-1.5 text-sm rounded-md border border-sidebar-border text-text-secondary hover:bg-sidebar-border disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
69
+ aria-label="Previous page"
70
+ >
71
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
72
+ <polyline points="15 18 9 12 15 6" />
73
+ </svg>
74
+ </button>
75
+
76
+ {/* Page numbers */}
77
+ <div className="flex items-center gap-1">
78
+ {pages.map((page, index) => {
79
+ if (page === 'ellipsis-start' || page === 'ellipsis-end') {
80
+ return (
81
+ <span
82
+ key={page}
83
+ className="px-2 py-1 text-sm text-text-muted"
84
+ >
85
+ ...
86
+ </span>
87
+ );
88
+ }
89
+
90
+ const isActive = page === currentPage;
91
+ return (
92
+ <button
93
+ key={page}
94
+ onClick={() => onPageChange(page)}
95
+ className={`
96
+ min-w-[32px] px-2 py-1.5 text-sm rounded-md border transition-colors
97
+ ${isActive
98
+ ? 'bg-accent-cyan text-bg-deep border-accent-cyan font-medium'
99
+ : 'border-sidebar-border text-text-secondary hover:bg-sidebar-border'
100
+ }
101
+ `}
102
+ aria-label={`Page ${page}`}
103
+ aria-current={isActive ? 'page' : undefined}
104
+ >
105
+ {page}
106
+ </button>
107
+ );
108
+ })}
109
+ </div>
110
+
111
+ {/* Next button */}
112
+ <button
113
+ onClick={() => onPageChange(currentPage + 1)}
114
+ disabled={currentPage === totalPages}
115
+ className="px-2 py-1.5 text-sm rounded-md border border-sidebar-border text-text-secondary hover:bg-sidebar-border disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
116
+ aria-label="Next page"
117
+ >
118
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
119
+ <polyline points="9 18 15 12 9 6" />
120
+ </svg>
121
+ </button>
122
+ </nav>
123
+ );
124
+ }
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Pricing Plans Component
3
+ *
4
+ * Displays available subscription plans with features and pricing.
5
+ */
6
+
7
+ import React, { useState } from 'react';
8
+
9
+ export interface Plan {
10
+ id: string;
11
+ name: string;
12
+ description: string;
13
+ priceMonthly: number;
14
+ priceYearly: number;
15
+ limits: {
16
+ maxWorkspaces: number;
17
+ maxAgentsPerWorkspace: number;
18
+ maxTeamMembers: number;
19
+ maxStorageGB: number;
20
+ maxComputeHoursPerMonth: number;
21
+ customDomains: boolean;
22
+ prioritySupport: boolean;
23
+ sla: boolean;
24
+ ssoEnabled: boolean;
25
+ auditLogs: boolean;
26
+ };
27
+ features: string[];
28
+ }
29
+
30
+ export interface PricingPlansProps {
31
+ plans: Plan[];
32
+ currentPlan?: string;
33
+ onSelectPlan: (planId: string, interval: 'month' | 'year') => void;
34
+ isLoading?: boolean;
35
+ }
36
+
37
+ export function PricingPlans({
38
+ plans,
39
+ currentPlan = 'free',
40
+ onSelectPlan,
41
+ isLoading = false,
42
+ }: PricingPlansProps) {
43
+ const [billingInterval, setBillingInterval] = useState<'month' | 'year'>('month');
44
+
45
+ const formatPrice = (cents: number) => {
46
+ if (cents === 0) return 'Free';
47
+ return `$${(cents / 100).toFixed(0)}`;
48
+ };
49
+
50
+ const formatLimit = (value: number) => {
51
+ if (value === -1) return 'Unlimited';
52
+ return value.toString();
53
+ };
54
+
55
+ return (
56
+ <div className="pricing-plans">
57
+ {/* Billing Toggle */}
58
+ <div className="billing-toggle">
59
+ <button
60
+ className={`toggle-btn ${billingInterval === 'month' ? 'active' : ''}`}
61
+ onClick={() => setBillingInterval('month')}
62
+ >
63
+ Monthly
64
+ </button>
65
+ <button
66
+ className={`toggle-btn ${billingInterval === 'year' ? 'active' : ''}`}
67
+ onClick={() => setBillingInterval('year')}
68
+ >
69
+ Yearly
70
+ <span className="save-badge">Save 17%</span>
71
+ </button>
72
+ </div>
73
+
74
+ {/* Plans Grid */}
75
+ <div className="plans-grid">
76
+ {plans.map((plan) => {
77
+ const isCurrent = plan.id === currentPlan;
78
+ const price = billingInterval === 'month' ? plan.priceMonthly : plan.priceYearly;
79
+ const monthlyEquivalent = billingInterval === 'year' ? plan.priceYearly / 12 : plan.priceMonthly;
80
+
81
+ return (
82
+ <div
83
+ key={plan.id}
84
+ className={`plan-card ${isCurrent ? 'current' : ''} ${plan.id === 'pro' ? 'popular' : ''}`}
85
+ >
86
+ {plan.id === 'pro' && <div className="popular-badge">Most Popular</div>}
87
+ {isCurrent && <div className="current-badge">Current Plan</div>}
88
+
89
+ <div className="plan-header">
90
+ <h3 className="plan-name">{plan.name}</h3>
91
+ <p className="plan-description">{plan.description}</p>
92
+ </div>
93
+
94
+ <div className="plan-pricing">
95
+ <span className="plan-price">{formatPrice(monthlyEquivalent)}</span>
96
+ {price > 0 && (
97
+ <span className="plan-period">
98
+ /month{billingInterval === 'year' && ', billed yearly'}
99
+ </span>
100
+ )}
101
+ </div>
102
+
103
+ <ul className="plan-features">
104
+ {plan.features.map((feature, i) => (
105
+ <li key={i}>
106
+ <CheckIcon />
107
+ <span>{feature}</span>
108
+ </li>
109
+ ))}
110
+ </ul>
111
+
112
+ <div className="plan-limits">
113
+ <div className="limit-item">
114
+ <span className="limit-label">Workspaces</span>
115
+ <span className="limit-value">{formatLimit(plan.limits.maxWorkspaces)}</span>
116
+ </div>
117
+ <div className="limit-item">
118
+ <span className="limit-label">Agents/Workspace</span>
119
+ <span className="limit-value">{formatLimit(plan.limits.maxAgentsPerWorkspace)}</span>
120
+ </div>
121
+ <div className="limit-item">
122
+ <span className="limit-label">Team Members</span>
123
+ <span className="limit-value">{formatLimit(plan.limits.maxTeamMembers)}</span>
124
+ </div>
125
+ <div className="limit-item">
126
+ <span className="limit-label">Storage</span>
127
+ <span className="limit-value">{formatLimit(plan.limits.maxStorageGB)} GB</span>
128
+ </div>
129
+ </div>
130
+
131
+ <button
132
+ className={`plan-button ${isCurrent ? 'current' : ''} ${plan.id === 'free' ? 'free' : ''}`}
133
+ onClick={() => onSelectPlan(plan.id, billingInterval)}
134
+ disabled={isCurrent || isLoading}
135
+ >
136
+ {isLoading ? (
137
+ 'Loading...'
138
+ ) : isCurrent ? (
139
+ 'Current Plan'
140
+ ) : plan.id === 'free' ? (
141
+ 'Downgrade'
142
+ ) : plan.id === 'enterprise' ? (
143
+ 'Contact Sales'
144
+ ) : (
145
+ 'Upgrade'
146
+ )}
147
+ </button>
148
+ </div>
149
+ );
150
+ })}
151
+ </div>
152
+ </div>
153
+ );
154
+ }
155
+
156
+ function CheckIcon() {
157
+ return (
158
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
159
+ <polyline points="20 6 9 17 4 12" />
160
+ </svg>
161
+ );
162
+ }
163
+
164
+ export const pricingPlansStyles = `
165
+ .pricing-plans {
166
+ max-width: 1200px;
167
+ margin: 0 auto;
168
+ padding: 24px;
169
+ }
170
+
171
+ .billing-toggle {
172
+ display: flex;
173
+ justify-content: center;
174
+ gap: 4px;
175
+ margin-bottom: 32px;
176
+ background: #2a2a3e;
177
+ padding: 4px;
178
+ border-radius: 8px;
179
+ width: fit-content;
180
+ margin-left: auto;
181
+ margin-right: auto;
182
+ }
183
+
184
+ .toggle-btn {
185
+ padding: 10px 20px;
186
+ background: transparent;
187
+ border: none;
188
+ border-radius: 6px;
189
+ color: #8d8d8e;
190
+ font-size: 14px;
191
+ cursor: pointer;
192
+ transition: all 0.2s;
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 8px;
196
+ }
197
+
198
+ .toggle-btn.active {
199
+ background: #00c896;
200
+ color: #1a1a2e;
201
+ }
202
+
203
+ .save-badge {
204
+ font-size: 11px;
205
+ padding: 2px 6px;
206
+ background: rgba(0, 200, 150, 0.2);
207
+ border-radius: 4px;
208
+ color: #00c896;
209
+ }
210
+
211
+ .toggle-btn.active .save-badge {
212
+ background: rgba(26, 26, 46, 0.3);
213
+ color: #1a1a2e;
214
+ }
215
+
216
+ .plans-grid {
217
+ display: grid;
218
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
219
+ gap: 24px;
220
+ }
221
+
222
+ .plan-card {
223
+ background: #1a1a2e;
224
+ border: 1px solid #2a2a3e;
225
+ border-radius: 12px;
226
+ padding: 24px;
227
+ position: relative;
228
+ transition: all 0.2s;
229
+ }
230
+
231
+ .plan-card:hover {
232
+ border-color: #3a3a4e;
233
+ }
234
+
235
+ .plan-card.popular {
236
+ border-color: #00c896;
237
+ }
238
+
239
+ .plan-card.current {
240
+ border-color: #1264a3;
241
+ }
242
+
243
+ .popular-badge,
244
+ .current-badge {
245
+ position: absolute;
246
+ top: -12px;
247
+ left: 50%;
248
+ transform: translateX(-50%);
249
+ padding: 4px 12px;
250
+ border-radius: 12px;
251
+ font-size: 12px;
252
+ font-weight: 500;
253
+ white-space: nowrap;
254
+ }
255
+
256
+ .popular-badge {
257
+ background: #00c896;
258
+ color: #1a1a2e;
259
+ }
260
+
261
+ .current-badge {
262
+ background: #1264a3;
263
+ color: white;
264
+ }
265
+
266
+ .plan-header {
267
+ margin-bottom: 20px;
268
+ }
269
+
270
+ .plan-name {
271
+ margin: 0 0 8px;
272
+ font-size: 20px;
273
+ font-weight: 600;
274
+ color: #e8e8e8;
275
+ }
276
+
277
+ .plan-description {
278
+ margin: 0;
279
+ font-size: 14px;
280
+ color: #8d8d8e;
281
+ }
282
+
283
+ .plan-pricing {
284
+ margin-bottom: 24px;
285
+ }
286
+
287
+ .plan-price {
288
+ font-size: 36px;
289
+ font-weight: 700;
290
+ color: #e8e8e8;
291
+ }
292
+
293
+ .plan-period {
294
+ font-size: 14px;
295
+ color: #8d8d8e;
296
+ margin-left: 4px;
297
+ }
298
+
299
+ .plan-features {
300
+ list-style: none;
301
+ padding: 0;
302
+ margin: 0 0 24px;
303
+ }
304
+
305
+ .plan-features li {
306
+ display: flex;
307
+ align-items: flex-start;
308
+ gap: 10px;
309
+ padding: 8px 0;
310
+ font-size: 14px;
311
+ color: #b8b8b8;
312
+ }
313
+
314
+ .plan-features li svg {
315
+ color: #00c896;
316
+ flex-shrink: 0;
317
+ margin-top: 2px;
318
+ }
319
+
320
+ .plan-limits {
321
+ padding: 16px 0;
322
+ border-top: 1px solid #2a2a3e;
323
+ margin-bottom: 24px;
324
+ }
325
+
326
+ .limit-item {
327
+ display: flex;
328
+ justify-content: space-between;
329
+ padding: 6px 0;
330
+ font-size: 13px;
331
+ }
332
+
333
+ .limit-label {
334
+ color: #8d8d8e;
335
+ }
336
+
337
+ .limit-value {
338
+ color: #e8e8e8;
339
+ font-weight: 500;
340
+ }
341
+
342
+ .plan-button {
343
+ width: 100%;
344
+ padding: 12px 24px;
345
+ border: none;
346
+ border-radius: 8px;
347
+ font-size: 14px;
348
+ font-weight: 500;
349
+ cursor: pointer;
350
+ transition: all 0.2s;
351
+ background: #00c896;
352
+ color: #1a1a2e;
353
+ }
354
+
355
+ .plan-button:hover:not(:disabled) {
356
+ background: #00a87d;
357
+ }
358
+
359
+ .plan-button.current {
360
+ background: #2a2a3e;
361
+ color: #8d8d8e;
362
+ cursor: default;
363
+ }
364
+
365
+ .plan-button.free {
366
+ background: transparent;
367
+ border: 1px solid #3a3a4e;
368
+ color: #8d8d8e;
369
+ }
370
+
371
+ .plan-button.free:hover:not(:disabled) {
372
+ background: rgba(255, 255, 255, 0.05);
373
+ border-color: #4a4a5e;
374
+ }
375
+
376
+ .plan-button:disabled {
377
+ opacity: 0.6;
378
+ cursor: not-allowed;
379
+ }
380
+
381
+ @media (max-width: 768px) {
382
+ .plans-grid {
383
+ grid-template-columns: 1fr;
384
+ }
385
+ }
386
+ `;