public: updates of content

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-03-23 14:59:06 +01:00
parent b467a6ce7f
commit 2fb993fae5
10 changed files with 87 additions and 81 deletions

View File

@@ -34,13 +34,13 @@ const questions = [
{
question: 'How do I change my billing information?',
answer: [
'You can change your billing information by clicking the "Manage your subscription" button in the billing section.',
'You can change your billing information by clicking the "Customer portal" button in the billing section.',
],
},
{
question: 'We need a custom plan, can you help us?',
answer: [
'Yes, we can help you with that. Please contact us at hello@openpanel.com to request a quote.',
'Yes, we can help you with that. Please contact us at hello@openpanel.dev to request a quote.',
],
},
];
@@ -52,13 +52,13 @@ export function BillingFaq() {
<span className="title">Frequently asked questions</span>
</WidgetHead>
<Accordion
type="single"
collapsible
className="w-full max-w-screen-md self-center"
collapsible
type="single"
>
{questions.map((q) => (
<AccordionItem value={q.question} key={q.question}>
<AccordionTrigger className="text-left px-4">
<AccordionItem key={q.question} value={q.question}>
<AccordionTrigger className="px-4 text-left">
{q.question}
</AccordionTrigger>
<AccordionContent>

View File

@@ -1,8 +1,3 @@
import { PageHeader } from '@/components/page-header';
import { Button, LinkButton } from '@/components/ui/button';
import { useNumber } from '@/hooks/use-numer-formatter';
import { useTRPC } from '@/integrations/trpc/react';
import { op } from '@/utils/op';
import type { IServiceOrganization } from '@openpanel/db';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
@@ -11,11 +6,17 @@ import {
InfinityIcon,
type LucideIcon,
MapIcon,
SearchIcon,
ShieldCheckIcon,
TrendingUpIcon,
} from 'lucide-react';
import { useEffect } from 'react';
import { toast } from 'sonner';
import { PageHeader } from '@/components/page-header';
import { Button, LinkButton } from '@/components/ui/button';
import { useNumber } from '@/hooks/use-numer-formatter';
import { useTRPC } from '@/integrations/trpc/react';
import { op } from '@/utils/op';
const COPY = {
expired: {
@@ -59,7 +60,7 @@ export default function BillingPrompt({
const { data: products, isLoading: isLoadingProducts } = useQuery(
trpc.subscription.products.queryOptions({
organizationId: organization.id,
}),
})
);
const checkout = useMutation(
trpc.subscription.checkout.mutationOptions({
@@ -72,15 +73,14 @@ export default function BillingPrompt({
});
}
},
}),
})
);
const { title, description, body } = COPY[type];
const bestProductFit = products?.find(
(product) =>
typeof product.metadata.eventsLimit === 'number' &&
product.metadata.eventsLimit >=
organization.subscriptionPeriodEventsCount,
product.metadata.eventsLimit >= organization.subscriptionPeriodEventsCount
);
useEffect(() => {
@@ -98,32 +98,30 @@ export default function BillingPrompt({
}).format(
bestProductFit.prices[0] && 'priceAmount' in bestProductFit.prices[0]
? bestProductFit.prices[0].priceAmount / 100
: 0,
: 0
)
: null;
return (
<div className="p-4 md:p-20 max-w-7xl mx-auto">
<div className="border rounded-lg overflow-hidden bg-def-200 p-2 items-center">
<div className="mx-auto max-w-7xl p-4 md:p-20">
<div className="items-center overflow-hidden rounded-lg border bg-def-200 p-2">
<div className="md:row">
<div className="p-6 bg-background rounded-md border col gap-4 flex-1">
<PageHeader title={title} description={description} />
<div className="col flex-1 gap-4 rounded-md border bg-background p-6">
<PageHeader description={description} title={title} />
{body.map((paragraph) => (
<p key={paragraph}>
{paragraph.replace(
'{{events}}',
number.format(
organization.subscriptionPeriodEventsCount ?? 0,
),
number.format(organization.subscriptionPeriodEventsCount ?? 0)
)}
</p>
))}
<div className="col gap-2 mt-auto">
<div className="col mt-auto gap-2">
{bestProductFit && (
<div className="text-sm text-muted-foreground leading-normal">
<div className="text-muted-foreground text-sm leading-normal">
Based on your usage (
{number.format(
organization.subscriptionPeriodEventsCount ?? 0,
organization.subscriptionPeriodEventsCount ?? 0
)}{' '}
events) we recommend upgrading <br />
to the <strong>{bestProductFit.name}</strong> plan for{' '}
@@ -132,9 +130,8 @@ export default function BillingPrompt({
)}
<div className="col md:row gap-2">
<Button
size="lg"
loading={isLoadingProducts}
disabled={!bestProductFit}
loading={isLoadingProducts}
onClick={() => {
if (bestProductFit) {
op.track('billing_prompt_upgrade_clicked', {
@@ -152,33 +149,34 @@ export default function BillingPrompt({
});
}
}}
size="lg"
>
Upgrade to {price}
</Button>
<LinkButton
size="lg"
variant="outline"
to="/$organizationId/billing"
params={{ organizationId: organization.id }}
size="lg"
to="/$organizationId/billing"
variant="outline"
>
View pricing
</LinkButton>
</div>
</div>
</div>
<div className="shrink-0 flex-1 p-6 gap-4 col min-w-[200px] max-w-[300px]">
<div className="col min-w-[200px] max-w-[300px] flex-1 shrink-0 gap-4 p-6">
<Point icon={DollarSignIcon}>Plans start at just $2.5/month</Point>
<Point icon={InfinityIcon}>
Unlimited reports, members and projects
</Point>
<Point icon={BarChart3Icon}>Advanced funnels and conversions</Point>
<Point icon={MapIcon}>Real-time analytics</Point>
<Point icon={TrendingUpIcon}>
Track KPIs and custom events (revenue soon)
</Point>
<Point icon={TrendingUpIcon}>Track KPIs and custom events</Point>
<Point icon={ShieldCheckIcon}>
Privacy-focused and GDPR compliant
</Point>
<Point icon={DollarSignIcon}>Revenue tracking</Point>
<Point icon={SearchIcon}>Google Search Console integration</Point>
</div>
</div>
</div>
@@ -189,13 +187,16 @@ export default function BillingPrompt({
function Point({
icon: Icon,
children,
}: { icon: LucideIcon; children: React.ReactNode }) {
}: {
icon: LucideIcon;
children: React.ReactNode;
}) {
return (
<div className="row gap-2">
<div className="size-6 shrink-0 center-center rounded-full bg-amber-500 text-white">
<div className="center-center size-6 shrink-0 rounded-full bg-amber-500 text-white">
<Icon className="size-4" />
</div>
<h3 className="font-medium mt-[1.5px]">{children}</h3>
<h3 className="mt-[1.5px] font-medium">{children}</h3>
</div>
);
}