diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx index aee44a63..8fd3316f 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx @@ -21,6 +21,7 @@ import type { LucideIcon } from 'lucide-react'; import { usePathname } from 'next/navigation'; import { ProjectLink } from '@/components/links'; +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { useNumber } from '@/hooks/useNumerFormatter'; import type { IServiceDashboards, IServiceOrganization } from '@openpanel/db'; import { differenceInDays, format } from 'date-fns'; @@ -72,10 +73,29 @@ export default function LayoutMenu({ subscriptionEndsAt, subscriptionPeriodEventsCount, subscriptionPeriodEventsLimit, + subscriptionProductId, } = organization; return ( <>
+ {(subscriptionProductId === '036efa2a-b3b4-4c75-b24a-9cac6bb8893b' || + subscriptionProductId === 'a18b4bee-d3db-4404-be6f-fba2f042d9ed') && ( + + +
+
Free plan is removed
+
+ We've removed the free plan. You can upgrade to a paid plan to + continue using OpenPanel. +
+
+
+ )} {process.env.SELF_HOSTED && ( { - return (productsQuery.data || []).filter( - (product) => product.recurringInterval === recurringInterval, - ); + return (productsQuery.data || []) + .filter((product) => product.recurringInterval === recurringInterval) + .filter((product) => product.prices.some((p) => p.amountType !== 'free')); }, [productsQuery.data, recurringInterval]); useEffect(() => { @@ -77,19 +77,22 @@ export default function Billing({ organization }: Props) { } return ( item.id} columns={[ { name: 'Tier', className: 'text-left', + width: 'auto', render(item) { return
{item.name}
; }, }, { name: 'Price', + width: 'auto', render(item) { const price = item.prices[0]; if (!price) { @@ -97,20 +100,21 @@ export default function Billing({ organization }: Props) { } if (price.amountType === 'free') { - return ( -
-
- Free - -
-
- ); + return null; + // return ( + //
+ //
+ // Free + // + //
+ //
+ // ); } if (price.amountType !== 'fixed') { diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/current-subscription.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/current-subscription.tsx index bd6e4323..a40c73c2 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/current-subscription.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/current-subscription.tsx @@ -107,6 +107,15 @@ export default function CurrentSubscription({ organization }: Props) { return ( <>
+ {price.amountType === 'free' && ( + + Free plan is removed + + We've removed the free plan. You can upgrade to a paid plan to + continue using OpenPanel. + + + )}
Name
{product.name}
diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/usage.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/usage.tsx index bd1eeea7..736cb0d2 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/usage.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/settings/organization/organization/usage.tsx @@ -114,10 +114,9 @@ export default function Usage({ organization }: Props) { subscriptionPeriodEventsLimit === 0 ? '👀' : number.formatWithUnit( - (1 - + 1 - subscriptionPeriodEventsCount / - subscriptionPeriodEventsLimit) * - 100, + subscriptionPeriodEventsLimit, '%', ) } diff --git a/apps/dashboard/src/components/overview/overview-widget-table.tsx b/apps/dashboard/src/components/overview/overview-widget-table.tsx index 4c6aafbe..178addc8 100644 --- a/apps/dashboard/src/components/overview/overview-widget-table.tsx +++ b/apps/dashboard/src/components/overview/overview-widget-table.tsx @@ -24,11 +24,11 @@ export const OverviewWidgetTable = ({ { return ( -
+
({ className: cn( index === 0 ? 'text-left w-full font-medium min-w-0' - : 'text-right w-20 font-mono', + : 'text-right font-mono', index !== 0 && index !== columns.length - 1 && 'hidden @[310px]:table-cell', @@ -72,10 +72,12 @@ export function OverviewWidgetTableLoading({ { name: 'Path', render: () => , + width: '1fr', }, { name: 'BR', render: () => , + width: '60px', }, // { // name: 'Duration', @@ -84,6 +86,7 @@ export function OverviewWidgetTableLoading({ { name: 'Sessions', render: () => , + width: '84px', }, ]} /> @@ -131,6 +134,7 @@ export function OverviewWidgetTablePages({ columns={[ { name: 'Path', + width: '1fr', render(item) { return ( @@ -173,20 +177,21 @@ export function OverviewWidgetTablePages({ }, { name: 'BR', - className: 'w-16', + width: '60px', render(item) { return number.shortWithUnit(item.bounce_rate, '%'); }, }, { name: 'Duration', + width: '75px', render(item) { return number.shortWithUnit(item.avg_duration, 'min'); }, }, { name: lastColumnName, - // className: 'w-28', + width: '84px', render(item) { return (
@@ -228,6 +233,7 @@ export function OverviewWidgetTableBots({ columns={[ { name: 'Path', + width: '1fr', render(item) { return ( @@ -256,7 +262,7 @@ export function OverviewWidgetTableBots({ }, { name: 'Bot', - // className: 'w-28', + width: '60px', render(item) { return (
@@ -267,7 +273,7 @@ export function OverviewWidgetTableBots({ }, { name: 'Date', - // className: 'w-28', + width: '60px', render(item) { return (
@@ -290,6 +296,7 @@ export function OverviewWidgetTableGeneric({ data: RouterOutputs['overview']['topGeneric']; column: { name: string; + width: string; render: ( item: RouterOutputs['overview']['topGeneric'][number], ) => React.ReactNode; @@ -307,7 +314,7 @@ export function OverviewWidgetTableGeneric({ column, { name: 'BR', - className: 'w-16', + width: '60px', render(item) { return number.shortWithUnit(item.bounce_rate, '%'); }, @@ -320,6 +327,7 @@ export function OverviewWidgetTableGeneric({ // }, { name: 'Sessions', + width: '84px', render(item) { return (
diff --git a/apps/dashboard/src/components/report-chart/funnel/chart.tsx b/apps/dashboard/src/components/report-chart/funnel/chart.tsx index c5c4b3fa..1e31fee6 100644 --- a/apps/dashboard/src/components/report-chart/funnel/chart.tsx +++ b/apps/dashboard/src/components/report-chart/funnel/chart.tsx @@ -212,12 +212,14 @@ export function Tables({ {item.event.displayName}
), + width: '1fr', className: 'text-left font-mono font-semibold', }, { name: 'Completed', render: (item) => number.format(item.count), className: 'text-right font-mono', + width: '82px', }, { name: 'Dropped after', @@ -226,11 +228,13 @@ export function Tables({ ? number.format(item.dropoffCount) : null, className: 'text-right font-mono', + width: '110px', }, { name: 'Conversion', render: (item) => number.formatWithUnit(item.percent / 100, '%'), className: 'text-right font-mono font-semibold', + width: '90px', }, ]} /> diff --git a/apps/dashboard/src/components/widget-table.tsx b/apps/dashboard/src/components/widget-table.tsx index a1152af3..673d2212 100644 --- a/apps/dashboard/src/components/widget-table.tsx +++ b/apps/dashboard/src/components/widget-table.tsx @@ -5,6 +5,7 @@ export interface Props { name: React.ReactNode; render: (item: T, index: number) => React.ReactNode; className?: string; + width: string; }[]; keyExtractor: (item: T) => string; data: T[]; @@ -40,58 +41,71 @@ export function WidgetTable({ eachRow, columnClassName, }: Props) { + const gridTemplateColumns = + columns.length > 1 + ? `1fr ${columns + .slice(1) + .map(() => 'auto') + .join(' ')}` + : '1fr'; + return (
- - - + {columns.map((column) => ( +
td]:p-2', + 'p-2 font-medium font-sans text-sm whitespace-nowrap cell', + columns.length > 1 && column !== columns[0] + ? 'text-right' + : 'text-left', + column.className, + )} + style={{ width: column.width }} + > + {column.name} +
+ ))} + + + {/* Body */} +
+ {data.map((item, index) => ( +
- {columns.map((column) => ( -
- ))} - - - - {data.map((item, index) => ( - td]:p-2', - columnClassName, - )} - > - {columns.map((column, columnIndex) => ( - + ))} - - ))} - -
- {column.name} -
+ {columns.map((column) => ( +
1 && column !== columns[0] + ? 'text-right' + : 'text-left', column.className, + column.width === '1fr' && 'w-full min-w-0', )} + style={{ width: column.width }} > - {columnIndex === 0 && eachRow?.(item, index)} {column.render(item, index)} -
+
+
+ ))} +
); diff --git a/apps/public/components/footer.tsx b/apps/public/components/footer.tsx index 3ddfffe6..d28dfafb 100644 --- a/apps/public/components/footer.tsx +++ b/apps/public/components/footer.tsx @@ -19,7 +19,7 @@ export function Footer() { Discover User Insights} title="Effortless web & product analytics" - description="Simplify your web & product analytics with our user-friendly platform. Collect, analyze, and optimize your data in minutes, for free." + description="Simplify your web & product analytics with our user-friendly platform. Collect, analyze, and optimize your data in minutes." />