Skip to content

Commit f91fae6

Browse files
Merge pull request #34 from Ditectrev/feature/fix-user-profile
chore: bump version to 0.58.0 and enhance profile management
2 parents 0e82780 + df871a5 commit f91fae6

15 files changed

Lines changed: 861 additions & 429 deletions

app/(main)/home-page-client.tsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import { SymbolHeader } from "@/components/SymbolHeader";
1919
import { TabNavigation } from "@/components/TabNavigation";
2020
import { LoadingSpinner } from "@/components/LoadingSpinner";
2121
import { usePricingTier } from "@/lib/use-pricing-tier";
22+
import {
23+
EXPLANATIONS_PROVIDER_CHANGED_EVENT,
24+
getAIProviderHeaders,
25+
} from "@/lib/explanation-provider";
2226
import { AIPredictionPanel } from "@/components/AIPredictionPanel";
2327
import { StockOfTheDayPanel } from "@/components/StockOfTheDayPanel";
2428
const OverviewTab = dynamic(
@@ -379,12 +383,20 @@ export function HomePageClient() {
379383
};
380384
}, []);
381385

382-
const getAIProviderHeader = (): HeadersInit => {
383-
if (typeof window === "undefined") return {};
384-
const provider = localStorage.getItem("explanations_provider");
385-
if (!provider) return {};
386-
return { "x-ai-provider": provider };
387-
};
386+
const [aiProviderVersion, setAiProviderVersion] = useState(0);
387+
388+
useEffect(() => {
389+
const onProviderChanged = () => setAiProviderVersion((v) => v + 1);
390+
window.addEventListener(
391+
EXPLANATIONS_PROVIDER_CHANGED_EVENT,
392+
onProviderChanged
393+
);
394+
return () =>
395+
window.removeEventListener(
396+
EXPLANATIONS_PROVIDER_CHANGED_EVENT,
397+
onProviderChanged
398+
);
399+
}, []);
388400

