diff --git a/src/components/Onboarding.tsx b/src/components/Onboarding.tsx
new file mode 100644
index 0000000..ac75ac5
--- /dev/null
+++ b/src/components/Onboarding.tsx
@@ -0,0 +1,41 @@
+import { useAuthActions } from '@convex-dev/auth/react';
+import { Button } from '~/components/ui/button';
+
+export function Onboarding() {
+ //
+ const { signIn } = useAuthActions();
+
+ const handleSignIn = () => {
+ console.log('signing in');
+ signIn('google', { redirectTo: location.href });
+ };
+
+ return (
+
+
+
+ Welcome to Meseeks
+
+
+
+
+ );
+}
diff --git a/src/components/subscribe/SubscribePage.tsx b/src/components/subscribe/SubscribePage.tsx
deleted file mode 100644
index 48a5e17..0000000
--- a/src/components/subscribe/SubscribePage.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { useNavigate } from '@tanstack/react-router';
-import { track } from '@vercel/analytics/react';
-import { api } from 'convex/_generated/api';
-import { useAction } from 'convex/react';
-import { useEffect } from 'react';
-import { toast } from 'sonner';
-import { FaqSection, FounderCard, ProCard } from '~/components/subscribe';
-import { useIsPro } from '~/hooks/useIsPro';
-import { Route } from '~/routes/subscribe';
-
-export function SubscribePage({ route }: { route: typeof Route }) {
- //
- const navigate = useNavigate();
- const startSubscription = useAction(api.subscriptions.public.startSubscription);
- const { isPro } = useIsPro();
-
- // redirect to balance if already subscribed
- useEffect(() => {
- if (isPro) {
- navigate({ to: '/balance' });
- }
- }, [isPro, navigate]);
-
- const handleSubscribe = async (product: 'pro' | 'founder') => {
- track('tap_subscribe', { product });
- try {
- const { paymentUrl } = await startSubscription({ product });
- location.href = paymentUrl;
- } catch (error) {
- console.error(error);
- toast.error('Failed to start subscription.');
- }
- };
-
- track('subscribe', { isPro });
-
- // don't render if already subscribed (will redirect)
- if (isPro) return null;
-
- return (
-
-
-
Choose your path
- {/*
- The following offers are available during research preview, and are subject to change.
-
*/}
-
-
-
-
-
-
-
-
- This is a research preview . Expect issues.
-
-
© 2025 isPro. All rights reserved.
-
-
- );
-}
diff --git a/src/components/subscribe/faq/CreditsFaq.tsx b/src/components/subscribe/faq/CreditsFaq.tsx
index 4827862..d2332e7 100644
--- a/src/components/subscribe/faq/CreditsFaq.tsx
+++ b/src/components/subscribe/faq/CreditsFaq.tsx
@@ -15,7 +15,7 @@ export const CreditsFaq = {
advertised cost.
- Pro subscribers get $10 worth of monthly credits that never expire .
+ Pro subscribers get $10 worth of credits that never expire , every month.
You can also top up as much credits as you need, with zero markup from us -{' '}
diff --git a/src/components/subscribe/faq/FaqSection.tsx b/src/components/subscribe/faq/FaqSection.tsx
index 55c8c88..ebdc564 100644
--- a/src/components/subscribe/faq/FaqSection.tsx
+++ b/src/components/subscribe/faq/FaqSection.tsx
@@ -1,9 +1,10 @@
+import { type ReactNode } from 'react';
import { QuestionDialog } from '~/components/QuestionDialog';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '~/components/ui/accordion';
import { faqs } from './index';
-export function FaqSection() {
+export function FaqSection({ questionComponent }: { questionComponent?: ReactNode }) {
//
return (
@@ -24,7 +25,7 @@ export function FaqSection() {
Still have questions? We're here to help!
-
+ {questionComponent ||
}
);
diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts
index d947b39..6ac778e 100644
--- a/src/routeTree.gen.ts
+++ b/src/routeTree.gen.ts
@@ -13,6 +13,7 @@ import { Route as TopUpRouteImport } from './routes/top-up'
import { Route as SubscribeRouteImport } from './routes/subscribe'
import { Route as SkillsRouteImport } from './routes/skills'
import { Route as SchedulesRouteImport } from './routes/schedules'
+import { Route as PricingRouteImport } from './routes/pricing'
import { Route as BalanceRouteImport } from './routes/balance'
import { Route as SplatRouteImport } from './routes/$'
import { Route as PolarRouteRouteImport } from './routes/polar/route'
@@ -43,6 +44,11 @@ const SchedulesRoute = SchedulesRouteImport.update({
path: '/schedules',
getParentRoute: () => rootRouteImport,
} as any)
+const PricingRoute = PricingRouteImport.update({
+ id: '/pricing',
+ path: '/pricing',
+ getParentRoute: () => rootRouteImport,
+} as any)
const BalanceRoute = BalanceRouteImport.update({
id: '/balance',
path: '/balance',
@@ -93,6 +99,7 @@ export interface FileRoutesByFullPath {
'/polar': typeof PolarRouteRouteWithChildren
'/$': typeof SplatRoute
'/balance': typeof BalanceRoute
+ '/pricing': typeof PricingRoute
'/schedules': typeof SchedulesRoute
'/skills': typeof SkillsRoute
'/subscribe': typeof SubscribeRoute
@@ -108,6 +115,7 @@ export interface FileRoutesByTo {
'/polar': typeof PolarRouteRouteWithChildren
'/$': typeof SplatRoute
'/balance': typeof BalanceRoute
+ '/pricing': typeof PricingRoute
'/schedules': typeof SchedulesRoute
'/skills': typeof SkillsRoute
'/subscribe': typeof SubscribeRoute
@@ -124,6 +132,7 @@ export interface FileRoutesById {
'/polar': typeof PolarRouteRouteWithChildren
'/$': typeof SplatRoute
'/balance': typeof BalanceRoute
+ '/pricing': typeof PricingRoute
'/schedules': typeof SchedulesRoute
'/skills': typeof SkillsRoute
'/subscribe': typeof SubscribeRoute
@@ -141,6 +150,7 @@ export interface FileRouteTypes {
| '/polar'
| '/$'
| '/balance'
+ | '/pricing'
| '/schedules'
| '/skills'
| '/subscribe'
@@ -156,6 +166,7 @@ export interface FileRouteTypes {
| '/polar'
| '/$'
| '/balance'
+ | '/pricing'
| '/schedules'
| '/skills'
| '/subscribe'
@@ -171,6 +182,7 @@ export interface FileRouteTypes {
| '/polar'
| '/$'
| '/balance'
+ | '/pricing'
| '/schedules'
| '/skills'
| '/subscribe'
@@ -187,6 +199,7 @@ export interface RootRouteChildren {
PolarRouteRoute: typeof PolarRouteRouteWithChildren
SplatRoute: typeof SplatRoute
BalanceRoute: typeof BalanceRoute
+ PricingRoute: typeof PricingRoute
SchedulesRoute: typeof SchedulesRoute
SkillsRoute: typeof SkillsRoute
SubscribeRoute: typeof SubscribeRoute
@@ -227,6 +240,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof SchedulesRouteImport
parentRoute: typeof rootRouteImport
}
+ '/pricing': {
+ id: '/pricing'
+ path: '/pricing'
+ fullPath: '/pricing'
+ preLoaderRoute: typeof PricingRouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/balance': {
id: '/balance'
path: '/balance'
@@ -311,6 +331,7 @@ const rootRouteChildren: RootRouteChildren = {
PolarRouteRoute: PolarRouteRouteWithChildren,
SplatRoute: SplatRoute,
BalanceRoute: BalanceRoute,
+ PricingRoute: PricingRoute,
SchedulesRoute: SchedulesRoute,
SkillsRoute: SkillsRoute,
SubscribeRoute: SubscribeRoute,
diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx
index b9c6b39..37185be 100644
--- a/src/routes/__root.tsx
+++ b/src/routes/__root.tsx
@@ -1,8 +1,7 @@
-import { useAuthActions } from '@convex-dev/auth/react';
import { QueryClient } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
-import { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/react-router';
-import { TanStackRouterDevtools } from '@tanstack/react-router-devtools';
+import { HeadContent, Outlet, Scripts, createRootRouteWithContext, useRouter } from '@tanstack/react-router';
+import { TanStackRouterDevtools } from '@tanstack/router-devtools';
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/react';
import { AuthLoading, Authenticated, Unauthenticated } from 'convex/react';
@@ -11,9 +10,9 @@ import { CommandMenuDialog } from '~/components/CommandMenu';
import { FeedbackDialog } from '~/components/FeedbackDialog';
import { Loading } from '~/components/Loading';
import { MainHeader } from '~/components/MainHeader';
+import { Onboarding } from '~/components/Onboarding';
import { RotatingLoadingMessage } from '~/components/RotatingLoadingMessage';
import { ScheduleIterationDialog } from '~/components/ScheduleIterationDialog';
-import { Button } from '~/components/ui/button';
import { Toaster } from '~/components/ui/sonner';
import { useCurrentUser } from '~/hooks/useCurrentUser';
import { FeedbackDialogProvider, useFeedbackDialog } from '~/hooks/useFeedbackDialog';
@@ -113,7 +112,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
-
+ {children}
{children}
@@ -167,34 +166,20 @@ function MainWithFeedback({ children }: { children: React.ReactNode }) {
);
}
-function AccessDenied() {
+function AccessDenied({ children }: { children: React.ReactNode }) {
//
- const { signIn } = useAuthActions();
-
- return (
-
-
signIn('google', { redirectTo: location.href })}>Continue with Google
-
-
- );
+ const router = useRouter();
+ const currentPath = router.state.location.pathname;
+
+ if (currentPath === '/pricing') {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return ;
}
// TODO: on .webmanifest:
diff --git a/src/routes/pricing.tsx b/src/routes/pricing.tsx
new file mode 100644
index 0000000..d8c6faa
--- /dev/null
+++ b/src/routes/pricing.tsx
@@ -0,0 +1,54 @@
+import { useAuthActions } from '@convex-dev/auth/react';
+import { createFileRoute } from '@tanstack/react-router';
+import { track } from '@vercel/analytics/react';
+import { MessageCircle } from 'lucide-react';
+import { FaqSection, FounderCard, ProCard } from '~/components/subscribe';
+import { Button } from '~/components/ui/button';
+
+export const Route = createFileRoute('/pricing')({
+ component: PricingPage,
+});
+
+function PricingPage() {
+ //
+ const { signIn } = useAuthActions();
+
+ const handleGetStarted = async (product: 'pro' | 'founder') => {
+ track('tap_get_started_pricing', { product });
+ signIn('google', { redirectTo: '/subscribe' });
+ };
+
+ const handleAskQuestion = () => {
+ track('tap_ask_question_pricing');
+ signIn('google', { redirectTo: '/subscribe' });
+ };
+
+ const questionButton = (
+
+
+ Ask a question
+
+ );
+
+ return (
+
+
+
Pricing
+
+
+
+
+
+
+
+
+ This is a research preview . Expect issues.
+
+
© 2025 isPro. All rights reserved.
+
+
+ );
+}
diff --git a/src/routes/subscribe.tsx b/src/routes/subscribe.tsx
index 360c189..7a8ca21 100644
--- a/src/routes/subscribe.tsx
+++ b/src/routes/subscribe.tsx
@@ -1,6 +1,67 @@
-import { createFileRoute } from '@tanstack/react-router';
-import { SubscribePage } from '~/components/subscribe/SubscribePage';
+import { createFileRoute, useNavigate } from '@tanstack/react-router';
+import { track } from '@vercel/analytics/react';
+import { api } from 'convex/_generated/api';
+import { useAction } from 'convex/react';
+import { useEffect } from 'react';
+import { toast } from 'sonner';
+import { FaqSection, FounderCard, ProCard } from '~/components/subscribe';
+import { useIsPro } from '~/hooks/useIsPro';
export const Route = createFileRoute('/subscribe')({
- component: () => ,
+ component: RouteComponent,
});
+
+export function RouteComponent() {
+ //
+ const navigate = useNavigate();
+ const startSubscription = useAction(api.subscriptions.public.startSubscription);
+ const { isPro } = useIsPro();
+
+ // redirect to balance if already subscribed
+ useEffect(() => {
+ if (isPro) {
+ navigate({ to: '/balance' });
+ }
+ }, [isPro, navigate]);
+
+ const handleSubscribe = async (product: 'pro' | 'founder') => {
+ track('tap_subscribe', { product });
+ try {
+ const { paymentUrl } = await startSubscription({ product });
+ location.href = paymentUrl;
+ } catch (error) {
+ console.error(error);
+ toast.error('Failed to start subscription.');
+ }
+ };
+
+ track('subscribe', { isPro });
+
+ // don't render if already subscribed (will redirect)
+ if (isPro) return null;
+
+ return (
+
+
+
Choose your path
+ {/*
+ The following offers are available during research preview, and are subject to change.
+
*/}
+
+
+
+
+
+
+
+
+ This is a research preview . Expect issues.
+
+
© 2025 isPro. All rights reserved.
+
+
+ );
+}