@wopr-network/platform-ui-core 1.27.5 → 1.27.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/platform-ui-core",
3
- "version": "1.27.5",
3
+ "version": "1.27.7",
4
4
  "description": "Brand-agnostic AI agent platform UI — deploy as any brand via env vars",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,6 +20,7 @@ import { Skeleton } from "@/components/ui/skeleton";
20
20
  import type { CreditBalance as CreditBalanceData, DividendWalletStats } from "@/lib/api";
21
21
  import { getDividendStats } from "@/lib/api";
22
22
  import { useSession } from "@/lib/auth-client";
23
+ import { getBrandConfig } from "@/lib/brand-config";
23
24
  import { getOrganization } from "@/lib/org-api";
24
25
  import { trpc } from "@/lib/trpc";
25
26
 
@@ -56,6 +57,7 @@ function CreditsContent() {
56
57
  }, [session?.user?.email, session?.user?.id]);
57
58
 
58
59
  const [showCryptoPending, setShowCryptoPending] = useState(cryptoPending);
60
+ const showDividends = getBrandConfig().dividendsEnabled;
59
61
  const [dividendStats, setDividendStats] = useState<DividendWalletStats | null>(null);
60
62
  const [todayDividendCents, setTodayDividendCents] = useState(0);
61
63
 
@@ -91,6 +93,7 @@ function CreditsContent() {
91
93
  const error = balanceError ? "Failed to load credit balance." : null;
92
94
 
93
95
  useEffect(() => {
96
+ if (!showDividends) return;
94
97
  getDividendStats()
95
98
  .then((statsData) => {
96
99
  if (statsData) {
@@ -101,7 +104,7 @@ function CreditsContent() {
101
104
  }
102
105
  })
103
106
  .catch(() => null);
104
- }, []);
107
+ }, [showDividends]);
105
108
 
106
109
  if (!orgChecked) {
107
110
  return (
@@ -161,14 +164,12 @@ function CreditsContent() {
161
164
  <div className="max-w-3xl space-y-6">
162
165
  <div>
163
166
  <h1 className="text-2xl font-bold tracking-tight">Credits</h1>
164
- <p className="text-sm text-muted-foreground">
165
- Stay active to keep claiming your daily dividend
166
- </p>
167
+ <p className="text-sm text-muted-foreground">Purchase and manage your credits</p>
167
168
  </div>
168
169
 
169
170
  <LowBalanceBanner balance={balance.balance} runway={balance.runway} />
170
171
 
171
- {dividendStats && (
172
+ {showDividends && dividendStats && (
172
173
  <DividendBanner todayAmountCents={todayDividendCents} stats={dividendStats} />
173
174
  )}
174
175
 
@@ -183,14 +184,14 @@ function CreditsContent() {
183
184
 
184
185
  <CreditBalance data={balance} />
185
186
 
186
- {dividendStats && (
187
+ {showDividends && dividendStats && (
187
188
  <DividendEligibility
188
189
  windowExpiresAt={dividendStats.userWindowExpiresAt}
189
190
  eligible={dividendStats.userEligible}
190
191
  />
191
192
  )}
192
193
 
193
- {dividendStats && (
194
+ {showDividends && dividendStats && (
194
195
  <DividendPoolStats
195
196
  poolCents={dividendStats.poolCents}
196
197
  activeUsers={dividendStats.activeUsers}
@@ -204,7 +205,9 @@ function CreditsContent() {
204
205
  <AutoTopupCard />
205
206
  <TransactionHistory />
206
207
 
207
- {dividendStats && <FirstDividendDialog todayAmountCents={todayDividendCents} />}
208
+ {showDividends && dividendStats && (
209
+ <FirstDividendDialog todayAmountCents={todayDividendCents} />
210
+ )}
208
211
  </div>
209
212
  );
210
213
  }
@@ -4,10 +4,11 @@ import { BuyCreditsPanel } from "@/components/billing/buy-credits-panel";
4
4
  import { CryptoCheckout } from "@/components/billing/crypto-checkout";
5
5
  import { Button } from "@/components/ui/button";
6
6
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
7
- import { productName } from "@/lib/brand-config";
7
+ import { getBrandConfig, productName } from "@/lib/brand-config";
8
8
  import { pricingData } from "@/lib/pricing-data";
9
9
 
10
10
  export default function PlansPage() {
11
+ const { planFeatures } = getBrandConfig();
11
12
  return (
12
13
  <div className="max-w-3xl space-y-6">
13
14
  <div>
@@ -30,12 +31,7 @@ export default function PlansPage() {
30
31
  </p>
31
32
  <p className="text-muted-foreground">per bot</p>
32
33
  <ul className="space-y-2 text-sm">
33
- {[
34
- `$${pricingData.signup_credit} signup credit included`,
35
- "All channels",
36
- "All plugins",
37
- "Hosted AI — no API keys needed",
38
- ].map((feature) => (
34
+ {planFeatures.map((feature) => (
39
35
  <li key={feature} className="flex items-center gap-2 text-muted-foreground">
40
36
  <Check className="size-4 shrink-0 text-terminal" />
41
37
  {feature}
@@ -93,6 +93,12 @@ export interface BrandConfig {
93
93
 
94
94
  /** Whether the embedded chat widget is enabled (default true). */
95
95
  chatEnabled: boolean;
96
+
97
+ /** Whether dividend features are shown in billing (default false). */
98
+ dividendsEnabled: boolean;
99
+
100
+ /** Feature bullet points shown on the billing plans page. */
101
+ planFeatures: string[];
96
102
  }
97
103
 
98
104
  /**
@@ -129,6 +135,20 @@ function parseNavItems(raw: string | undefined): Array<{ label: string; href: st
129
135
  return null;
130
136
  }
131
137
 
138
+ /** Parse a JSON string array env var. Returns null if unset/invalid. */
139
+ function parseStringArray(raw: string | undefined): string[] | null {
140
+ if (!raw) return null;
141
+ try {
142
+ const parsed = JSON.parse(raw);
143
+ if (Array.isArray(parsed) && parsed.every((s: unknown) => typeof s === "string")) {
144
+ return parsed as string[];
145
+ }
146
+ } catch {
147
+ // Invalid JSON — fall back to defaults
148
+ }
149
+ return null;
150
+ }
151
+
132
152
  function envDefaults(): BrandConfig {
133
153
  // Direct process.env.X access is required — Next.js Turbopack only inlines
134
154
  // NEXT_PUBLIC_* vars when accessed as literal dot-property references.
@@ -158,6 +178,13 @@ function envDefaults(): BrandConfig {
158
178
  price: process.env.NEXT_PUBLIC_BRAND_PRICE || "",
159
179
  homePath: process.env.NEXT_PUBLIC_BRAND_HOME_PATH || "/marketplace",
160
180
  chatEnabled: process.env.NEXT_PUBLIC_BRAND_CHAT_ENABLED !== "false",
181
+ dividendsEnabled: process.env.NEXT_PUBLIC_BRAND_DIVIDENDS_ENABLED === "true",
182
+ planFeatures: parseStringArray(process.env.NEXT_PUBLIC_BRAND_PLAN_FEATURES) ?? [
183
+ "Signup credit included",
184
+ "All channels",
185
+ "All plugins",
186
+ "Hosted AI — no API keys needed",
187
+ ],
161
188
  navItems: parseNavItems(process.env.NEXT_PUBLIC_BRAND_NAV_ITEMS) ?? [
162
189
  { label: "Dashboard", href: "/dashboard" },
163
190
  { label: "Chat", href: "/chat" },