389401
useEffect(() => {
390402
const fetchAIPrediction = async () => {
@@ -398,7 +410,7 @@ export function HomePageClient() {
398410
try {
399411
const response = await fetch(
400412
`/api/market/ai-prediction/${selectedSymbol}`,
401-
{ headers: getAIProviderHeader(), credentials: "include" }
413+
{ headers: getAIProviderHeaders(), credentials: "include" }
402414
);
403415
if (!response.ok) {
404416
const body = (await response.json().catch(() => ({}))) as {
@@ -423,7 +435,7 @@ export function HomePageClient() {
423435
};
424436

425437
fetchAIPrediction();
426-
}, [selectedSymbol, hasAIAccess]);
438+
}, [selectedSymbol, hasAIAccess, aiProviderVersion]);
427439

428440
useEffect(() => {
429441
const fetchStockOfTheDay = async () => {
@@ -436,7 +448,7 @@ export function HomePageClient() {
436448
setStockOfTheDayLoading(true);
437449
try {
438450
const response = await fetch("/api/market/stock-of-the-day", {
439-
headers: getAIProviderHeader(),
451+
headers: getAIProviderHeaders(),
440452
credentials: "include",
441453
});
442454
if (!response.ok) {
@@ -462,7 +474,7 @@ export function HomePageClient() {
462474
};
463475

464476
fetchStockOfTheDay();
465-
}, [hasAIAccess, selectedSymbol]);
477+
}, [hasAIAccess, selectedSymbol, aiProviderVersion]);
466478

467479
const handleTimeRangeChange = (range: TimeRange) => {
468480
setTimeRange(range);

app/(main)/profile/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ProfileSettings } from "@/components/ProfileSettings";
2+
3+
export default function ProfilePage() {
4+
return <ProfileSettings />;
5+
}

app/(main)/stock-of-the-day/page.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import { useEffect, useState } from "react";
44
import { usePricingTier } from "@/lib/use-pricing-tier";
5+
import {
6+
EXPLANATIONS_PROVIDER_CHANGED_EVENT,
7+
getAIProviderHeaders,
8+
} from "@/lib/explanation-provider";
59
import { StockOfTheDayPanel } from "@/components/StockOfTheDayPanel";
610
import type { StockOfTheDay } from "@/types";
711

@@ -19,6 +23,20 @@ export default function StockOfTheDayPage() {
1923
const [item, setItem] = useState<StockOfTheDay | null>(null);
2024
const [loading, setLoading] = useState(false);
2125
const [loadError, setLoadError] = useState<string | null>(null);
26+
const [aiProviderVersion, setAiProviderVersion] = useState(0);
27+
28+
useEffect(() => {
29+
const onProviderChanged = () => setAiProviderVersion((v) => v + 1);
30+
window.addEventListener(
31+
EXPLANATIONS_PROVIDER_CHANGED_EVENT,
32+
onProviderChanged
33+
);
34+
return () =>
35+
window.removeEventListener(
36+
EXPLANATIONS_PROVIDER_CHANGED_EVENT,
37+
onProviderChanged
38+
);
39+
}, []);
2240

2341
useEffect(() => {
2442
const load = async () => {
@@ -28,16 +46,10 @@ export default function StockOfTheDayPage() {
2846
return;
2947
}
3048

31-
const aiHeaders: Record<string, string> = {};
32-
if (typeof window !== "undefined") {
33-
const provider = localStorage.getItem("explanations_provider");
34-
if (provider) aiHeaders["x-ai-provider"] = provider;
35-
}
36-
3749
setLoading(true);
3850
try {
3951
const response = await fetch("/api/market/stock-of-the-day", {
40-
headers: aiHeaders,
52+
headers: getAIProviderHeaders(),
4153
credentials: "include",
4254
cache: "no-store",
4355
});
@@ -61,7 +73,7 @@ export default function StockOfTheDayPage() {
6173
};
6274

6375
load();
64-
}, [hasAIAccess]);
76+
}, [hasAIAccess, aiProviderVersion]);
6577

6678
useEffect(() => {
6779
const loadBYOKAccess = async () => {

app/api/subscription/current/route.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ export async function GET(request: NextRequest) {
2424
}
2525

2626
const tier = await subscriptionService.getCurrentTier(auth.id);
27+
const record = await subscriptionService.getSubscriptionRecord(auth.id);
2728

2829
return NextResponse.json({
2930
success: true,
30-
data: { tier },
31+
data: {
32+
tier,
33+
currentPeriodEnd: record?.currentPeriodEnd ?? null,
34+
cancelAtPeriodEnd: record?.cancelAtPeriodEnd ?? false,
35+
status: record?.status ?? null,
36+
},
3137
timestamp: new Date(),
3238
});
3339
} catch (error) {

components/AIPredictionPanel.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,22 +127,15 @@ export function AIPredictionPanel({
127127
{isMissingByokApiKeyMessage(error) && (
128128
<div className="mt-3 space-y-2">
129129
<p className="text-xs opacity-90">
130-
Add your API key under the avatar menu → profile → API keys,
131-
then pick the same provider as your explanation model.
130+
Add your API key on the Profile page under API keys, then
131+
pick the same provider as your explanation model.
132132
</p>
133-
<button
134-
type="button"
135-
onClick={() => {
136-
if (typeof window !== "undefined") {
137-
window.dispatchEvent(
138-
new Event("open-user-profile-menu")
139-
);
140-
}
141-
}}
133+
<a
134+
href="/profile"
142135
className="inline-flex items-center rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-blue-700"
143136
>
144-
Open profile menu
145-
</button>
137+
Open profile
138+
</a>
146139
</div>
147140
)}
148141
</div>

components/Navigation.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ export function Navigation({
2929

3030
return (
3131
<nav
32-
className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700"
32+
className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 overflow-visible"
3333
aria-label="Main navigation"
3434
data-testid="navigation"
3535
data-active-section={activeSection}
3636
>
3737
<div className="max-w-7xl xl:max-w-[1400px] mx-auto px-4 sm:px-6">
38-
<div className="flex items-center justify-between h-16 gap-4">
38+
<div className="flex items-center justify-between h-16 gap-4 overflow-visible">
3939
<Link
4040
href="/"
4141
className="flex-shrink-0 text-lg font-bold text-gray-900 dark:text-gray-100"
@@ -45,7 +45,7 @@ export function Navigation({
4545
<span className="sm:hidden">SE</span>
4646
</Link>
4747

48-
<div className="hidden md:flex items-center gap-1">
48+
<div className="hidden md:flex items-center gap-2">
4949
{MAIN_NAV.map((link) => (
5050
<Link
5151
key={link.id}
@@ -71,7 +71,7 @@ export function Navigation({
7171
/>
7272
</div>
7373

74-
<div className="flex items-center gap-2">
74+
<div className="relative z-[10060] flex items-center gap-2 flex-shrink-0">
7575
<UserProfileMenu />
7676
<ThemeToggle />
7777
<button

0 commit comments

Comments
 (0)