feat(subscriptions): added polar as payment provider for subscriptions
* feature(dashboard): add polar / subscription * wip(payments): manage subscription * wip(payments): add free product, faq and some other improvements * fix(root): change node to bundler in tsconfig * wip(payments): display current subscription * feat(dashboard): schedule project for deletion * wip(payments): support custom products/subscriptions * wip(payments): fix polar scripts * wip(payments): add json package to dockerfiles
This commit is contained in:
committed by
GitHub
parent
86bf9dd064
commit
168ebc3430
@@ -1,5 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { cn } from '@/utils/cn';
|
||||
import { motion } from 'framer-motion';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function PageTabs({
|
||||
children,
|
||||
@@ -27,15 +31,23 @@ export function PageTabsLink({
|
||||
isActive?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
className={cn(
|
||||
'inline-block opacity-100 transition-transform hover:translate-y-[-1px]',
|
||||
isActive ? 'opacity-100' : 'opacity-50',
|
||||
<div className="relative">
|
||||
<Link
|
||||
className={cn(
|
||||
'inline-block opacity-100 transition-transform hover:translate-y-[-1px]',
|
||||
isActive ? 'opacity-100' : 'opacity-50',
|
||||
)}
|
||||
href={href}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
className="rounded-full absolute -bottom-1 left-0 right-0 h-0.5 bg-primary"
|
||||
layoutId={'page-tabs-link'}
|
||||
/>
|
||||
)}
|
||||
href={href}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,6 @@ export function Chart({ data }: Props) {
|
||||
}, [series]);
|
||||
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: [data.metrics.max],
|
||||
hide: hideYAxis,
|
||||
});
|
||||
const xAxisProps = useXAxisProps({
|
||||
|
||||
@@ -22,12 +22,7 @@ export function getYAxisWidth(value: string | undefined | null) {
|
||||
return charLength * value.length + charLength;
|
||||
}
|
||||
|
||||
export const useYAxisProps = ({
|
||||
data,
|
||||
hide,
|
||||
tickFormatter,
|
||||
}: {
|
||||
data: number[];
|
||||
export const useYAxisProps = (options?: {
|
||||
hide?: boolean;
|
||||
tickFormatter?: (value: number) => string;
|
||||
}) => {
|
||||
@@ -38,12 +33,14 @@ export const useYAxisProps = ({
|
||||
|
||||
return {
|
||||
...AXIS_FONT_PROPS,
|
||||
width: hide ? 0 : width,
|
||||
width: options?.hide ? 0 : width,
|
||||
axisLine: false,
|
||||
tickLine: false,
|
||||
allowDecimals: false,
|
||||
tickFormatter: (value: number) => {
|
||||
const tick = tickFormatter ? tickFormatter(value) : number.short(value);
|
||||
const tick = options?.tickFormatter
|
||||
? options.tickFormatter(value)
|
||||
: number.short(value);
|
||||
const newWidth = getYAxisWidth(tick);
|
||||
ref.current.push(newWidth);
|
||||
setWidthDebounced(Math.max(...ref.current));
|
||||
|
||||
@@ -49,7 +49,6 @@ export function Chart({ data }: Props) {
|
||||
const { series, setVisibleSeries } = useVisibleSeries(data);
|
||||
const rechartData = useRechartDataModel(series);
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: [data.metrics.max],
|
||||
hide: hideYAxis,
|
||||
});
|
||||
const xAxisProps = useXAxisProps({
|
||||
|
||||
@@ -110,7 +110,6 @@ export function Chart({ data }: Props) {
|
||||
|
||||
const xAxisProps = useXAxisProps({ interval, hide: hideXAxis });
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: [data.metrics.max],
|
||||
hide: hideYAxis,
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from 'recharts';
|
||||
|
||||
import { average, round } from '@openpanel/common';
|
||||
import { fix } from 'mathjs';
|
||||
import { useXAxisProps, useYAxisProps } from '../common/axis';
|
||||
import { useReportChartContext } from '../context';
|
||||
import { RetentionTooltip } from './tooltip';
|
||||
@@ -33,7 +32,6 @@ export function Chart({ data }: Props) {
|
||||
|
||||
const xAxisProps = useXAxisProps({ interval, hide: hideXAxis });
|
||||
const yAxisProps = useYAxisProps({
|
||||
data: [100],
|
||||
hide: hideYAxis,
|
||||
tickFormatter: (value) => `${value}%`,
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ const AccordionItem = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn('border-b', className)}
|
||||
className={cn('border-b [&:last-child]:border-b-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -11,6 +11,8 @@ const alertVariants = cva(
|
||||
default: 'bg-card text-foreground',
|
||||
destructive:
|
||||
'border-destructive text-destructive dark:border-destructive [&>svg]:text-destructive',
|
||||
warning:
|
||||
'bg-orange-400/10 border-orange-400 text-orange-600 dark:border-orange-400 [&>svg]:text-orange-400',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -10,12 +10,12 @@ import Link from 'next/link';
|
||||
import * as React from 'react';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex flex-shrink-0 select-none items-center justify-center whitespace-nowrap rounded-md font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
'inline-flex flex-shrink-0 select-none items-center justify-center whitespace-nowrap rounded-md font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:translate-y-[-1px]',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
cta: 'bg-highlight text-white hover:bg-highlight',
|
||||
cta: 'bg-highlight text-white hover:bg-highlight/80',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
|
||||
@@ -86,10 +86,7 @@ const DialogTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-lg font-semibold leading-none tracking-tight',
|
||||
className,
|
||||
)}
|
||||
className={cn('text-lg font-semibold tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -101,7 +98,7 @@ const DialogDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn(' text-muted-foreground', className)}
|
||||
className={cn(' text-muted-foreground mt-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -4,6 +4,7 @@ interface Props<T> {
|
||||
columns: {
|
||||
name: string;
|
||||
render: (item: T) => React.ReactNode;
|
||||
className?: string;
|
||||
}[];
|
||||
keyExtractor: (item: T) => string;
|
||||
data: T[];
|
||||
@@ -41,7 +42,9 @@ export function WidgetTable<T>({
|
||||
<WidgetTableHead>
|
||||
<tr>
|
||||
{columns.map((column) => (
|
||||
<th key={column.name}>{column.name}</th>
|
||||
<th key={column.name} className={cn(column.className)}>
|
||||
{column.name}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</WidgetTableHead>
|
||||
@@ -49,10 +52,14 @@ export function WidgetTable<T>({
|
||||
{data.map((item) => (
|
||||
<tr
|
||||
key={keyExtractor(item)}
|
||||
className="border-b border-border text-right last:border-0 [&_td:first-child]:text-left [&_td]:p-4"
|
||||
className={
|
||||
'border-b border-border text-right last:border-0 [&_td:first-child]:text-left [&_td]:p-4'
|
||||
}
|
||||
>
|
||||
{columns.map((column) => (
|
||||
<td key={column.name}>{column.render(item)}</td>
|
||||
<td key={column.name} className={cn(column.className)}>
|
||||
{column.render(item)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user