groove-dev 0.27.142 → 0.27.144

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 (187) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
  4. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
  5. package/node_modules/@groove-dev/daemon/src/index.js +3 -0
  6. package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
  7. package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
  8. package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
  9. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  10. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  11. package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
  12. package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
  13. package/node_modules/@groove-dev/daemon/src/routes/agents.js +889 -0
  14. package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
  15. package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
  16. package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
  17. package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
  18. package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
  19. package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
  20. package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
  21. package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
  22. package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
  23. package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
  24. package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
  25. package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
  26. package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
  27. package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
  28. package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
  29. package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
  30. package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  31. package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  32. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  33. package/node_modules/@groove-dev/gui/package.json +1 -1
  34. package/{packages/gui/src/app.jsx → node_modules/@groove-dev/gui/src/App.jsx} +0 -2
  35. package/node_modules/@groove-dev/gui/src/app.css +35 -0
  36. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
  37. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +144 -31
  38. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
  39. package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
  40. package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
  41. package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
  42. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
  43. package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
  44. package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
  45. package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
  46. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
  47. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
  48. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  49. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
  50. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
  51. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  52. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  53. package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
  54. package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
  55. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
  56. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
  57. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +195 -14
  58. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +286 -102
  59. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
  60. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
  61. package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
  62. package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
  63. package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
  64. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
  65. package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
  66. package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
  67. package/node_modules/@groove-dev/gui/src/lib/status.js +24 -24
  68. package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
  69. package/node_modules/@groove-dev/gui/src/stores/groove.js +34 -3144
  70. package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
  71. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +452 -0
  72. package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
  73. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +227 -0
  74. package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
  75. package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
  76. package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
  77. package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
  78. package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
  79. package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
  80. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
  81. package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
  82. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
  83. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
  84. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +17 -6
  85. package/node_modules/@groove-dev/gui/src/views/models.jsx +410 -509
  86. package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
  87. package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
  88. package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
  89. package/package.json +1 -1
  90. package/packages/cli/package.json +1 -1
  91. package/packages/daemon/package.json +1 -1
  92. package/packages/daemon/src/api.js +1086 -6532
  93. package/packages/daemon/src/gateways/manager.js +35 -1
  94. package/packages/daemon/src/index.js +3 -0
  95. package/packages/daemon/src/journalist.js +23 -13
  96. package/packages/daemon/src/mlx-server.js +365 -0
  97. package/packages/daemon/src/model-lab.js +308 -12
  98. package/packages/daemon/src/pm.js +1 -1
  99. package/packages/daemon/src/process.js +2 -2
  100. package/packages/daemon/src/providers/local.js +36 -8
  101. package/packages/daemon/src/registry.js +21 -5
  102. package/packages/daemon/src/routes/agents.js +889 -0
  103. package/packages/daemon/src/routes/coordination.js +318 -0
  104. package/packages/daemon/src/routes/files.js +751 -0
  105. package/packages/daemon/src/routes/integrations.js +485 -0
  106. package/packages/daemon/src/routes/network.js +1784 -0
  107. package/packages/daemon/src/routes/providers.js +755 -0
  108. package/packages/daemon/src/routes/schedules.js +110 -0
  109. package/packages/daemon/src/routes/teams.js +650 -0
  110. package/packages/daemon/src/scheduler.js +456 -24
  111. package/packages/daemon/src/teams.js +1 -1
  112. package/packages/daemon/src/validate.js +38 -1
  113. package/packages/daemon/templates/mlx-setup.json +12 -0
  114. package/packages/daemon/templates/tgi-setup.json +1 -1
  115. package/packages/daemon/templates/vllm-setup.json +1 -1
  116. package/packages/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  117. package/packages/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  118. package/packages/gui/dist/index.html +2 -2
  119. package/packages/gui/package.json +1 -1
  120. package/{node_modules/@groove-dev/gui/src/app.jsx → packages/gui/src/App.jsx} +0 -2
  121. package/packages/gui/src/app.css +35 -0
  122. package/packages/gui/src/components/agents/agent-config.jsx +1 -128
  123. package/packages/gui/src/components/agents/agent-feed.jsx +144 -31
  124. package/packages/gui/src/components/agents/agent-node.jsx +8 -13
  125. package/packages/gui/src/components/agents/code-review.jsx +159 -122
  126. package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
  127. package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
  128. package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
  129. package/packages/gui/src/components/automations/automation-card.jsx +274 -0
  130. package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
  131. package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
  132. package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
  133. package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
  134. package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  135. package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
  136. package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
  137. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  138. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  139. package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
  140. package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
  141. package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
  142. package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
  143. package/packages/gui/src/components/lab/parameter-panel.jsx +195 -14
  144. package/packages/gui/src/components/lab/runtime-config.jsx +286 -102
  145. package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
  146. package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
  147. package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
  148. package/packages/gui/src/components/network/network-health.jsx +2 -2
  149. package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
  150. package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
  151. package/packages/gui/src/components/ui/sheet.jsx +5 -2
  152. package/packages/gui/src/lib/cron.js +64 -0
  153. package/packages/gui/src/lib/status.js +24 -24
  154. package/packages/gui/src/lib/theme-hex.js +1 -0
  155. package/packages/gui/src/stores/groove.js +34 -3144
  156. package/packages/gui/src/stores/helpers.js +10 -0
  157. package/packages/gui/src/stores/slices/agents-slice.js +452 -0
  158. package/packages/gui/src/stores/slices/automations-slice.js +96 -0
  159. package/packages/gui/src/stores/slices/chat-slice.js +227 -0
  160. package/packages/gui/src/stores/slices/editor-slice.js +285 -0
  161. package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
  162. package/packages/gui/src/stores/slices/network-slice.js +361 -0
  163. package/packages/gui/src/stores/slices/preview-slice.js +109 -0
  164. package/packages/gui/src/stores/slices/providers-slice.js +897 -0
  165. package/packages/gui/src/stores/slices/teams-slice.js +413 -0
  166. package/packages/gui/src/stores/slices/ui-slice.js +98 -0
  167. package/packages/gui/src/views/agents.jsx +5 -5
  168. package/packages/gui/src/views/dashboard.jsx +12 -13
  169. package/packages/gui/src/views/marketplace.jsx +191 -3
  170. package/packages/gui/src/views/model-lab.jsx +17 -6
  171. package/packages/gui/src/views/models.jsx +410 -509
  172. package/packages/gui/src/views/network.jsx +3 -3
  173. package/packages/gui/src/views/settings.jsx +81 -94
  174. package/packages/gui/src/views/teams.jsx +40 -483
  175. package/SECURITY_SWEEP.md +0 -228
  176. package/TRAINING_DATA_v4.md +0 -6
  177. package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +0 -984
  178. package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +0 -1
  179. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -322
  180. package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
  181. package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
  182. package/packages/gui/dist/assets/index-Bjd91ufV.js +0 -984
  183. package/packages/gui/dist/assets/index-BqdwIFn4.css +0 -1
  184. package/packages/gui/src/components/agents/agent-chat.jsx +0 -322
  185. package/packages/gui/src/views/preview.jsx +0 -6
  186. package/packages/gui/src/views/subscription-panel.jsx +0 -327
  187. package/test.py +0 -571
