feat: dashboard v2, esm, upgrades (#211)

* esm

* wip

* wip

* wip

* wip

* wip

* wip

* subscription notice

* wip

* wip

* wip

* fix envs

* fix: update docker build

* fix

* esm/types

* delete dashboard :D

* add patches to dockerfiles

* update packages + catalogs + ts

* wip

* remove native libs

* ts

* improvements

* fix redirects and fetching session

* try fix favicon

* fixes

* fix

* order and resize reportds within a dashboard

* improvements

* wip

* added userjot to dashboard

* fix

* add op

* wip

* different cache key

* improve date picker

* fix table

* event details loading

* redo onboarding completely

* fix login

* fix

* fix

* extend session, billing and improve bars

* fix

* reduce price on 10M
This commit is contained in:
Carl-Gerhard Lindesvärd
2025-10-16 12:27:44 +02:00
committed by GitHub
parent 436e81ecc9
commit 81a7e5d62e
741 changed files with 32695 additions and 16996 deletions

View File

@@ -0,0 +1,155 @@
import { shortNumber } from '@/hooks/use-numer-formatter';
import { useTRPC } from '@/integrations/trpc/react';
import { useQuery } from '@tanstack/react-query';
import { Link } from '@tanstack/react-router';
import type { IServiceProject } from '@openpanel/db';
import { cn } from '@/utils/cn';
import { SettingsIcon, TrendingDownIcon, TrendingUpIcon } from 'lucide-react';
import { ChartSSR } from '../chart-ssr';
import { FadeIn } from '../fade-in';
import { SerieIcon } from '../report-chart/common/serie-icon';
import { Skeleton } from '../skeleton';
import { LinkButton } from '../ui/button';
export function ProjectCardRoot({
children,
className,
}: { children: React.ReactNode; className?: string }) {
return (
<div
className={cn(
'relative card hover:-translate-y-px hover:shadow-sm',
className,
)}
>
{children}
</div>
);
}
export function ProjectCardSkeleton() {
return (
<ProjectCardRoot className="aspect-[340/116.25] p-4 col">
<Skeleton className="h-5 w-full" />
<div className="row mt-auto gap-4 w-1/2 ml-auto">
<Skeleton className="h-3 w-full" />
<Skeleton className="h-3 w-full" />
<Skeleton className="h-3 w-full" />
</div>
</ProjectCardRoot>
);
}
function ProjectCard({ id, domain, name, organizationId }: IServiceProject) {
return (
<ProjectCardRoot>
<Link
to="/$organizationId/$projectId"
params={{
organizationId,
projectId: id,
}}
className="col p-4 transition-transform"
>
<div className="font-medium flex items-center gap-2 text-lg pb-2">
<div className="row gap-2 flex-1">
{domain && <SerieIcon name={domain ?? ''} />}
{name}
</div>
</div>
<div className="-mx-4 aspect-[8/1] mb-4">
<ProjectChart id={id} />
</div>
<div className="flex flex-1 gap-4 h-9 md:h-4">
<ProjectMetrics id={id} />
</div>
</Link>
<LinkButton
variant="ghost"
href={`/${organizationId}/${id}/settings`}
className="text-muted-foreground absolute top-2 right-2"
>
<SettingsIcon size={16} />
</LinkButton>
</ProjectCardRoot>
);
}
function ProjectChart({ id }: { id: string }) {
const trpc = useTRPC();
const { data } = useQuery(
trpc.chart.projectCard.queryOptions({
projectId: id,
}),
);
return (
<FadeIn className="h-full w-full">
<ChartSSR data={data?.chart || []} color={'blue'} />
</FadeIn>
);
}
function Metric({ value, label }: { value: React.ReactNode; label: string }) {
return (
<div className="flex flex-col gap-2 md:flex-row items-center">
<div className="text-muted-foreground text-xs">{label}</div>
<span className="font-semibold">{value}</span>
</div>
);
}
function ProjectMetrics({ id }: { id: string }) {
const trpc = useTRPC();
const { data } = useQuery(
trpc.chart.projectCard.queryOptions({
projectId: id,
}),
);
return (
<FadeIn className="flex gap-8 flex-1">
<div className="flex-1 items-center gap-2 row">
{typeof data?.trend?.percentage === 'number' && (
<Metric
label="3M DIFF"
value={
<span
className={cn(
'font-semibold',
'row gap-1 items-center',
data?.trend?.direction === 'up'
? 'text-emerald-300'
: data?.trend?.direction === 'down'
? 'text-orange-300'
: 'text-muted-foreground',
)}
>
{data.trend.direction === 'up' && (
<TrendingUpIcon className="size-4" />
)}
{data.trend.direction === 'down' && (
<TrendingDownIcon className="size-4" />
)}
{Math.abs(data.trend.percentage)}%
</span>
}
/>
)}
</div>
<Metric
label="3M"
value={shortNumber('en')(data?.metrics?.months_3 ?? 0)}
/>
<Metric
label="30D"
value={shortNumber('en')(data?.metrics?.month ?? 0)}
/>
<Metric label="24H" value={shortNumber('en')(data?.metrics?.day ?? 0)} />
</FadeIn>
);
}
export default ProjectCard;