mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 23:14:37 +00:00
fix(frontend): preserve post-checkout dashboard redirect (#5036)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useT } from '../../lib/i18n/I18nContext';
|
||||
import { BILLING_DASHBOARD_URL, DISCORD_INVITE_URL } from '../../utils/links';
|
||||
import { DISCORD_INVITE_URL, PRICING_URL } from '../../utils/links';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
|
||||
function formatUsd(amount: number): string {
|
||||
@@ -53,7 +53,7 @@ export function UsageLimitBanner({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
className={`cursor-pointer border-b border-dashed font-bold ${styles.button}`}>
|
||||
{ctaLabel}
|
||||
@@ -101,7 +101,7 @@ export function PromotionalCreditsBanner({ promoCredits }: { promoCredits: numbe
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
className="cursor-pointer border-b border-dashed border-amber-700 font-bold text-amber-700 hover:text-amber-800 dark:border-amber-300 dark:text-amber-300 dark:hover:text-amber-200">
|
||||
{t('home.banners.getSubscription')}
|
||||
@@ -143,7 +143,7 @@ export function EarlyBirdyBanner({ onDismiss }: { onDismiss?: () => void }) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
className="cursor-pointer border-b border-amber-700 border-dashed font-bold text-amber-700 hover:text-amber-800 dark:border-amber-300 dark:text-amber-300 dark:hover:text-amber-200">
|
||||
{t('home.banners.earlyBirdFirstSub')}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { BILLING_DASHBOARD_URL, DISCORD_INVITE_URL } from '../../../utils/links';
|
||||
import { DISCORD_INVITE_URL, PRICING_URL } from '../../../utils/links';
|
||||
import { openUrl } from '../../../utils/openUrl';
|
||||
import {
|
||||
DiscordBanner,
|
||||
@@ -45,7 +45,7 @@ describe('HomeBanners', () => {
|
||||
);
|
||||
expect(screen.getByText('Out of Usage')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Get a subscription' }));
|
||||
expect(openUrl).toHaveBeenCalledWith(BILLING_DASHBOARD_URL);
|
||||
expect(openUrl).toHaveBeenCalledWith(PRICING_URL);
|
||||
});
|
||||
|
||||
it('opens the billing dashboard through openUrl from the promotional credits banner', () => {
|
||||
@@ -77,7 +77,7 @@ describe('HomeBanners', () => {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /first subscription/i }));
|
||||
|
||||
expect(openUrl).toHaveBeenCalledWith(BILLING_DASHBOARD_URL);
|
||||
expect(openUrl).toHaveBeenCalledWith(PRICING_URL);
|
||||
});
|
||||
|
||||
it('does not render a dismiss button when onDismiss is not provided', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { BILLING_DASHBOARD_URL, DISCORD_INVITE_URL } from '../../utils/links';
|
||||
import { DISCORD_INVITE_URL, PRICING_URL } from '../../utils/links';
|
||||
import MedullaOverviewPanel from './MedullaOverviewPanel';
|
||||
|
||||
// Pass-through translator so assertions can target the i18n keys directly.
|
||||
@@ -31,6 +31,6 @@ describe('MedullaOverviewPanel', () => {
|
||||
it('opens the billing dashboard when the subscription CTA is clicked', () => {
|
||||
render(<MedullaOverviewPanel />);
|
||||
fireEvent.click(screen.getByTestId('orch-medulla-subscribe'));
|
||||
expect(openUrl).toHaveBeenCalledWith(BILLING_DASHBOARD_URL);
|
||||
expect(openUrl).toHaveBeenCalledWith(PRICING_URL);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* ("Medulla", "OpenHuman", "Discord") stay in every locale.
|
||||
*/
|
||||
import { useT } from '../../lib/i18n/I18nContext';
|
||||
import { BILLING_DASHBOARD_URL, DISCORD_INVITE_URL } from '../../utils/links';
|
||||
import { DISCORD_INVITE_URL, PRICING_URL } from '../../utils/links';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
|
||||
export default function MedullaOverviewPanel() {
|
||||
@@ -79,7 +79,7 @@ export default function MedullaOverviewPanel() {
|
||||
<button
|
||||
type="button"
|
||||
data-testid="orch-medulla-subscribe"
|
||||
onClick={() => void openUrl(BILLING_DASHBOARD_URL)}
|
||||
onClick={() => void openUrl(PRICING_URL)}
|
||||
className="mt-auto flex w-full items-center justify-center gap-2 rounded-xl bg-primary-500 px-4 py-2 text-xs font-semibold text-content-inverted shadow-soft transition-colors hover:bg-primary-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1">
|
||||
{t('orchPage.medulla.subscriberCta')}
|
||||
</button>
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('<BillingPanel />', () => {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Open billing dashboard' }));
|
||||
await waitFor(() => expect(openUrlMock).toHaveBeenCalledTimes(1));
|
||||
expect(openUrlMock).toHaveBeenLastCalledWith('https://tinyhumans.ai/pricing');
|
||||
expect(openUrlMock).toHaveBeenLastCalledWith('https://tinyhumans.ai/dashboard');
|
||||
});
|
||||
|
||||
it('invokes the navigation back handler from both the header and the inline button', async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useUsageState } from '../../hooks/useUsageState';
|
||||
import { useT } from '../../lib/i18n/I18nContext';
|
||||
import { BILLING_DASHBOARD_URL } from '../../utils/links';
|
||||
import { PRICING_URL } from '../../utils/links';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
import UpsellBanner from './UpsellBanner';
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function GlobalUpsellBanner() {
|
||||
ctaLabel={t('chat.upgrade')}
|
||||
rounded={false}
|
||||
onCtaClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -38,7 +38,7 @@ export default function GlobalUpsellBanner() {
|
||||
ctaLabel={t('chat.upgrade')}
|
||||
rounded={false}
|
||||
onCtaClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -103,9 +103,9 @@ describe('AVATAR_MENU_ITEMS', () => {
|
||||
expect(openUrlItems).toEqual(['billing']);
|
||||
});
|
||||
|
||||
it('opens billing on the public pricing page', () => {
|
||||
it('opens billing on the authenticated dashboard', () => {
|
||||
expect(AVATAR_MENU_ITEMS.find(i => i.id === 'billing')?.target).toBe(
|
||||
'https://tinyhumans.ai/pricing'
|
||||
'https://tinyhumans.ai/dashboard'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,7 +133,7 @@ import type { ThreadMessage } from '../../types/thread';
|
||||
import { splitAgentMessageIntoBubbles } from '../../utils/agentMessageBubbles';
|
||||
import { chatThreadPath } from '../../utils/chatRoutes';
|
||||
import { CHAT_ATTACHMENTS_ENABLED } from '../../utils/config';
|
||||
import { BILLING_DASHBOARD_URL } from '../../utils/links';
|
||||
import { PRICING_URL } from '../../utils/links';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
import {
|
||||
isTauri,
|
||||
@@ -2799,7 +2799,7 @@ const Conversations = ({
|
||||
)}
|
||||
ctaLabel={t('chat.upgrade')}
|
||||
onCtaClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
dismissible
|
||||
onDismiss={() => dismissBanner('conversations-warning')}
|
||||
@@ -2844,7 +2844,7 @@ const Conversations = ({
|
||||
type="button"
|
||||
data-analytics-id="chat-budget-top-up"
|
||||
onClick={() => {
|
||||
void openUrl(BILLING_DASHBOARD_URL);
|
||||
void openUrl(PRICING_URL);
|
||||
}}
|
||||
className="px-3 py-1.5 rounded-lg bg-coral-500 hover:bg-coral-400 text-content-inverted text-xs font-medium transition-colors">
|
||||
{t('chat.topUp')}
|
||||
|
||||
@@ -12,9 +12,12 @@ import { getStoredCoreMode } from '../configPersistence';
|
||||
import {
|
||||
authStoreFailureUserMessage,
|
||||
classifyAuthStoreFailure,
|
||||
handleDeepLinkUrls,
|
||||
registerAuthDeepLinkState,
|
||||
setupDesktopDeepLinkListener,
|
||||
} from '../desktopDeepLinkListener';
|
||||
import { BILLING_DASHBOARD_URL } from '../links';
|
||||
import { openUrl } from '../openUrl';
|
||||
import { storeSession } from '../tauriCommands';
|
||||
|
||||
vi.mock('../configPersistence', () => ({ getStoredCoreMode: vi.fn() }));
|
||||
@@ -22,6 +25,7 @@ vi.mock('../../services/coreRpcClient', () => ({
|
||||
clearCoreRpcUrlCache: vi.fn(),
|
||||
clearCoreRpcTokenCache: vi.fn(),
|
||||
}));
|
||||
vi.mock('../openUrl', () => ({ openUrl: vi.fn() }));
|
||||
|
||||
// Build an `openhuman://auth` deep link bound to a freshly registered state
|
||||
// nonce, mirroring how the real OAuth button registers the loopback/deep-link
|
||||
@@ -83,12 +87,27 @@ describe('desktopDeepLinkListener', () => {
|
||||
vi.mocked(getStoredCoreMode).mockReturnValue(null);
|
||||
vi.mocked(clearCoreRpcUrlCache).mockClear();
|
||||
vi.mocked(clearCoreRpcTokenCache).mockClear();
|
||||
vi.mocked(openUrl).mockReset();
|
||||
vi.mocked(openUrl).mockResolvedValue(undefined);
|
||||
windowControls.show.mockClear();
|
||||
windowControls.unminimize.mockClear();
|
||||
windowControls.setFocus.mockClear();
|
||||
completeDeepLinkAuthProcessing();
|
||||
});
|
||||
|
||||
it('returns successful payment deep links to the billing dashboard', async () => {
|
||||
await handleDeepLinkUrls(['openhuman://payment/success?session_id=checkout-session']);
|
||||
|
||||
expect(openUrl).toHaveBeenCalledWith(BILLING_DASHBOARD_URL);
|
||||
expect(BILLING_DASHBOARD_URL).toBe('https://tinyhumans.ai/dashboard');
|
||||
});
|
||||
|
||||
it('returns cancelled payment deep links to the billing dashboard', async () => {
|
||||
await handleDeepLinkUrls(['openhuman://payment/cancel']);
|
||||
|
||||
expect(openUrl).toHaveBeenCalledWith(BILLING_DASHBOARD_URL);
|
||||
});
|
||||
|
||||
it('turns Twitter OAuth error deep links into actionable UI and event diagnostics', async () => {
|
||||
const oauthErrorEvents: CustomEvent[] = [];
|
||||
window.addEventListener('oauth:error', event => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const DISCORD_INVITE_URL = 'https://discord.tinyhumans.ai';
|
||||
export const BILLING_DASHBOARD_URL = 'https://tinyhumans.ai/pricing';
|
||||
export const PRICING_URL = 'https://tinyhumans.ai/pricing';
|
||||
export const BILLING_DASHBOARD_URL = 'https://tinyhumans.ai/dashboard';
|
||||
export const PRIVACY_POLICY_URL = 'https://tinyhumans.gitbook.io/openhuman/legal/privacy-policy';
|
||||
export const TERMS_OF_USE_URL = 'https://tinyhumans.gitbook.io/openhuman/legal/terms-of-use';
|
||||
|
||||
Reference in New Issue
Block a user