@@ -1,327 +0,0 @@
1
- // FSL-1.1-Apache-2.0 — see LICENSE
2
- import { useState, useEffect } from 'react';
3
- import { useGrooveStore } from '../stores/groove';
4
- import { Badge } from '../components/ui/badge';
5
- import { Button } from '../components/ui/button';
6
- import { cn } from '../lib/cn';
7
- import {
8
- Crown, Sparkles, Users, Check, CreditCard, AlertTriangle,
9
- Minus, Plus, Shield, Radio, Cloud, Server, Headphones,
10
- } from 'lucide-react';
11
-
12
- const FEATURE_LABELS = {
13
- 'remote-access': { label: 'Remote Access', icon: Radio },
14
- 'federation': { label: 'Federation', icon: Server },
15
- 'cloud-teams': { label: 'Cloud Teams', icon: Cloud },
16
- 'cloud-backup': { label: 'Cloud Backup', icon: Shield },
17
- 'shared-workspace': { label: 'Shared Workspace', icon: Users },
18
- 'admin-controls': { label: 'Admin Controls', icon: Shield },
19
- 'priority-support': { label: 'Priority Support', icon: Headphones },
20
- };
21
-
22
- function formatDate(iso) {
23
- if (!iso) return '';
24
- return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
25
- }
26
-
27
- function PlanBadge({ plan, status, cancelAtPeriodEnd }) {
28
- if (status === 'past_due') {
29
- return <Badge variant="outline" className="border-warning/30 bg-warning/10 text-warning text-2xs">Payment issue</Badge>;
30
- }
31
- if (cancelAtPeriodEnd) {
32
- return <Badge variant="outline" className="border-warning/30 bg-warning/10 text-warning text-2xs">Cancels at period end</Badge>;
33
- }
34
- if (status === 'active' || status === 'trialing') {
35
- return <Badge variant="outline" className="border-success/30 bg-success/10 text-success text-2xs">Active</Badge>;
36
- }
37
- return null;
38
- }
39
-
40
- function FeatureList({ features }) {
41
- return (
42
- <div className="flex flex-wrap gap-1.5 mt-3">
43
- {features.map((key) => {
44
- const f = FEATURE_LABELS[key] || { label: key, icon: Check };
45
- const Icon = f.icon;
46
- return (
47
- <span key={key} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-surface-3 text-2xs text-text-2 font-sans">
48
- <Icon size={10} className="text-accent" />
49
- {f.label}
50
- </span>
51
- );
52
- })}
53
- </div>
54
- );
55
- }
56
-
57
- function SeatControl({ seats, onChange }) {
58
- const [value, setValue] = useState(seats);
59
-
60
- useEffect(() => { setValue(seats); }, [seats]);
61
-
62
- const dec = () => { if (value > 1) { setValue(value - 1); onChange(value - 1); } };
63
- const inc = () => { if (value < 999) { setValue(value + 1); onChange(value + 1); } };
64
-
65
- return (
66
- <div className="mt-4 flex items-center gap-3">
67
- <span className="text-xs text-text-2 font-sans">Seats</span>
68
- <div className="flex items-center gap-1 bg-surface-0 rounded-md border border-border-subtle p-0.5">
69
- <button onClick={dec} disabled={value <= 1} className="w-6 h-6 flex items-center justify-center rounded text-text-3 hover:text-text-0 hover:bg-surface-3 transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed">
70
- <Minus size={12} />
71
- </button>
72
- <span className="w-8 text-center text-xs font-semibold text-text-0 font-mono">{value}</span>
73
- <button onClick={inc} disabled={value >= 999} className="w-6 h-6 flex items-center justify-center rounded text-text-3 hover:text-text-0 hover:bg-surface-3 transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed">
74
- <Plus size={12} />
75
- </button>
76
- </div>
77
- </div>
78
- );
79
- }
80
-
81
- function ActivePlanCard({ subscription }) {
82
- const openPortal = useGrooveStore((s) => s.openPortal);
83
- const updateSeats = useGrooveStore((s) => s.updateSeats);
84
- const planLabel = subscription.plan === 'team' ? 'Team Plan' : 'Pro Plan';
85
- const PlanIcon = subscription.plan === 'team' ? Users : Crown;
86
-
87
- return (
88
- <div className="rounded-md border border-border-subtle bg-surface-1 p-4">
89
- <div className="flex items-start justify-between">
90
- <div className="flex items-center gap-2.5">
91
- <div className="flex h-8 w-8 items-center justify-center rounded-md bg-purple/15">
92
- <PlanIcon size={16} className="text-purple" />
93
- </div>
94
- <div>
95
- <h4 className="text-sm font-semibold text-text-0 font-sans">{planLabel}</h4>
96
- <div className="flex items-center gap-2 mt-0.5">
97
- <PlanBadge plan={subscription.plan} status={subscription.status} cancelAtPeriodEnd={subscription.cancelAtPeriodEnd} />
98
- {subscription.periodEnd && (
99
- <span className="text-2xs text-text-3 font-sans">
100
- {subscription.cancelAtPeriodEnd ? 'Ends' : 'Renews'} {formatDate(subscription.periodEnd)}
101
- </span>
102
- )}
103
- </div>
104
- </div>
105
- </div>
106
- </div>
107
-
108
- {subscription.features?.length > 0 && (
109
- <FeatureList features={subscription.features} />
110
- )}
111
-
112
- {subscription.plan === 'team' && (
113
- <SeatControl seats={subscription.seats || 1} onChange={updateSeats} />
114
- )}
115
-
116
- {subscription.status === 'past_due' && (
117
- <div className="mt-3 flex items-center gap-2 rounded-md bg-warning/10 border border-warning/20 px-3 py-2">
118
- <AlertTriangle size={14} className="text-warning shrink-0" />
119
- <span className="text-2xs text-warning font-sans">There's an issue with your payment method.</span>
120
- </div>
121
- )}
122
-
123
- <div className="flex gap-2 mt-4">
124
- <Button size="sm" variant="ghost" onClick={openPortal} className="h-7 text-2xs gap-1.5 text-text-2 hover:text-accent">
125
- <CreditCard size={12} />
126
- Manage Subscription
127
- </Button>
128
- </div>
129
- </div>
130
- );
131
- }
132
-
133
- function PricingCard({ name, plan, price, interval, features, onUpgrade, highlighted }) {
134
- const PlanIcon = plan === 'team' ? Users : Sparkles;
135
- const perSeat = plan === 'team';
136
-
137
- return (
138
- <div className={cn(
139
- 'rounded-md border bg-surface-1 p-4 flex flex-col',
140
- highlighted ? 'border-accent/40' : 'border-border-subtle',
141
- )}>
142
- <div className="flex items-center gap-2 mb-1">
143
- <div className={cn(
144
- 'flex h-7 w-7 items-center justify-center rounded-md',
145
- highlighted ? 'bg-accent/15' : 'bg-purple/15',
146
- )}>
147
- <PlanIcon size={14} className={highlighted ? 'text-accent' : 'text-purple'} />
148
- </div>
149
- <h4 className="text-sm font-semibold text-text-0 font-sans">{name}</h4>
150
- </div>
151
-
152
- <div className="mt-2 mb-3">
153
- <span className="text-lg font-bold text-text-0 font-sans">${price}</span>
154
- <span className="text-2xs text-text-3 font-sans">/{interval === 'year' ? 'yr' : 'mo'}{perSeat ? '/seat' : ''}</span>
155
- </div>
156
-
157
- <div className="flex-1 space-y-1.5 mb-4">
158
- {features.map((key) => {
159
- const f = FEATURE_LABELS[key] || { label: key };
160
- return (
161
- <div key={key} className="flex items-center gap-1.5 text-2xs text-text-2 font-sans">
162
- <Check size={11} className="text-success shrink-0" />
163
- {f.label}
164
- </div>
165
- );
166
- })}
167
- </div>
168
-
169
- <Button
170
- size="sm"
171
- onClick={onUpgrade}
172
- className={cn(
173
- 'h-8 text-xs font-semibold w-full',
174
- highlighted
175
- ? 'bg-accent/15 text-accent hover:bg-accent/25'
176
- : 'bg-purple/15 text-purple hover:bg-purple/25',
177
- )}
178
- >
179
- Upgrade
180
- </Button>
181
- </div>
182
- );
183
- }
184
-
185
- export function SubscriptionPanel() {
186
- const subscription = useGrooveStore((s) => s.subscription);
187
- const authenticated = useGrooveStore((s) => s.marketplaceAuthenticated);
188
- const fetchSubscriptionPlans = useGrooveStore((s) => s.fetchSubscriptionPlans);
189
- const checkMarketplaceAuth = useGrooveStore((s) => s.checkMarketplaceAuth);
190
- const startCheckout = useGrooveStore((s) => s.startCheckout);
191
- const addToast = useGrooveStore((s) => s.addToast);
192
-
193
- const [plans, setPlans] = useState(null);
194
- const [billing, setBilling] = useState('monthly');
195
- const [loading, setLoading] = useState(false);
196
- const [planError, setPlanError] = useState(false);
197
- const [verifying, setVerifying] = useState(false);
198
-
199
- useEffect(() => {
200
- if (authenticated && !subscription?.active) {
201
- setVerifying(true);
202
- checkMarketplaceAuth().finally(() => setVerifying(false));
203
- }
204
- }, [authenticated]);
205
-
206
- useEffect(() => {
207
- if (!subscription?.active && !verifying && authenticated) {
208
- setLoading(true);
209
- setPlanError(false);
210
- fetchSubscriptionPlans()
211
- .then((data) => setPlans(data))
212
- .catch(() => setPlanError(true))
213
- .finally(() => setLoading(false));
214
- }
215
- }, [subscription?.active, verifying, authenticated, fetchSubscriptionPlans]);
216
-
217
- const handleUpgrade = async (priceId) => {
218
- try {
219
- await startCheckout(priceId);
220
- } catch (err) {
221
- if (err.status === 409) {
222
- addToast('info', 'Already subscribed', 'Use Manage Subscription to switch plans');
223
- }
224
- }
225
- };
226
-
227
- if (subscription?.active) {
228
- return (
229
- <div className="py-2">
230
- <ActivePlanCard subscription={subscription} />
231
- </div>
232
- );
233
- }
234
-
235
- if (verifying) {
236
- return (
237
- <div className="rounded-md border border-border-subtle bg-surface-1 p-4 flex items-center gap-3 my-2">
238
- <div className="h-4 w-4 border-2 border-accent border-t-transparent rounded-full animate-spin" />
239
- <span className="text-xs text-text-2 font-sans">Verifying subscription…</span>
240
- </div>
241
- );
242
- }
243
-
244
- if (!authenticated) {
245
- return (
246
- <div className="rounded-md border border-dashed border-border-subtle bg-surface-1/50 px-4 py-6 text-center my-2">
247
- <Crown size={20} className="text-text-4 mx-auto mb-2" />
248
- <p className="text-xs text-text-3 font-sans">Sign in to manage your subscription.</p>
249
- </div>
250
- );
251
- }
252
-
253
- if (loading) {
254
- return (
255
- <div className="grid grid-cols-2 gap-3 py-2">
256
- {[0, 1].map((i) => (
257
- <div key={i} className="rounded-md border border-border-subtle bg-surface-1 p-4 h-52 animate-pulse" />
258
- ))}
259
- </div>
260
- );
261
- }
262
-
263
- if (planError || !plans) {
264
- return (
265
- <div className="rounded-md border border-dashed border-border-subtle bg-surface-1/50 px-4 py-6 text-center my-2">
266
- <Sparkles size={20} className="text-text-4 mx-auto mb-2" />
267
- <p className="text-xs text-text-3 font-sans">Plans unavailable right now. Visit groovedev.ai/pro for details.</p>
268
- </div>
269
- );
270
- }
271
-
272
- const proPlan = plans.pro;
273
- const teamPlan = plans.team;
274
- const isAnnual = billing === 'annual';
275
-
276
- return (
277
- <div className="py-2">
278
- <div className="flex justify-center mb-4">
279
- <div className="flex bg-surface-0 rounded-md p-0.5 border border-border-subtle">
280
- <button
281
- onClick={() => setBilling('monthly')}
282
- className={cn(
283
- 'px-3 py-1.5 text-2xs font-semibold font-sans rounded transition-all cursor-pointer',
284
- billing === 'monthly' ? 'bg-accent/15 text-accent shadow-sm' : 'text-text-3 hover:text-text-1',
285
- )}
286
- >
287
- Monthly
288
- </button>
289
- <button
290
- onClick={() => setBilling('annual')}
291
- className={cn(
292
- 'px-3 py-1.5 text-2xs font-semibold font-sans rounded transition-all cursor-pointer',
293
- billing === 'annual' ? 'bg-accent/15 text-accent shadow-sm' : 'text-text-3 hover:text-text-1',
294
- )}
295
- >
296
- Annual
297
- <span className="ml-1 text-success">-20%</span>
298
- </button>
299
- </div>
300
- </div>
301
-
302
- <div className="grid grid-cols-2 gap-3">
303
- {proPlan && (
304
- <PricingCard
305
- name="Pro"
306
- plan="pro"
307
- price={isAnnual ? proPlan.annual.price : proPlan.monthly.price}
308
- interval={isAnnual ? 'year' : 'month'}
309
- features={proPlan.features}
310
- highlighted
311
- onUpgrade={() => handleUpgrade(isAnnual ? proPlan.annual.priceId : proPlan.monthly.priceId)}
312
- />
313
- )}
314
- {teamPlan && (
315
- <PricingCard
316
- name="Team"
317
- plan="team"
318
- price={isAnnual ? teamPlan.annual.price : teamPlan.monthly.price}
319
- interval={isAnnual ? 'year' : 'month'}
320
- features={teamPlan.features}
321
- onUpgrade={() => handleUpgrade(isAnnual ? teamPlan.annual.priceId : teamPlan.monthly.priceId)}
322
- />
323
- )}
324
- </div>
325
- </div>
326
- );
327
- }