add tailwind prettier and some clean up
This commit is contained in:
@@ -1,28 +1 @@
|
||||
# Create T3 App
|
||||
|
||||
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
|
||||
|
||||
## What's next? How do I make an app with this?
|
||||
|
||||
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
|
||||
|
||||
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
|
||||
|
||||
- [Next.js](https://nextjs.org)
|
||||
- [NextAuth.js](https://next-auth.js.org)
|
||||
- [Prisma](https://prisma.io)
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
- [tRPC](https://trpc.io)
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:
|
||||
|
||||
- [Documentation](https://create.t3.gg/)
|
||||
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials
|
||||
|
||||
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!
|
||||
|
||||
## How do I deploy this?
|
||||
|
||||
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
|
||||
# Dashboard
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
- new org
|
||||
- create project
|
||||
- all trpc mutations seems to break in prod
|
||||
- top event convertions
|
||||
- create events_meta (name, color, icon)
|
||||
- edit event convertion
|
||||
@@ -53,12 +53,12 @@ export default withSentryConfig(
|
||||
widenClientFileUpload: true,
|
||||
|
||||
// Transpiles SDK to be compatible with IE11 (increases bundle size)
|
||||
transpileClientSDK: true,
|
||||
transpileClientSDK: false,
|
||||
|
||||
// Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. (increases server load)
|
||||
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
||||
// side errors will fail.
|
||||
tunnelRoute: '/monitoring',
|
||||
// tunnelRoute: '/monitoring',
|
||||
|
||||
// Hides source maps from generated client bundles
|
||||
hideSourceMaps: true,
|
||||
@@ -70,6 +70,6 @@ export default withSentryConfig(
|
||||
// See the following for more information:
|
||||
// https://docs.sentry.io/product/crons/
|
||||
// https://vercel.com/docs/cron-jobs
|
||||
automaticVercelMonitors: true,
|
||||
automaticVercelMonitors: false,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ export function ListReports({ reports }: ListReportsProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<StickyBelowHeader className="p-4 items-center justify-between flex">
|
||||
<StickyBelowHeader className="flex items-center justify-between p-4">
|
||||
<OverviewReportRange />
|
||||
<Button
|
||||
icon={PlusIcon}
|
||||
@@ -54,20 +54,20 @@ export function ListReports({ reports }: ListReportsProps) {
|
||||
<span className="sm:hidden">Report</span>
|
||||
</Button>
|
||||
</StickyBelowHeader>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 p-4">
|
||||
<div className="grid grid-cols-1 gap-4 p-4 sm:grid-cols-2">
|
||||
{reports.map((report) => {
|
||||
const chartRange = report.range; // timeRanges[report.range];
|
||||
return (
|
||||
<div className="card" key={report.id}>
|
||||
<Link
|
||||
href={`/${params.organizationId}/${params.projectId}/reports/${report.id}`}
|
||||
className="flex border-b border-border p-4 leading-none [&_svg]:hover:opacity-100 items-center justify-between"
|
||||
className="flex items-center justify-between border-b border-border p-4 leading-none [&_svg]:hover:opacity-100"
|
||||
shallow
|
||||
>
|
||||
<div>
|
||||
<div className="font-medium">{report.name}</div>
|
||||
{chartRange !== null && (
|
||||
<div className="mt-2 text-sm flex gap-2">
|
||||
<div className="mt-2 flex gap-2 text-sm">
|
||||
<span
|
||||
className={
|
||||
range !== null || (startDate && endDate)
|
||||
@@ -87,7 +87,7 @@ export function ListReports({ reports }: ListReportsProps) {
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="h-8 w-8 hover:border rounded justify-center items-center flex">
|
||||
<DropdownMenuTrigger className="flex h-8 w-8 items-center justify-center rounded hover:border">
|
||||
<MoreHorizontal size={16} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[200px]">
|
||||
@@ -116,7 +116,7 @@ export function ListReports({ reports }: ListReportsProps) {
|
||||
<div
|
||||
className={cn(
|
||||
'p-4',
|
||||
report.chartType === 'bar' && 'overflow-auto max-h-[300px]'
|
||||
report.chartType === 'bar' && 'max-h-[300px] overflow-auto'
|
||||
)}
|
||||
>
|
||||
<LazyChart
|
||||
|
||||
@@ -9,7 +9,7 @@ import { StickyBelowHeader } from '../layout-sticky-below-header';
|
||||
export function HeaderDashboards() {
|
||||
return (
|
||||
<StickyBelowHeader>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<div />
|
||||
<Button
|
||||
icon={PlusIcon}
|
||||
|
||||
@@ -60,13 +60,13 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid sm:grid-cols-2 gap-4 p-4">
|
||||
<div className="grid gap-4 p-4 sm:grid-cols-2">
|
||||
{dashboards.map((item) => (
|
||||
<Card key={item.id} hover>
|
||||
<div>
|
||||
<Link
|
||||
href={`/${organizationId}/${projectId}/dashboards/${item.id}`}
|
||||
className="block p-4 flex flex-col"
|
||||
className="block flex flex-col p-4"
|
||||
>
|
||||
<span className="font-medium">{item.name}</span>
|
||||
</Link>
|
||||
@@ -83,7 +83,7 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
|
||||
Edit
|
||||
</button>
|
||||
</CardActionsItem>
|
||||
<CardActionsItem className="text-destructive w-full" asChild>
|
||||
<CardActionsItem className="w-full text-destructive" asChild>
|
||||
<button
|
||||
onClick={() => {
|
||||
deletion.mutate({
|
||||
|
||||
@@ -20,7 +20,7 @@ export function EventsPerDayChart({ projectId, filters, events }: Props) {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="card p-4 mb-8">
|
||||
<div className="card mb-8 p-4">
|
||||
<ChartSwitchShortcut
|
||||
projectId={projectId}
|
||||
range="1m"
|
||||
|
||||
@@ -22,13 +22,13 @@ export function EventConversionsList({ data }: EventListProps) {
|
||||
<WidgetHead>
|
||||
<div className="title">Conversions</div>
|
||||
</WidgetHead>
|
||||
<div className="flex flex-col gap-2 overflow-y-auto max-h-80 p-4">
|
||||
<div className="flex max-h-80 flex-col gap-2 overflow-y-auto p-4">
|
||||
{data.map((item, index, list) => (
|
||||
<Fragment key={item.id}>
|
||||
{showDateHeader(item.createdAt, list[index - 1]?.createdAt) && (
|
||||
<div className="flex flex-row justify-between gap-2 [&:not(:first-child)]:mt-12">
|
||||
<div className="flex gap-2">
|
||||
<div className="bg-slate-100 border border-slate-300 rounded h-8 px-3 leading-none flex items-center text-sm font-medium gap-2">
|
||||
<div className="flex h-8 items-center gap-2 rounded border border-slate-300 bg-slate-100 px-3 text-sm font-medium leading-none">
|
||||
{item.createdAt.toLocaleDateString()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -157,8 +157,8 @@ export function EventDetails({ event, open, setOpen }: Props) {
|
||||
|
||||
{properties.length > 0 && (
|
||||
<div>
|
||||
<div className="text-sm font-medium mb-2">Params</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<div className="mb-2 text-sm font-medium">Params</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{properties.map((item) => (
|
||||
<KeyValue
|
||||
key={item.name}
|
||||
@@ -177,8 +177,8 @@ export function EventDetails({ event, open, setOpen }: Props) {
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div className="text-sm font-medium mb-2">Common</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<div className="mb-2 text-sm font-medium">Common</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{common.map((item) => (
|
||||
<KeyValue
|
||||
key={item.name}
|
||||
@@ -191,10 +191,10 @@ export function EventDetails({ event, open, setOpen }: Props) {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between text-sm font-medium mb-2">
|
||||
<div className="mb-2 flex justify-between text-sm font-medium">
|
||||
<div>Similar events</div>
|
||||
<button
|
||||
className="hover:underline text-muted-foreground"
|
||||
className="text-muted-foreground hover:underline"
|
||||
onClick={() => {
|
||||
setEvents([event.name]);
|
||||
setOpen(false);
|
||||
|
||||
@@ -80,10 +80,10 @@ export function EventEdit({ event, open, setOpen }: Props) {
|
||||
<SheetHeader>
|
||||
<SheetTitle>Edit "{name}"</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="flex flex-col gap-8 my-8">
|
||||
<div className="my-8 flex flex-col gap-8">
|
||||
<div>
|
||||
<Label className="mb-4 block">Conversion</Label>
|
||||
<label className="cursor-pointer flex items-center select-none border border-border rounded-md p-4 gap-4">
|
||||
<label className="flex cursor-pointer select-none items-center gap-4 rounded-md border border-border p-4">
|
||||
<Checkbox
|
||||
checked={conversion}
|
||||
onCheckedChange={(checked) => {
|
||||
@@ -106,7 +106,7 @@ export function EventEdit({ event, open, setOpen }: Props) {
|
||||
setIcon(name);
|
||||
}}
|
||||
className={cn(
|
||||
'flex-shrink-0 rounded-md w-8 h-8 cursor-pointer inline-flex transition-all bg-slate-100 flex items-center justify-center',
|
||||
'flex inline-flex h-8 w-8 flex-shrink-0 cursor-pointer items-center justify-center rounded-md bg-slate-100 transition-all',
|
||||
name === selectedIcon
|
||||
? 'scale-110 ring-1 ring-black'
|
||||
: '[&_svg]:opacity-50'
|
||||
@@ -127,7 +127,7 @@ export function EventEdit({ event, open, setOpen }: Props) {
|
||||
setColor(color);
|
||||
}}
|
||||
className={cn(
|
||||
'flex-shrink-0 rounded-md w-8 h-8 cursor-pointer transition-all flex justify-center items-center',
|
||||
'flex h-8 w-8 flex-shrink-0 cursor-pointer items-center justify-center rounded-md transition-all',
|
||||
color === selectedColor ? 'ring-1 ring-black' : '',
|
||||
getBg(color)
|
||||
)}
|
||||
|
||||
@@ -23,11 +23,11 @@ import { toast } from 'sonner';
|
||||
|
||||
import type { EventMeta } from '@openpanel/db';
|
||||
|
||||
const variants = cva('flex items-center justify-center shrink-0 rounded-full', {
|
||||
const variants = cva('flex shrink-0 items-center justify-center rounded-full', {
|
||||
variants: {
|
||||
size: {
|
||||
sm: 'w-6 h-6',
|
||||
default: 'w-10 h-10',
|
||||
sm: 'h-6 w-6',
|
||||
default: 'h-10 w-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -55,11 +55,11 @@ export function EventListItem(props: EventListItemProps) {
|
||||
<button
|
||||
onClick={() => setIsDetailsOpen(true)}
|
||||
className={cn(
|
||||
'w-full card p-4 flex hover:bg-slate-50 rounded-lg transition-colors justify-between items-center',
|
||||
'card flex w-full items-center justify-between rounded-lg p-4 transition-colors hover:bg-slate-50',
|
||||
meta?.conversion && `bg-${meta.color}-50 hover:bg-${meta.color}-100`
|
||||
)}
|
||||
>
|
||||
<div className="flex gap-4 items-center text-left text-sm">
|
||||
<div className="flex items-center gap-4 text-left text-sm">
|
||||
<EventIcon size="sm" name={name} meta={meta} projectId={projectId} />
|
||||
<span>
|
||||
<span className="font-medium">{renderName()}</span>
|
||||
@@ -77,14 +77,14 @@ export function EventListItem(props: EventListItemProps) {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
href={`/${organizationId}/${projectId}/profiles/${profile?.id}`}
|
||||
className="text-muted-foreground text-sm hover:underline whitespace-nowrap max-w-[80px] overflow-hidden text-ellipsis"
|
||||
className="max-w-[80px] overflow-hidden text-ellipsis whitespace-nowrap text-sm text-muted-foreground hover:underline"
|
||||
>
|
||||
{profile?.firstName} {profile?.lastName}
|
||||
</Link>
|
||||
</Tooltiper>
|
||||
|
||||
<Tooltiper asChild content={createdAt.toLocaleString()}>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{createdAt.toLocaleTimeString()}
|
||||
</div>
|
||||
</Tooltiper>
|
||||
|
||||
@@ -67,7 +67,7 @@ export function EventList({ data, count }: EventListProps) {
|
||||
<div className="flex flex-row justify-between gap-2 [&:not(:first-child)]:mt-12">
|
||||
{index === 0 ? <EventListener /> : <div />}
|
||||
<div className="flex gap-2">
|
||||
<div className="bg-slate-100 border border-slate-300 rounded h-8 px-3 leading-none flex items-center text-sm font-medium gap-2">
|
||||
<div className="flex h-8 items-center gap-2 rounded border border-slate-300 bg-slate-100 px-3 text-sm font-medium leading-none">
|
||||
{item.createdAt.toLocaleDateString()}
|
||||
</div>
|
||||
{index === 0 && (
|
||||
|
||||
@@ -49,17 +49,17 @@ export default function EventListener() {
|
||||
setCounter(0);
|
||||
router.refresh();
|
||||
}}
|
||||
className="bg-white border border-border rounded h-8 px-3 leading-none flex items-center text-sm font-medium gap-2"
|
||||
className="flex h-8 items-center gap-2 rounded border border-border bg-white px-3 text-sm font-medium leading-none"
|
||||
>
|
||||
<div className="relative">
|
||||
<div
|
||||
className={cn(
|
||||
'bg-emerald-500 h-3 w-3 rounded-full animate-ping opacity-100 transition-all'
|
||||
'h-3 w-3 animate-ping rounded-full bg-emerald-500 opacity-100 transition-all'
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-emerald-500 h-3 w-3 rounded-full absolute top-0 left-0 transition-all'
|
||||
'absolute left-0 top-0 h-3 w-3 rounded-full bg-emerald-500 transition-all'
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ export default async function Page({
|
||||
|
||||
return (
|
||||
<PageLayout title="Events" organizationSlug={organizationId}>
|
||||
<StickyBelowHeader className="p-4 flex justify-between">
|
||||
<StickyBelowHeader className="flex justify-between p-4">
|
||||
<OverviewFiltersDrawer
|
||||
mode="events"
|
||||
projectId={projectId}
|
||||
@@ -65,11 +65,11 @@ export default async function Page({
|
||||
enableEventsFilter
|
||||
/>
|
||||
<OverviewFiltersButtons
|
||||
className="p-0 justify-end"
|
||||
className="justify-end p-0"
|
||||
nuqsOptions={nuqsOptions}
|
||||
/>
|
||||
</StickyBelowHeader>
|
||||
<div className="grid md:grid-cols-2 p-4 gap-4">
|
||||
<div className="grid gap-4 p-4 md:grid-cols-2">
|
||||
<div>
|
||||
<EventList data={events} count={count} />
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ function LinkWithIcon({
|
||||
return (
|
||||
<Link
|
||||
className={cn(
|
||||
'text-slate-800 text-sm font-medium flex gap-2 items-center px-3 py-2 transition-colors hover:bg-blue-100 leading-none rounded-md transition-all',
|
||||
'flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium leading-none text-slate-800 transition-all transition-colors hover:bg-blue-100',
|
||||
active && 'bg-blue-50',
|
||||
className
|
||||
)}
|
||||
@@ -106,7 +106,7 @@ export default function LayoutMenu({ dashboards }: LayoutMenuProps) {
|
||||
href={`/${params.organizationId}/${projectId}/settings/organization`}
|
||||
/>
|
||||
{pathname?.includes('/settings/') && (
|
||||
<div className="pl-7 flex flex-col gap-1">
|
||||
<div className="flex flex-col gap-1 pl-7">
|
||||
<LinkWithIcon
|
||||
icon={BuildingIcon}
|
||||
label="Organization"
|
||||
@@ -136,14 +136,14 @@ export default function LayoutMenu({ dashboards }: LayoutMenuProps) {
|
||||
)}
|
||||
{dashboards.length > 0 && (
|
||||
<div className="mt-8">
|
||||
<div className="font-medium mb-2 text-sm">Your dashboards</div>
|
||||
<div className="mb-2 text-sm font-medium">Your dashboards</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{dashboards.map((item) => (
|
||||
<LinkWithIcon
|
||||
key={item.id}
|
||||
icon={LayoutPanelTopIcon}
|
||||
label={
|
||||
<div className="flex justify-between gap-0.5 items-center">
|
||||
<div className="flex items-center justify-between gap-0.5">
|
||||
<span>{item.name}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{item.project.name}
|
||||
|
||||
@@ -38,35 +38,35 @@ export function LayoutSidebar({
|
||||
<button
|
||||
onClick={() => setActive(false)}
|
||||
className={cn(
|
||||
'fixed top-0 left-0 right-0 bottom-0 backdrop-blur-sm z-30 transition-opacity',
|
||||
'fixed bottom-0 left-0 right-0 top-0 z-30 backdrop-blur-sm transition-opacity',
|
||||
active
|
||||
? 'opacity-100 pointer-events-auto'
|
||||
: 'opacity-0 pointer-events-none'
|
||||
? 'pointer-events-auto opacity-100'
|
||||
: 'pointer-events-none opacity-0'
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
'fixed top-0 left-0 h-screen border-r border-border w-72 bg-white flex flex-col z-30 transition-transform',
|
||||
'fixed left-0 top-0 z-30 flex h-screen w-72 flex-col border-r border-border bg-white transition-transform',
|
||||
'-translate-x-72 lg:-translate-x-0', // responsive
|
||||
active && 'translate-x-0' // force active on mobile
|
||||
)}
|
||||
>
|
||||
<div className="absolute -right-12 h-16 flex items-center lg:hidden">
|
||||
<div className="absolute -right-12 flex h-16 items-center lg:hidden">
|
||||
<Hamburger toggled={active} onToggle={setActive} size={20} />
|
||||
</div>
|
||||
<div className="h-16 border-b border-border px-4 shrink-0 flex items-center">
|
||||
<div className="flex h-16 shrink-0 items-center border-b border-border px-4">
|
||||
<Link href="/">
|
||||
<Logo />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col p-4 gap-2 flex-grow overflow-auto">
|
||||
<div className="flex flex-grow flex-col gap-2 overflow-auto p-4">
|
||||
<LayoutMenu dashboards={dashboards} />
|
||||
{/* Placeholder for LayoutOrganizationSelector */}
|
||||
<div className="h-32 block shrink-0"></div>
|
||||
<div className="block h-32 shrink-0"></div>
|
||||
</div>
|
||||
<div className="fixed bottom-0 left-0 right-0">
|
||||
<div className="bg-gradient-to-t from-white to-white/0 h-8 w-full"></div>
|
||||
<div className="bg-white p-4 pt-0 flex flex-col gap-2">
|
||||
<div className="h-8 w-full bg-gradient-to-t from-white to-white/0"></div>
|
||||
<div className="flex flex-col gap-2 bg-white p-4 pt-0">
|
||||
<Link
|
||||
className={cn('flex gap-2', buttonVariants())}
|
||||
href={`/${organizationId}/${projectId}/reports`}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function StickyBelowHeader({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'md:sticky bg-white border-b border-border z-20 [[id=dashboard]_&]:top-16 [[id=dashboard]_&]:rounded-none rounded-lg top-0',
|
||||
'top-0 z-20 rounded-lg border-b border-border bg-white md:sticky [[id=dashboard]_&]:top-16 [[id=dashboard]_&]:rounded-none',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -55,7 +55,7 @@ export default async function AppLayout({
|
||||
<LayoutSidebar
|
||||
{...{ organizationId, projectId, organizations, dashboards }}
|
||||
/>
|
||||
<div className="lg:pl-72 transition-all">{children}</div>
|
||||
<div className="transition-all lg:pl-72">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default async function PageLayout({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-16 border-b border-border flex-shrink-0 sticky top-0 bg-white px-4 flex items-center justify-between z-20 pl-12 lg:pl-4">
|
||||
<div className="sticky top-0 z-20 flex h-16 flex-shrink-0 items-center justify-between border-b border-border bg-white px-4 pl-12 lg:pl-4">
|
||||
<div className="text-xl font-medium">{title}</div>
|
||||
{projects.length > 0 && <LayoutProjectSelector projects={projects} />}
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default async function Page({
|
||||
return (
|
||||
<PageLayout title="Overview" organizationSlug={organizationId}>
|
||||
<StickyBelowHeader>
|
||||
<div className="p-4 flex gap-2 justify-between">
|
||||
<div className="flex justify-between gap-2 p-4">
|
||||
<div className="flex gap-2">
|
||||
<OverviewReportRange />
|
||||
<OverviewFiltersDrawer projectId={projectId} mode="events" />
|
||||
@@ -47,7 +47,7 @@ export default async function Page({
|
||||
</div>
|
||||
<OverviewFiltersButtons />
|
||||
</StickyBelowHeader>
|
||||
<div className="p-4 grid gap-4 grid-cols-6">
|
||||
<div className="grid grid-cols-6 gap-4 p-4">
|
||||
<div className="col-span-6">
|
||||
<OverviewLiveHistogram projectId={projectId} />
|
||||
</div>
|
||||
|
||||
@@ -133,7 +133,7 @@ export default async function Page({
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<StickyBelowHeader className="p-4 flex justify-between">
|
||||
<StickyBelowHeader className="flex justify-between p-4">
|
||||
<OverviewFiltersDrawer
|
||||
projectId={projectId}
|
||||
mode="events"
|
||||
@@ -141,11 +141,11 @@ export default async function Page({
|
||||
/>
|
||||
<OverviewFiltersButtons
|
||||
nuqsOptions={{ shallow: false }}
|
||||
className="p-0 justify-end"
|
||||
className="justify-end p-0"
|
||||
/>
|
||||
</StickyBelowHeader>
|
||||
<div className="p-4">
|
||||
<div className="grid gap-4 grid-cols-1 md:grid-cols-2 mb-8">
|
||||
<div className="mb-8 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<EventList data={events} count={count} />
|
||||
</div>
|
||||
@@ -159,7 +159,7 @@ export default async function Page({
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
<Widget className="w-full">
|
||||
<WidgetHead className="flex justify-between items-center">
|
||||
<WidgetHead className="flex items-center justify-between">
|
||||
<span className="title">Profile</span>
|
||||
<ProfileAvatar {...profile} />
|
||||
</WidgetHead>
|
||||
@@ -199,10 +199,10 @@ function ValueRow({ name, value }: { name: string; value?: unknown }) {
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-row justify-between p-2 px-4">
|
||||
<div className="font-medium text-muted-foreground capitalize">
|
||||
<div className="font-medium capitalize text-muted-foreground">
|
||||
{name.replace('_', ' ')}
|
||||
</div>
|
||||
<div className="flex gap-2 items-center text-right">
|
||||
<div className="flex items-center gap-2 text-right">
|
||||
{typeof value === 'string' ? (
|
||||
<>
|
||||
<SerieIcon name={value} /> {value}
|
||||
|
||||
@@ -30,18 +30,18 @@ export default function Page({
|
||||
}: PageProps) {
|
||||
return (
|
||||
<PageLayout title="Profiles" organizationSlug={organizationId}>
|
||||
<StickyBelowHeader className="p-4 flex justify-between">
|
||||
<StickyBelowHeader className="flex justify-between p-4">
|
||||
<OverviewFiltersDrawer
|
||||
projectId={projectId}
|
||||
nuqsOptions={nuqsOptions}
|
||||
mode="events"
|
||||
/>
|
||||
<OverviewFiltersButtons
|
||||
className="p-0 justify-end"
|
||||
className="justify-end p-0"
|
||||
nuqsOptions={nuqsOptions}
|
||||
/>
|
||||
</StickyBelowHeader>
|
||||
<div className="p-4 grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 gap-4 p-4 md:grid-cols-2">
|
||||
<ProfileListServer
|
||||
projectId={projectId}
|
||||
cursor={parseAsInteger.parseServerSide(cursor ?? '') ?? undefined}
|
||||
|
||||
@@ -29,11 +29,11 @@ export default async function ProfileLastSeenServer({ projectId }: Props) {
|
||||
const renderItem = (item: Row) => (
|
||||
<div
|
||||
key={item.days}
|
||||
className="flex-1 shrink-0 h-full flex flex-col items-center"
|
||||
className="flex h-full flex-1 shrink-0 flex-col items-center"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="w-full flex-1 bg-slate-200 rounded flex flex-col justify-end">
|
||||
<div className="flex w-full flex-1 flex-col justify-end rounded bg-slate-200">
|
||||
<div
|
||||
className={cn(
|
||||
'w-full rounded',
|
||||
@@ -50,7 +50,7 @@ export default async function ProfileLastSeenServer({ projectId }: Props) {
|
||||
{item.days === 0 ? 'today' : `${item.days} days ago`}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<div className="text-xs mt-1">{item.days}</div>
|
||||
<div className="mt-1 text-xs">{item.days}</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
@@ -59,7 +59,7 @@ export default async function ProfileLastSeenServer({ projectId }: Props) {
|
||||
<div className="title">Last seen</div>
|
||||
</WidgetHead>
|
||||
<WidgetBody>
|
||||
<div className="flex aspect-[3/1] w-full gap-1 items-end">
|
||||
<div className="flex aspect-[3/1] w-full items-end gap-1">
|
||||
{res.length >= 18 ? (
|
||||
<>
|
||||
{res.slice(0, split).map(renderItem)}
|
||||
|
||||
@@ -25,7 +25,7 @@ export function ProfileList({ data, count }: ProfileListProps) {
|
||||
const { cursor, setCursor } = useCursor();
|
||||
return (
|
||||
<Widget>
|
||||
<WidgetHead className="flex justify-between items-center">
|
||||
<WidgetHead className="flex items-center justify-between">
|
||||
<div className="title">Profiles</div>
|
||||
<Pagination
|
||||
size="sm"
|
||||
@@ -47,7 +47,7 @@ export function ProfileList({ data, count }: ProfileListProps) {
|
||||
return (
|
||||
<Link
|
||||
href={`/${organizationId}/${projectId}/profiles/${profile.id}`}
|
||||
className="flex gap-2 items-center font-medium"
|
||||
className="flex items-center gap-2 font-medium"
|
||||
>
|
||||
<ProfileAvatar size="sm" {...profile} />
|
||||
{getProfileName(profile)}
|
||||
@@ -69,7 +69,7 @@ export function ProfileList({ data, count }: ProfileListProps) {
|
||||
asChild
|
||||
content={profile.createdAt.toLocaleString()}
|
||||
>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{profile.createdAt.toLocaleTimeString()}
|
||||
</div>
|
||||
</Tooltiper>
|
||||
@@ -78,7 +78,7 @@ export function ProfileList({ data, count }: ProfileListProps) {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className="p-4 border-t border-border">
|
||||
<div className="border-t border-border p-4">
|
||||
<Pagination
|
||||
cursor={cursor}
|
||||
setCursor={setCursor}
|
||||
|
||||
@@ -44,7 +44,7 @@ export default async function ProfileTopServer({
|
||||
return (
|
||||
<Link
|
||||
href={`/${organizationId}/${projectId}/profiles/${profile.id}`}
|
||||
className="flex gap-2 items-center font-medium"
|
||||
className="flex items-center gap-2 font-medium"
|
||||
>
|
||||
<ProfileAvatar size="sm" {...profile} />
|
||||
{getProfileName(profile)}
|
||||
|
||||
@@ -27,7 +27,7 @@ export default async function Page({
|
||||
<PageLayout
|
||||
organizationSlug={organizationId}
|
||||
title={
|
||||
<div className="flex gap-2 items-center cursor-pointer">
|
||||
<div className="flex cursor-pointer items-center gap-2">
|
||||
{report.name}
|
||||
<Pencil size={16} />
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function Page({ params: { organizationId } }: PageProps) {
|
||||
<PageLayout
|
||||
organizationSlug={organizationId}
|
||||
title={
|
||||
<div className="flex gap-2 items-center cursor-pointer">
|
||||
<div className="flex cursor-pointer items-center gap-2">
|
||||
Unnamed report
|
||||
<Pencil size={16} />
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function ReportEditor({
|
||||
|
||||
return (
|
||||
<Sheet>
|
||||
<StickyBelowHeader className="p-4 grid grid-cols-2 gap-2 md:grid-cols-6">
|
||||
<StickyBelowHeader className="grid grid-cols-2 gap-2 p-4 md:grid-cols-6">
|
||||
<SheetTrigger asChild>
|
||||
<div>
|
||||
<Button icon={GanttChartSquareIcon} variant="cta">
|
||||
@@ -61,7 +61,7 @@ export default function ReportEditor({
|
||||
</Button>
|
||||
</div>
|
||||
</SheetTrigger>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 col-span-4">
|
||||
<div className="col-span-4 grid grid-cols-2 gap-2 md:grid-cols-4">
|
||||
<ReportChartType className="min-w-0 flex-1" />
|
||||
<ReportRange
|
||||
className="min-w-0 flex-1"
|
||||
@@ -93,7 +93,7 @@ export default function ReportEditor({
|
||||
<ReportInterval className="min-w-0 flex-1" />
|
||||
<ReportLineType className="min-w-0 flex-1" />
|
||||
</div>
|
||||
<div className="col-start-2 md:col-start-6 row-start-1 text-right">
|
||||
<div className="col-start-2 row-start-1 text-right md:col-start-6">
|
||||
<ReportSaveButton />
|
||||
</div>
|
||||
</StickyBelowHeader>
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function ListClients({ clients }: ListClientsProps) {
|
||||
return (
|
||||
<>
|
||||
<StickyBelowHeader>
|
||||
<div className="p-4 flex items-center justify-between">
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<div />
|
||||
<Button icon={PlusIcon} onClick={() => pushModal('AddClient')}>
|
||||
<span className="max-sm:hidden">Create client</span>
|
||||
|
||||
@@ -123,7 +123,7 @@ export default function CreateInvite({ projects }: Props) {
|
||||
value: item.id,
|
||||
}))}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Leave empty to give access to all projects
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -73,7 +73,7 @@ function Item({
|
||||
{new Date(createdAt).toLocaleDateString()}
|
||||
</TooltipComplete>
|
||||
</TableCell>
|
||||
<TableCell className="capitalize flex items-center gap-2">
|
||||
<TableCell className="flex items-center gap-2 capitalize">
|
||||
<Dot
|
||||
className={cn(
|
||||
status === 'accepted' && 'bg-emerald-600',
|
||||
|
||||
@@ -26,7 +26,7 @@ export default async function Page({
|
||||
|
||||
return (
|
||||
<PageLayout title={organization.name} organizationSlug={organizationSlug}>
|
||||
<div className="p-4 grid grid-cols-1 gap-8">
|
||||
<div className="grid grid-cols-1 gap-8 p-4">
|
||||
<EditOrganization organization={organization} />
|
||||
<MembersServer organizationSlug={organizationSlug} />
|
||||
<InvitesServer organizationSlug={organizationSlug} />
|
||||
|
||||
@@ -17,7 +17,7 @@ export default async function Page({ params: { organizationId } }: PageProps) {
|
||||
|
||||
return (
|
||||
<PageLayout title={profile.lastName} organizationSlug={organizationId}>
|
||||
<div className="p-4 flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<EditProfile profile={profile} />
|
||||
<Logout />
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function ListProjects({ projects }: ListProjectsProps) {
|
||||
return (
|
||||
<>
|
||||
<StickyBelowHeader>
|
||||
<div className="p-4 flex items-center justify-between">
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<div />
|
||||
<Button
|
||||
icon={PlusIcon}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function ListReferences({ data }: ListProjectsProps) {
|
||||
return (
|
||||
<>
|
||||
<StickyBelowHeader>
|
||||
<div className="p-4 flex items-center justify-between">
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<div />
|
||||
<Button icon={PlusIcon} onClick={() => pushModal('AddReference')}>
|
||||
<span className="max-sm:hidden">Create reference</span>
|
||||
|
||||
@@ -44,8 +44,8 @@ export function CreateProject() {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<LogoSquare className="w-20 md:w-28 mb-8" />
|
||||
<h1 className="font-medium text-3xl">Create your first project</h1>
|
||||
<LogoSquare className="mb-8 w-20 md:w-28" />
|
||||
<h1 className="text-3xl font-medium">Create your first project</h1>
|
||||
<div className="text-lg">
|
||||
A project is just a container for your events. You can create as many
|
||||
as you want.
|
||||
|
||||
@@ -31,10 +31,10 @@ export default async function Page({ params: { organizationId } }: PageProps) {
|
||||
const isAccepted = await isWaitlistUserAccepted();
|
||||
if (!isAccepted) {
|
||||
return (
|
||||
<div className="p-4 flex items-center justify-center h-screen">
|
||||
<div className="max-w-lg w-full">
|
||||
<LogoSquare className="w-20 md:w-28 mb-8" />
|
||||
<h1 className="font-medium text-3xl">Not quite there yet</h1>
|
||||
<div className="flex h-screen items-center justify-center p-4">
|
||||
<div className="w-full max-w-lg">
|
||||
<LogoSquare className="mb-8 w-20 md:w-28" />
|
||||
<h1 className="text-3xl font-medium">Not quite there yet</h1>
|
||||
<div className="text-lg">
|
||||
We're still working on Openpanel, but we're not quite there yet.
|
||||
We'll let you know when we're ready to go!
|
||||
@@ -47,8 +47,8 @@ export default async function Page({ params: { organizationId } }: PageProps) {
|
||||
|
||||
if (projects.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen p-4 ">
|
||||
<div className="max-w-lg w-full">
|
||||
<div className="flex h-screen items-center justify-center p-4 ">
|
||||
<div className="w-full max-w-lg">
|
||||
<CreateProject />
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,8 +60,8 @@ export default async function Page({ params: { organizationId } }: PageProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-xl w-full mx-auto flex flex-col gap-4 pt-20 p-4 ">
|
||||
<h1 className="font-medium text-xl">Select project</h1>
|
||||
<div className="mx-auto flex w-full max-w-xl flex-col gap-4 p-4 pt-20 ">
|
||||
<h1 className="text-xl font-medium">Select project</h1>
|
||||
{projects.map((item) => (
|
||||
<ProjectCard key={item.id} {...item} />
|
||||
))}
|
||||
|
||||
@@ -58,14 +58,14 @@ export function CreateOrganization() {
|
||||
if (mutation.isSuccess && mutation.data.client) {
|
||||
return (
|
||||
<div className="card p-4 md:p-8">
|
||||
<LogoSquare className="w-20 mb-4" />
|
||||
<h1 className="font-medium text-3xl">Nice job!</h1>
|
||||
<LogoSquare className="mb-4 w-20" />
|
||||
<h1 className="text-3xl font-medium">Nice job!</h1>
|
||||
<div className="mb-4">
|
||||
You're ready to start using our SDK. Save the client ID and secret (if
|
||||
you have any)
|
||||
</div>
|
||||
<CreateClientSuccess {...mutation.data.client} />
|
||||
<div className="flex gap-4 mt-4">
|
||||
<div className="mt-4 flex gap-4">
|
||||
<a
|
||||
className={cn(buttonVariants({ variant: 'secondary' }), 'flex-1')}
|
||||
href="https://docs.openpanel.dev/docs"
|
||||
@@ -87,8 +87,8 @@ export function CreateOrganization() {
|
||||
|
||||
return (
|
||||
<div className="card p-4 md:p-8">
|
||||
<LogoSquare className="w-20 mb-4" />
|
||||
<h1 className="font-medium text-3xl">Welcome to Openpanel</h1>
|
||||
<LogoSquare className="mb-4 w-20" />
|
||||
<h1 className="text-3xl font-medium">Welcome to Openpanel</h1>
|
||||
<div className="text-lg">
|
||||
Create your organization below (can be personal or a company) and your
|
||||
first project.
|
||||
@@ -132,7 +132,7 @@ export function CreateOrganization() {
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="other">
|
||||
<div className="p-2 px-3 bg-white rounded text-sm">
|
||||
<div className="rounded bg-white p-2 px-3 text-sm">
|
||||
🔑 You will get a secret to use for your API requests.
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
@@ -13,10 +13,10 @@ export default async function Page() {
|
||||
const isAccepted = await isWaitlistUserAccepted();
|
||||
if (!isAccepted) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen">
|
||||
<div className="max-w-lg w-full">
|
||||
<LogoSquare className="w-20 md:w-28 mb-8" />
|
||||
<h1 className="font-medium text-3xl">Not quite there yet</h1>
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="w-full max-w-lg">
|
||||
<LogoSquare className="mb-8 w-20 md:w-28" />
|
||||
<h1 className="text-3xl font-medium">Not quite there yet</h1>
|
||||
<div className="text-lg">
|
||||
We're still working on Openpanel, but we're not quite there yet.
|
||||
We'll let you know when we're ready to go!
|
||||
@@ -32,8 +32,8 @@ export default async function Page() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen">
|
||||
<div className="max-w-lg w-full">
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="w-full max-w-lg">
|
||||
<CreateOrganization />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,12 +32,12 @@ export default async function Page({ params: { id } }: PageProps) {
|
||||
const organization = await getOrganizationBySlug(share.organization_slug);
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-16 bg-gradient-to-tl from-blue-950 to-blue-600">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="flex justify-between items-end mb-4">
|
||||
<div className="bg-gradient-to-tl from-blue-950 to-blue-600 p-4 md:p-16">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="mb-4 flex items-end justify-between">
|
||||
<div className="leading-none">
|
||||
<span className="text-white mb-4">{organization?.name}</span>
|
||||
<h1 className="text-white text-xl font-medium">
|
||||
<span className="mb-4 text-white">{organization?.name}</span>
|
||||
<h1 className="text-xl font-medium text-white">
|
||||
{share.project?.name}
|
||||
</h1>
|
||||
</div>
|
||||
@@ -45,9 +45,9 @@ export default async function Page({ params: { id } }: PageProps) {
|
||||
<Logo className="text-white max-sm:[&_span]:hidden" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="max-sm:-mx-3 bg-slate-100 rounded-lg shadow ring-8 ring-blue-600/50">
|
||||
<div className="rounded-lg bg-slate-100 shadow ring-8 ring-blue-600/50 max-sm:-mx-3">
|
||||
<StickyBelowHeader>
|
||||
<div className="p-4 flex gap-2 justify-between">
|
||||
<div className="flex justify-between gap-2 p-4">
|
||||
<div className="flex gap-2">
|
||||
<OverviewReportRange />
|
||||
{/* <OverviewFiltersDrawer projectId={projectId} mode="events" /> */}
|
||||
@@ -58,7 +58,7 @@ export default async function Page({ params: { id } }: PageProps) {
|
||||
</div>
|
||||
<OverviewFiltersButtons />
|
||||
</StickyBelowHeader>
|
||||
<div className="p-4 grid gap-4 grid-cols-6">
|
||||
<div className="grid grid-cols-6 gap-4 p-4">
|
||||
<div className="col-span-6">
|
||||
<OverviewLiveHistogram projectId={projectId} />
|
||||
</div>
|
||||
|
||||
@@ -28,8 +28,8 @@ export default function Auth() {
|
||||
const pathname = usePathname();
|
||||
const [state, setState] = useState<string | null>(null);
|
||||
return (
|
||||
<div className="flex items-center justify-center flex-col h-screen p-4">
|
||||
<Widget className="max-w-md w-full mb-4">
|
||||
<div className="flex h-screen flex-col items-center justify-center p-4">
|
||||
<Widget className="mb-4 w-full max-w-md">
|
||||
<WidgetBody>
|
||||
<div className="flex justify-center py-8">
|
||||
<Logo />
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en" className="light">
|
||||
<body
|
||||
className={cn('min-h-screen font-sans antialiased grainy bg-slate-100')}
|
||||
className={cn('grainy min-h-screen bg-slate-100 font-sans antialiased')}
|
||||
>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
|
||||
@@ -6,6 +6,6 @@ export function ButtonContainer({
|
||||
...props
|
||||
}: HtmlProps<HTMLDivElement>) {
|
||||
return (
|
||||
<div className={cn('flex justify-between mt-6', className)} {...props} />
|
||||
<div className={cn('mt-6 flex justify-between', className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ interface CardActionsProps {
|
||||
}
|
||||
export function CardActions({ children }: CardActionsProps) {
|
||||
return (
|
||||
<div className="absolute top-2 right-2 z-10">
|
||||
<div className="absolute right-2 top-2 z-10">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="h-8 w-8 hover:border rounded justify-center items-center flex">
|
||||
<DropdownMenuTrigger className="flex h-8 w-8 items-center justify-center rounded hover:border">
|
||||
<MoreHorizontal size={16} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[200px]">
|
||||
|
||||
@@ -13,12 +13,12 @@ export const InputWithLabel = forwardRef<HTMLInputElement, InputWithLabelProps>(
|
||||
({ label, className, ...props }, ref) => {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="block mb-2 flex justify-between">
|
||||
<div className="mb-2 block flex justify-between">
|
||||
<Label className="mb-0" htmlFor={label}>
|
||||
{label}
|
||||
</Label>
|
||||
{props.error && (
|
||||
<span className="text-sm text-destructive leading-none">
|
||||
<span className="text-sm leading-none text-destructive">
|
||||
{props.error}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -16,13 +16,13 @@ export function FullPageEmptyState({
|
||||
className,
|
||||
}: FullPageEmptyStateProps) {
|
||||
return (
|
||||
<div className={cn('p-4 flex items-center justify-center', className)}>
|
||||
<div className="p-8 w-full max-w-xl flex flex-col items-center justify-center">
|
||||
<div className="w-24 h-24 bg-white shadow-sm rounded-full flex justify-center items-center mb-6">
|
||||
<div className={cn('flex items-center justify-center p-4', className)}>
|
||||
<div className="flex w-full max-w-xl flex-col items-center justify-center p-8">
|
||||
<div className="mb-6 flex h-24 w-24 items-center justify-center rounded-full bg-white shadow-sm">
|
||||
<Icon size={60} strokeWidth={1} />
|
||||
</div>
|
||||
|
||||
<h1 className="text-xl font-medium mb-1">{title}</h1>
|
||||
<h1 className="mb-1 text-xl font-medium">{title}</h1>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ export function LogoSquare({ className }: LogoProps) {
|
||||
export function Logo({ className }: LogoProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn('text-xl font-medium flex gap-2 items-center', className)}
|
||||
className={cn('flex items-center gap-2 text-xl font-medium', className)}
|
||||
>
|
||||
<LogoSquare className="max-h-8" />
|
||||
<span>openpanel.dev</span>
|
||||
|
||||
@@ -76,7 +76,7 @@ export function OverviewFiltersDrawerContent({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4 mt-8">
|
||||
<div className="mt-8 flex flex-col gap-4">
|
||||
{filters
|
||||
.filter((filter) => filter.value[0] !== null)
|
||||
.map((filter) => {
|
||||
@@ -120,7 +120,7 @@ export function FilterOptionEvent({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<div>{filter.name}</div>
|
||||
<Combobox
|
||||
className="flex-1"
|
||||
@@ -160,7 +160,7 @@ export function FilterOptionProfile({
|
||||
const values = useProfileValues(projectId, filter.name);
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<div>{filter.name}</div>
|
||||
<Combobox
|
||||
className="flex-1"
|
||||
|
||||
@@ -17,7 +17,7 @@ export function OverviewFiltersDrawer(
|
||||
Filters
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="!max-w-lg w-full" side="right">
|
||||
<SheetContent className="w-full !max-w-lg" side="right">
|
||||
<OverviewFiltersDrawerContent {...props} />
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
@@ -58,18 +58,18 @@ export default function LiveCounter({ data = 0, projectId }: LiveCounterProps) {
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() => setLiveHistogram((p) => !p)}
|
||||
className="border border-border rounded h-8 px-3 leading-none flex items-center font-medium gap-2"
|
||||
className="flex h-8 items-center gap-2 rounded border border-border px-3 font-medium leading-none"
|
||||
>
|
||||
<div className="relative">
|
||||
<div
|
||||
className={cn(
|
||||
'bg-emerald-500 h-3 w-3 rounded-full animate-ping opacity-100 transition-all',
|
||||
'h-3 w-3 animate-ping rounded-full bg-emerald-500 opacity-100 transition-all',
|
||||
counter === 0 && 'bg-destructive opacity-0'
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-emerald-500 h-3 w-3 rounded-full absolute top-0 left-0 transition-all',
|
||||
'absolute left-0 top-0 h-3 w-3 rounded-full bg-emerald-500 transition-all',
|
||||
counter === 0 && 'bg-destructive'
|
||||
)}
|
||||
></div>
|
||||
|
||||
@@ -85,7 +85,7 @@ export function OverviewLiveHistogram({
|
||||
{staticArray.map((percent, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex-1 rounded-md bg-slate-200 animate-pulse"
|
||||
className="flex-1 animate-pulse rounded-md bg-slate-200"
|
||||
style={{ height: `${percent}%` }}
|
||||
/>
|
||||
))}
|
||||
@@ -105,7 +105,7 @@ export function OverviewLiveHistogram({
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn(
|
||||
'flex-1 rounded-md hover:scale-110 transition-all ease-in-out',
|
||||
'flex-1 rounded-md transition-all ease-in-out hover:scale-110',
|
||||
minute.count === 0 ? 'bg-slate-200' : 'bg-blue-600'
|
||||
)}
|
||||
style={{
|
||||
@@ -136,14 +136,14 @@ interface WrapperProps {
|
||||
function Wrapper({ open, children, count }: WrapperProps) {
|
||||
return (
|
||||
<AnimateHeight duration={500} height={open ? 'auto' : 0}>
|
||||
<div className="flex items-end flex-col md:flex-row">
|
||||
<div className="md:mr-2 flex md:flex-col max-md:justify-between items-end max-md:w-full max-md:mb-2 md:card md:p-4">
|
||||
<div className="flex flex-col items-end md:flex-row">
|
||||
<div className="md:card flex items-end max-md:mb-2 max-md:w-full max-md:justify-between md:mr-2 md:flex-col md:p-4">
|
||||
<div className="text-sm max-md:mb-1">Last 30 minutes</div>
|
||||
<div className="text-2xl font-bold text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-2xl font-bold">
|
||||
{count}
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-h-[150px] aspect-[5/1] flex flex-1 gap-0.5 md:gap-2 items-end w-full relative">
|
||||
<div className="relative flex aspect-[5/1] max-h-[150px] w-full flex-1 items-end gap-0.5 md:gap-2">
|
||||
<div className="absolute -top-3 right-0 text-xs text-muted-foreground">
|
||||
NOW
|
||||
</div>
|
||||
|
||||
@@ -192,13 +192,13 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-6 col-span-6 gap-1">
|
||||
<div className="col-span-6 grid grid-cols-6 gap-1">
|
||||
{reports.map((report, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className={cn(
|
||||
'relative col-span-3 md:col-span-2 lg:col-span-1 group transition-all scale-95',
|
||||
index === metric && 'shadow-md rounded-xl scale-105 z-10'
|
||||
'group relative col-span-3 scale-95 transition-all md:col-span-2 lg:col-span-1',
|
||||
index === metric && 'z-10 scale-105 rounded-xl shadow-md'
|
||||
)}
|
||||
onClick={() => {
|
||||
setMetric(index);
|
||||
|
||||
@@ -20,7 +20,7 @@ export function WidgetHead({ className, ...props }: WidgetHeadProps) {
|
||||
return (
|
||||
<WidgetHeadBase
|
||||
className={cn(
|
||||
'flex flex-col p-0 [&_.title]:p-4 [&_.title]:flex [&_.title]:justify-between [&_.title]:items-center',
|
||||
'flex flex-col p-0 [&_.title]:flex [&_.title]:items-center [&_.title]:justify-between [&_.title]:p-4',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -85,7 +85,7 @@ export function WidgetButtons({
|
||||
<div
|
||||
ref={container}
|
||||
className={cn(
|
||||
'px-4 self-stretch justify-start transition-opacity flex flex-wrap [&_button]:text-xs [&_button]:opacity-50 [&_button]:whitespace-nowrap [&_button.active]:opacity-100 [&_button.active]:border-b [&_button.active]:border-black [&_button]:py-1',
|
||||
'flex flex-wrap justify-start self-stretch px-4 transition-opacity [&_button.active]:border-b [&_button.active]:border-black [&_button.active]:opacity-100 [&_button]:whitespace-nowrap [&_button]:py-1 [&_button]:text-xs [&_button]:opacity-50',
|
||||
className
|
||||
)}
|
||||
style={{ gap }}
|
||||
@@ -102,7 +102,7 @@ export function WidgetButtons({
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className={cn(
|
||||
'flex items-center gap-1 select-none',
|
||||
'flex select-none items-center gap-1',
|
||||
sizes.current.length - 1 === slice ? hidden : 'opacity-50'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -48,9 +48,9 @@ export function Pagination({
|
||||
>
|
||||
{size === 'base' && (
|
||||
<>
|
||||
<div className="font-medium text-xs">Page: {cursor + 1}</div>
|
||||
<div className="text-xs font-medium">Page: {cursor + 1}</div>
|
||||
{typeof count === 'number' && (
|
||||
<div className="font-medium text-xs">Total rows: {count}</div>
|
||||
<div className="text-xs font-medium">Total rows: {count}</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -34,30 +34,30 @@ export async function ProjectCard({
|
||||
return (
|
||||
<Link
|
||||
href={`/${organizationSlug}/${id}`}
|
||||
className="card p-4 inline-flex flex-col gap-2 hover:-translate-y-1 transition-transform"
|
||||
className="card inline-flex flex-col gap-2 p-4 transition-transform hover:-translate-y-1"
|
||||
>
|
||||
<div className="font-medium">{name}</div>
|
||||
<div className="aspect-[15/1] -mx-4">
|
||||
<div className="-mx-4 aspect-[15/1]">
|
||||
<ChartSSR data={chart.map((d) => ({ ...d, date: new Date(d.date) }))} />
|
||||
</div>
|
||||
<div className="flex gap-4 justify-between text-muted-foreground text-sm">
|
||||
<div className="flex justify-between gap-4 text-sm text-muted-foreground">
|
||||
<div className="font-medium">Visitors</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex flex-col md:flex-row gap-2">
|
||||
<div className="flex flex-col gap-2 md:flex-row">
|
||||
<div>Total</div>
|
||||
<span className="text-black font-medium">
|
||||
<span className="font-medium text-black">
|
||||
{shortNumber('en')(data?.total)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-2">
|
||||
<div className="flex flex-col gap-2 md:flex-row">
|
||||
<div>Month</div>
|
||||
<span className="text-black font-medium">
|
||||
<span className="font-medium text-black">
|
||||
{shortNumber('en')(data?.month)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-2">
|
||||
<div className="flex flex-col gap-2 md:flex-row">
|
||||
<div>24h</div>
|
||||
<span className="text-black font-medium">
|
||||
<span className="font-medium text-black">
|
||||
{shortNumber('en')(data?.day)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -95,7 +95,7 @@ export function PreviousDiffIndicatorText({
|
||||
return (
|
||||
<div
|
||||
className={cn([
|
||||
'flex gap-0.5 items-center',
|
||||
'flex items-center gap-0.5',
|
||||
getDiffIndicator(
|
||||
previousIndicatorInverted,
|
||||
state,
|
||||
|
||||
@@ -62,7 +62,7 @@ export function ReportRange({
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<div className="p-4 border-b border-border">
|
||||
<div className="border-b border-border p-4">
|
||||
<ToggleGroup
|
||||
value={range}
|
||||
onValueChange={(value) => {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const ChartAnimationContainer = (
|
||||
<div
|
||||
{...props}
|
||||
className={cn(
|
||||
'border border-border rounded-md p-8 bg-white',
|
||||
'rounded-md border border-border bg-white p-8',
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -22,7 +22,7 @@ export function ChartEmpty() {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'aspect-video w-full max-h-[300px] min-h-[200px] flex justify-center items-center'
|
||||
'flex aspect-video max-h-[300px] min-h-[200px] w-full items-center justify-center'
|
||||
}
|
||||
>
|
||||
No data
|
||||
|
||||
@@ -7,7 +7,7 @@ export function ChartLoading({ className }: ChartLoadingProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'aspect-video w-full bg-slate-200 animate-pulse rounded max-h-[300px] min-h-[200px]',
|
||||
'aspect-video max-h-[300px] min-h-[200px] w-full animate-pulse rounded bg-slate-200',
|
||||
className
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -56,10 +56,10 @@ export function MetricCard({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="group relative card p-4 overflow-hidden h-24"
|
||||
className="card group relative h-24 overflow-hidden p-4"
|
||||
key={serie.name}
|
||||
>
|
||||
<div className="absolute inset-0 -left-1 -right-1 z-0 opacity-20 transition-opacity duration-300 group-hover:opacity-50 rounded-md">
|
||||
<div className="absolute inset-0 -left-1 -right-1 z-0 rounded-md opacity-20 transition-opacity duration-300 group-hover:opacity-50">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<AreaChart
|
||||
@@ -82,21 +82,21 @@ export function MetricCard({
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2 font-medium text-left min-w-0">
|
||||
<div className="flex min-w-0 items-center gap-2 text-left font-medium">
|
||||
<ColorSquare>{serie.event.id}</ColorSquare>
|
||||
<span className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{serie.name || serie.event.displayName || serie.event.name}
|
||||
</span>
|
||||
</div>
|
||||
{/* <PreviousDiffIndicator {...serie.metrics.previous[metric]} /> */}
|
||||
</div>
|
||||
<div className="flex justify-between items-end mt-2">
|
||||
<div className="text-2xl font-bold text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div className="mt-2 flex items-end justify-between">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-2xl font-bold">
|
||||
{renderValue(serie.metrics[metric], 'ml-1 font-light text-xl')}
|
||||
</div>
|
||||
<PreviousDiffIndicatorText
|
||||
{...serie.metrics.previous[metric]}
|
||||
className="font-medium text-xs mb-0.5"
|
||||
className="mb-0.5 text-xs font-medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,8 +106,8 @@ export function MetricCard({
|
||||
|
||||
export function MetricCardEmpty() {
|
||||
return (
|
||||
<div className="card p-4 h-24">
|
||||
<div className="flex items-center justify-center h-full text-slate-600">
|
||||
<div className="card h-24 p-4">
|
||||
<div className="flex h-full items-center justify-center text-slate-600">
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,9 +116,9 @@ export function MetricCardEmpty() {
|
||||
|
||||
export function MetricCardLoading() {
|
||||
return (
|
||||
<div className="h-24 p-4 py-5 flex flex-col card">
|
||||
<div className="bg-slate-200 rounded animate-pulse h-4 w-1/2"></div>
|
||||
<div className="bg-slate-200 rounded animate-pulse h-6 w-1/5 mt-auto"></div>
|
||||
<div className="card flex h-24 flex-col p-4 py-5">
|
||||
<div className="h-4 w-1/2 animate-pulse rounded bg-slate-200"></div>
|
||||
<div className="mt-auto h-6 w-1/5 animate-pulse rounded bg-slate-200"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ export function ReportBarChart({ data }: ReportBarChartProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col w-full text-xs -mx-2',
|
||||
editMode && 'text-base card p-4'
|
||||
'-mx-2 flex w-full flex-col text-xs',
|
||||
editMode && 'card p-4 text-base'
|
||||
)}
|
||||
>
|
||||
{series.map((serie, index) => {
|
||||
@@ -39,17 +39,17 @@ export function ReportBarChart({ data }: ReportBarChartProps) {
|
||||
<div
|
||||
key={serie.name}
|
||||
className={cn(
|
||||
'relative py-3 px-2 flex flex-1 w-full gap-4 items-center even:bg-slate-50 rounded overflow-hidden',
|
||||
'[&_[role=progressbar]]:even:bg-white [&_[role=progressbar]]:shadow-sm',
|
||||
'relative flex w-full flex-1 items-center gap-4 overflow-hidden rounded px-2 py-3 even:bg-slate-50',
|
||||
'[&_[role=progressbar]]:shadow-sm [&_[role=progressbar]]:even:bg-white',
|
||||
isClickable && 'cursor-pointer hover:!bg-slate-100'
|
||||
)}
|
||||
{...(isClickable ? { onClick: () => onClick(serie) } : {})}
|
||||
>
|
||||
<div className="flex-1 break-all flex items-center gap-2 font-medium">
|
||||
<div className="flex flex-1 items-center gap-2 break-all font-medium">
|
||||
<SerieIcon name={serie.name} />
|
||||
{serie.name}
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex w-1/4 gap-4 items-center justify-end">
|
||||
<div className="flex w-1/4 flex-shrink-0 items-center justify-end gap-4">
|
||||
<PreviousDiffIndicatorText
|
||||
{...serie.metrics.previous[metric]}
|
||||
className="text-xs font-medium"
|
||||
|
||||
@@ -39,7 +39,7 @@ export function ReportChartTooltip({
|
||||
const hidden = sorted.slice(limit);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 rounded-xl border bg-white p-3 text-sm shadow-xl min-w-[180px]">
|
||||
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-white p-3 text-sm shadow-xl">
|
||||
{visible.map((item, index) => {
|
||||
// If we have a <Cell /> component, payload can be nested
|
||||
const payload = item.payload.payload ?? item.payload;
|
||||
@@ -62,7 +62,7 @@ export function ReportChartTooltip({
|
||||
className="w-[3px] rounded-full"
|
||||
style={{ background: data.color }}
|
||||
/>
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="flex flex-1 flex-col">
|
||||
<div className="min-w-0 max-w-[200px] overflow-hidden text-ellipsis whitespace-nowrap font-medium">
|
||||
{getLabel(data.label)}
|
||||
</div>
|
||||
|
||||
@@ -55,8 +55,8 @@ export function ReportTable({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-[200px_1fr] border border-border rounded-md overflow-hidden">
|
||||
<Table className="rounded-none border-t-0 border-l-0 border-b-0">
|
||||
<div className="grid grid-cols-[200px_1fr] overflow-hidden rounded-md border border-border">
|
||||
<Table className="rounded-none border-b-0 border-l-0 border-t-0">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
@@ -88,7 +88,7 @@ export function ReportTable({
|
||||
/>
|
||||
<Tooltip delayDuration={200}>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="min-w-0 overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
<div className="min-w-0 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{getLabel(serie.name)}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
@@ -157,8 +157,8 @@ export function ReportTable({
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col-reverse gap-4 md:flex-row md:justify-between md:items-center">
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
<div className="flex flex-col-reverse gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
<Badge>Total: {number.format(data.metrics.sum)}</Badge>
|
||||
<Badge>Average: {number.format(data.metrics.average)}</Badge>
|
||||
<Badge>Min: {number.format(data.metrics.min)}</Badge>
|
||||
|
||||
@@ -17,7 +17,7 @@ export function ResponsiveContainer({ children }: ResponsiveContainerProps) {
|
||||
maxHeight,
|
||||
minHeight,
|
||||
}}
|
||||
className={cn('max-sm:-mx-3 aspect-video w-full', editMode && 'card p-4')}
|
||||
className={cn('aspect-video w-full max-sm:-mx-3', editMode && 'card p-4')}
|
||||
>
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) =>
|
||||
|
||||
@@ -4,7 +4,7 @@ const createFlagIcon = (url: string) => {
|
||||
return function (_props: LucideProps) {
|
||||
return (
|
||||
<span
|
||||
className={`rounded-[2px] overflow-hidden !block !leading-[1rem] fi fi-${url}`}
|
||||
className={`fi !block overflow-hidden rounded-[2px] !leading-[1rem] fi-${url}`}
|
||||
></span>
|
||||
);
|
||||
} as LucideIcon;
|
||||
|
||||
@@ -29,7 +29,7 @@ function getProxyImage(url: string) {
|
||||
|
||||
const createImageIcon = (url: string) => {
|
||||
return function (_props: LucideProps) {
|
||||
return <img className="h-4 object-contain rounded-[2px]" src={url} />;
|
||||
return <img className="h-4 rounded-[2px] object-contain" src={url} />;
|
||||
} as LucideIcon;
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ export function SerieIcon({ name, ...props }: SerieIconProps) {
|
||||
}, [name]);
|
||||
|
||||
return Icon ? (
|
||||
<div className="h-4 flex-shrink-0 relative [&_a]:![&_a]:!h-4 [&_svg]:!rounded-[2px]">
|
||||
<div className="[&_a]:![&_a]:!h-4 relative h-4 flex-shrink-0 [&_svg]:!rounded-[2px]">
|
||||
<Icon size={16} {...props} />
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
@@ -94,7 +94,7 @@ export function FunnelSteps({
|
||||
return (
|
||||
<CarouselItem
|
||||
className={cn(
|
||||
'flex-[0_0_250px] max-w-full p-0 px-1',
|
||||
'max-w-full flex-[0_0_250px] p-0 px-1',
|
||||
editMode && 'flex-[0_0_320px]'
|
||||
)}
|
||||
key={step.event.id}
|
||||
@@ -106,13 +106,13 @@ export function FunnelSteps({
|
||||
{step.event.displayName || step.event.name}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="aspect-square relative">
|
||||
<div className="relative aspect-square">
|
||||
<FunnelChart from={step.prevPercent} to={step.percent} />
|
||||
<div className="absolute top-0 left-0 right-0 p-4 flex flex-col bg-white/40">
|
||||
<div className="uppercase font-medium text-muted-foreground">
|
||||
<div className="absolute left-0 right-0 top-0 flex flex-col bg-white/40 p-4">
|
||||
<div className="font-medium uppercase text-muted-foreground">
|
||||
Sessions
|
||||
</div>
|
||||
<div className="uppercase text-3xl font-bold flex items-center">
|
||||
<div className="flex items-center text-3xl font-bold uppercase">
|
||||
<span className="text-muted-foreground">
|
||||
{step.before}
|
||||
</span>
|
||||
@@ -130,34 +130,34 @@ export function FunnelSteps({
|
||||
</div>
|
||||
</div>
|
||||
{finalStep ? (
|
||||
<div className={cn('p-4 flex flex-col items-center')}>
|
||||
<div className="uppercase text-xs font-medium">
|
||||
<div className={cn('flex flex-col items-center p-4')}>
|
||||
<div className="text-xs font-medium uppercase">
|
||||
Conversion
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'uppercase text-3xl font-bold',
|
||||
'text-3xl font-bold uppercase',
|
||||
getDropoffColor(step.dropoff.percent)
|
||||
)}
|
||||
>
|
||||
{round(step.percent, 1)}%
|
||||
</div>
|
||||
<div className="uppercase text-sm mt-0 font-medium text-muted-foreground">
|
||||
<div className="mt-0 text-sm font-medium uppercase text-muted-foreground">
|
||||
Converted {step.current} of {totalSessions} sessions
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={cn('p-4 flex flex-col items-center')}>
|
||||
<div className="uppercase text-xs font-medium">Dropoff</div>
|
||||
<div className={cn('flex flex-col items-center p-4')}>
|
||||
<div className="text-xs font-medium uppercase">Dropoff</div>
|
||||
<div
|
||||
className={cn(
|
||||
'uppercase text-3xl font-bold',
|
||||
'text-3xl font-bold uppercase',
|
||||
getDropoffColor(step.dropoff.percent)
|
||||
)}
|
||||
>
|
||||
{round(step.dropoff.percent, 1)}%
|
||||
</div>
|
||||
<div className="uppercase text-sm mt-0 font-medium text-muted-foreground">
|
||||
<div className="mt-0 text-sm font-medium uppercase text-muted-foreground">
|
||||
Lost {step.dropoff.count} sessions
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,7 +51,7 @@ export function EventPropertiesCombobox({
|
||||
>
|
||||
<button
|
||||
className={cn(
|
||||
'flex items-center gap-1 rounded-md border border-border p-1 px-2 font-medium leading-none text-xs',
|
||||
'flex items-center gap-1 rounded-md border border-border p-1 px-2 text-xs font-medium leading-none',
|
||||
!event.property && 'border-destructive text-destructive'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -135,7 +135,7 @@ export function ReportEvents() {
|
||||
]}
|
||||
label="Segment"
|
||||
>
|
||||
<button className="flex items-center gap-1 rounded-md border border-border p-1 px-2 font-medium leading-none text-xs bg-white">
|
||||
<button className="flex items-center gap-1 rounded-md border border-border bg-white p-1 px-2 text-xs font-medium leading-none">
|
||||
{event.segment === 'user' ? (
|
||||
<>
|
||||
<Users size={12} /> Unique users
|
||||
@@ -203,7 +203,7 @@ export function ReportEvents() {
|
||||
/>
|
||||
</div>
|
||||
<label
|
||||
className="flex items-center gap-2 cursor-pointer select-none text-sm font-medium mt-4"
|
||||
className="mt-4 flex cursor-pointer select-none items-center gap-2 text-sm font-medium"
|
||||
htmlFor="previous"
|
||||
>
|
||||
<Checkbox
|
||||
|
||||
@@ -54,7 +54,7 @@ export function FiltersCombobox({ event }: FiltersComboboxProps) {
|
||||
);
|
||||
}}
|
||||
>
|
||||
<button className="flex items-center gap-1 rounded-md border border-border p-1 px-2 font-medium leading-none text-xs bg-white">
|
||||
<button className="flex items-center gap-1 rounded-md border border-border bg-white p-1 px-2 text-xs font-medium leading-none">
|
||||
<FilterIcon size={12} /> Add filter
|
||||
</button>
|
||||
</Combobox>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cva } from 'class-variance-authority';
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
|
||||
const alertVariants = cva(
|
||||
'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
|
||||
'relative w-full rounded-lg border p-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { cva } from 'class-variance-authority';
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border px-1.5 h-[20px] text-[10px] font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
'inline-flex h-[20px] items-center rounded-full border px-1.5 text-[10px] font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { LucideIcon } from 'lucide-react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'flex-shrink-0 inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background whitespace-nowrap 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 items-center justify-center whitespace-nowrap rounded-md text-sm 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',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -75,7 +75,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
{Icon && (
|
||||
<Icon
|
||||
className={cn(
|
||||
'h-4 w-4 mr-2 flex-shrink-0',
|
||||
'mr-2 h-4 w-4 flex-shrink-0',
|
||||
responsive && 'mr-0 sm:mr-2',
|
||||
loading && 'animate-spin'
|
||||
)}
|
||||
|
||||
@@ -30,7 +30,7 @@ const CheckboxInput = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>((props, ref) => (
|
||||
<label className="cursor-pointer flex items-center select-none border border-border rounded-md px-3 gap-4 min-h-10">
|
||||
<label className="flex min-h-10 cursor-pointer select-none items-center gap-4 rounded-md border border-border px-3">
|
||||
<Checkbox ref={ref} {...props} />
|
||||
<div className="text-sm font-medium">{props.children}</div>
|
||||
</label>
|
||||
|
||||
@@ -63,7 +63,7 @@ export function ComboboxAdvanced({
|
||||
: [...value, item.value]
|
||||
);
|
||||
}}
|
||||
className={'cursor-pointer flex items-center gap-2'}
|
||||
className={'flex cursor-pointer items-center gap-2'}
|
||||
>
|
||||
<Checkbox checked={checked} className="pointer-events-none" />
|
||||
{item?.label ?? item?.value}
|
||||
@@ -84,7 +84,7 @@ export function ComboboxAdvanced({
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
className={className}
|
||||
>
|
||||
<div className="flex gap-1 flex-wrap w-full">
|
||||
<div className="flex w-full flex-wrap gap-1">
|
||||
{value.length === 0 && placeholder}
|
||||
{value.map((value) => {
|
||||
const item = items.find((item) => item.value === value) ?? {
|
||||
|
||||
@@ -117,7 +117,7 @@ export function Combobox<T extends string>({
|
||||
) : (
|
||||
<CommandEmpty>Nothing selected</CommandEmpty>
|
||||
)}
|
||||
<div className="max-h-[300px] overflow-y-auto over-x-hidden">
|
||||
<div className="over-x-hidden max-h-[300px] overflow-y-auto">
|
||||
<CommandGroup>
|
||||
{items.map((item) => (
|
||||
<CommandItem
|
||||
|
||||
@@ -77,7 +77,7 @@ const DialogFooter = ({
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end gap-2',
|
||||
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -13,12 +13,12 @@ export function GradientBackground({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-gradient-to-tr from-slate-100 to-white rounded-md',
|
||||
'rounded-md bg-gradient-to-tr from-slate-100 to-white',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="p-4 flex flex-col gap-4">{children}</div>
|
||||
<div className="flex flex-col gap-4 p-4">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@ export function KeyValue({ href, onClick, name, value }: KeyValueProps) {
|
||||
return (
|
||||
<Component
|
||||
className={cn(
|
||||
'group overflow-hidden flex border border-border rounded-md text-xs divide-x font-medium self-start min-w-0 max-w-full transition-transform',
|
||||
'group flex min-w-0 max-w-full divide-x self-start overflow-hidden rounded-md border border-border text-xs font-medium transition-transform',
|
||||
clickable && 'hover:-translate-y-0.5'
|
||||
)}
|
||||
{...{ href, onClick }}
|
||||
>
|
||||
<div className="p-1 px-2 bg-black/5">{name}</div>
|
||||
<div className="bg-black/5 p-1 px-2">{name}</div>
|
||||
<div
|
||||
className={cn(
|
||||
'p-1 px-2 font-mono text-blue-700 bg-white whitespace-nowrap overflow-hidden text-ellipsis shadow-[inset_0_0_0_1px_#fff]',
|
||||
'overflow-hidden text-ellipsis whitespace-nowrap bg-white p-1 px-2 font-mono text-blue-700 shadow-[inset_0_0_0_1px_#fff]',
|
||||
clickable && 'group-hover:underline'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cva } from 'class-variance-authority';
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
|
||||
const labelVariants = cva(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 block mb-2'
|
||||
'mb-2 block text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
||||
);
|
||||
|
||||
const Label = React.forwardRef<
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as React from "react"
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||
import { Circle } from "lucide-react"
|
||||
|
||||
import { cn } from "@/utils/cn"
|
||||
import * as React from 'react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
||||
import { Circle } from 'lucide-react';
|
||||
|
||||
const RadioGroup = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||
@@ -10,13 +9,13 @@ const RadioGroup = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
className={cn("grid gap-2", className)}
|
||||
className={cn('grid gap-2', className)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
})
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
||||
);
|
||||
});
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
||||
|
||||
const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
@@ -26,7 +25,7 @@ const RadioGroupItem = React.forwardRef<
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
'aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -35,8 +34,8 @@ const RadioGroupItem = React.forwardRef<
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
})
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
||||
);
|
||||
});
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
||||
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
export { RadioGroup, RadioGroupItem };
|
||||
|
||||
@@ -31,16 +31,16 @@ const SheetOverlay = React.forwardRef<
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
'flex flex-col max-sm:w-[calc(100%-theme(spacing.8))] overflow-y-auto fixed z-50 gap-4 bg-background p-6 rounded-lg shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
|
||||
'fixed z-50 flex flex-col gap-4 overflow-y-auto rounded-lg bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out max-sm:w-[calc(100%-theme(spacing.8))]',
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: 'inset-x-4 top-4 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
|
||||
bottom:
|
||||
'inset-x-4 bottom-4 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
|
||||
left: 'top-4 bottom-4 left-4 w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
|
||||
left: 'bottom-4 left-4 top-4 w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
|
||||
right:
|
||||
'top-4 bottom-4 right-4 w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
|
||||
'bottom-4 right-4 top-4 w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -75,7 +75,7 @@ const TableHead = React.forwardRef<
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'px-4 h-10 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 shadow-[0_0_0_0.5px] shadow-border',
|
||||
'h-10 px-4 text-left align-middle font-medium text-muted-foreground shadow-[0_0_0_0.5px] shadow-border [&:has([role=checkbox])]:pr-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -90,7 +90,7 @@ const TableCell = React.forwardRef<
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'px-4 h-12 align-middle [&:has([role=checkbox])]:pr-0 shadow-[0_0_0_0.5px] shadow-border whitespace-nowrap',
|
||||
'h-12 whitespace-nowrap px-4 align-middle shadow-[0_0_0_0.5px] shadow-border [&:has([role=checkbox])]:pr-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props<T> {
|
||||
export function WidgetTable<T>({ columns, data, keyExtractor }: Props<T>) {
|
||||
return (
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-50 border-b border-border text-slate-500 [&_th]:font-medium text-sm [&_th]:p-4 [&_th]:py-2 [&_th]:text-left [&_th:last-child]:text-right [&_th]:whitespace-nowrap">
|
||||
<thead className="border-b border-border bg-slate-50 text-sm text-slate-500 [&_th:last-child]:text-right [&_th]:whitespace-nowrap [&_th]:p-4 [&_th]:py-2 [&_th]:text-left [&_th]:font-medium">
|
||||
<tr>
|
||||
{columns.map((column) => (
|
||||
<th key={column.name}>{column.name}</th>
|
||||
@@ -21,7 +21,7 @@ export function WidgetTable<T>({ columns, data, keyExtractor }: Props<T>) {
|
||||
{data.map((item) => (
|
||||
<tr
|
||||
key={keyExtractor(item)}
|
||||
className="text-sm border-b border-border last:border-0 [&_td]:p-4 [&_td:first-child]:text-left text-right"
|
||||
className="border-b border-border text-right text-sm last:border-0 [&_td:first-child]:text-left [&_td]:p-4"
|
||||
>
|
||||
{columns.map((column) => (
|
||||
<td key={column.name}>{column.render(item)}</td>
|
||||
|
||||
@@ -8,7 +8,7 @@ export function WidgetHead({ children, className }: WidgetHeadProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'p-4 border-b border-border [&_.title]:font-medium [&_.title]:whitespace-nowrap',
|
||||
'border-b border-border p-4 [&_.title]:whitespace-nowrap [&_.title]:font-medium',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -4,7 +4,12 @@ import { authMiddleware } from '@clerk/nextjs';
|
||||
// Please edit this to allow other routes to be public as needed.
|
||||
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
|
||||
export default authMiddleware({
|
||||
publicRoutes: ['/share/overview/:id', '/api/trpc(.*)', '/api/clerk/(.*)?'],
|
||||
publicRoutes: [
|
||||
'/share/overview/:id',
|
||||
'/api/trpc(.*)',
|
||||
'/api/clerk/(.*)?',
|
||||
'/monitoring',
|
||||
],
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -77,7 +77,7 @@ export default function AddClient() {
|
||||
<>
|
||||
<ModalHeader title="Success" text={'Your client is created'} />
|
||||
<CreateClientSuccess {...mutation.data} />
|
||||
<div className="flex gap-4 mt-4">
|
||||
<div className="mt-4 flex gap-4">
|
||||
<a
|
||||
className={cn(buttonVariants({ variant: 'secondary' }), 'flex-1')}
|
||||
href="https://docs.openpanel.dev/docs"
|
||||
@@ -150,7 +150,7 @@ export default function AddClient() {
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="other">
|
||||
<div className="p-2 px-3 bg-white rounded text-sm">
|
||||
<div className="rounded bg-white p-2 px-3 text-sm">
|
||||
🔑 You will get a secret to use for your API requests.
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
@@ -25,9 +25,9 @@ interface ModalHeaderProps {
|
||||
|
||||
export function ModalHeader({ title, text, onClose }: ModalHeaderProps) {
|
||||
return (
|
||||
<div className="flex justify-between mb-6">
|
||||
<div className="mb-6 flex justify-between">
|
||||
<div>
|
||||
<div className="font-medium mt-0.5">{title}</div>
|
||||
<div className="mt-0.5 font-medium">{title}</div>
|
||||
{!!text && <div className="text-sm text-muted-foreground">{text}</div>}
|
||||
</div>
|
||||
{onClose !== false && (
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function ShareOverviewModal() {
|
||||
render={({ field }) => (
|
||||
<label
|
||||
htmlFor="public"
|
||||
className="flex items-center gap-2 text-sm font-medium leading-none mb-4"
|
||||
className="mb-4 flex items-center gap-2 text-sm font-medium leading-none"
|
||||
>
|
||||
<Checkbox
|
||||
id="public"
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useOnClickOutside } from 'usehooks-ts';
|
||||
import type { ConfirmProps } from './Confirm';
|
||||
|
||||
const Loading = () => (
|
||||
<div className="fixed left-0 top-0 z-50 flex h-screen w-screen items-center justify-center overflow-auto bg-backdrop">
|
||||
<div className="bg-backdrop fixed left-0 top-0 z-50 flex h-screen w-screen items-center justify-center overflow-auto">
|
||||
<Loader className="mb-8 animate-spin" size={40} />
|
||||
</div>
|
||||
);
|
||||
@@ -162,7 +162,7 @@ export function ModalProvider() {
|
||||
return (
|
||||
<>
|
||||
{!!state.length && (
|
||||
<div className="fixed top-0 left-0 right-0 bottom-0 bg-[rgba(0,0,0,0.2)] z-50"></div>
|
||||
<div className="fixed bottom-0 left-0 right-0 top-0 z-50 bg-[rgba(0,0,0,0.2)]"></div>
|
||||
)}
|
||||
{state.map((item, index) => {
|
||||
const Modal = modals[item.name];
|
||||
|
||||
@@ -5,11 +5,11 @@ interface Props {
|
||||
export function BrandLogo({ src, isDark }: Props) {
|
||||
if (isDark) {
|
||||
return (
|
||||
<div className="w-9 h-9 p-1 rounded-full bg-white">
|
||||
<img className="w-full h-full object-contain" src={src} />
|
||||
<div className="h-9 w-9 rounded-full bg-white p-1">
|
||||
<img className="h-full w-full object-contain" src={src} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <img className="w-9 h-9 object-contain" src={src} />;
|
||||
return <img className="h-9 w-9 object-contain" src={src} />;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Layout({ children }: Props) {
|
||||
<>
|
||||
<Navbar darkText />
|
||||
<div
|
||||
className="opacity-50 w-full h-screen text-blue-950 bg-[radial-gradient(circle_at_2px_2px,#D9DEF6_2px,transparent_0)] absolute top-0 left-0 right-0 z-0"
|
||||
className="absolute left-0 right-0 top-0 z-0 h-screen w-full bg-[radial-gradient(circle_at_2px_2px,#D9DEF6_2px,transparent_0)] text-blue-950 opacity-50"
|
||||
style={{
|
||||
backgroundSize: '70px 70px',
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,7 @@ interface Props {
|
||||
|
||||
export function Lead({ children, className }: Props) {
|
||||
return (
|
||||
<p className={cn('text-xl md:text-2xl font-light', className)}>
|
||||
<p className={cn('text-xl font-light md:text-2xl', className)}>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
@@ -15,7 +15,7 @@ export function Lead({ children, className }: Props) {
|
||||
|
||||
export function Lead2({ children, className }: Props) {
|
||||
return (
|
||||
<p className={cn('text-lg md:text-xl font-light', className)}>{children}</p>
|
||||
<p className={cn('text-lg font-light md:text-xl', className)}>{children}</p>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function Heading1({ children, className }: Props) {
|
||||
return (
|
||||
<h1
|
||||
className={cn(
|
||||
'text-4xl md:text-5xl font-bold text-slate-900 !leading-tight font-serif',
|
||||
'font-serif text-4xl font-bold !leading-tight text-slate-900 md:text-5xl',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -40,7 +40,7 @@ export function Heading2({ children, className }: Props) {
|
||||
return (
|
||||
<h2
|
||||
className={cn(
|
||||
'text-4xl md:text-5xl font-bold text-slate-900 font-serif',
|
||||
'font-serif text-4xl font-bold text-slate-900 md:text-5xl',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -53,7 +53,7 @@ export function Heading3({ children, className }: Props) {
|
||||
return (
|
||||
<h3
|
||||
className={cn(
|
||||
'text-2xl md:text-3xl font-bold text-slate-900 font-serif',
|
||||
'font-serif text-2xl font-bold text-slate-900 md:text-3xl',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -66,7 +66,7 @@ export function Heading4({ children, className }: Props) {
|
||||
return (
|
||||
<h3
|
||||
className={cn(
|
||||
'text-xl md:text-2xl font-bold text-slate-900 font-serif',
|
||||
'font-serif text-xl font-bold text-slate-900 md:text-2xl',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -59,21 +59,21 @@ const features: FeatureItem[] = [
|
||||
Create beautiful charts and graphs to visualize your data and share
|
||||
them with your team.
|
||||
</p>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<div className="border border-border px-3 py-1 rounded">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="rounded border border-border px-3 py-1">
|
||||
✅ Linear
|
||||
</div>
|
||||
<div className="border border-border px-3 py-1 rounded">✅ Area</div>
|
||||
<div className="border border-border px-3 py-1 rounded">✅ Bar</div>
|
||||
<div className="border border-border px-3 py-1 rounded">✅ Map</div>
|
||||
<div className="border border-border px-3 py-1 rounded">✅ Pie</div>
|
||||
<div className="border border-border px-3 py-1 rounded">
|
||||
<div className="rounded border border-border px-3 py-1">✅ Area</div>
|
||||
<div className="rounded border border-border px-3 py-1">✅ Bar</div>
|
||||
<div className="rounded border border-border px-3 py-1">✅ Map</div>
|
||||
<div className="rounded border border-border px-3 py-1">✅ Pie</div>
|
||||
<div className="rounded border border-border px-3 py-1">
|
||||
✅ Funnels
|
||||
</div>
|
||||
<div className="border border-border px-3 py-1 rounded">
|
||||
<div className="rounded border border-border px-3 py-1">
|
||||
✅ Histogram
|
||||
</div>
|
||||
<div className="border border-border px-3 py-1 rounded">
|
||||
<div className="rounded border border-border px-3 py-1">
|
||||
✅ Metrics
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,14 +119,14 @@ export function Feature({
|
||||
even,
|
||||
}: FeatureItem & { even: boolean; children: React.ReactNode }) {
|
||||
return (
|
||||
<section className={cn('py-16 group', className)}>
|
||||
<section className={cn('group py-16', className)}>
|
||||
<div
|
||||
className={cn(
|
||||
'container flex min-h-[300px] items-center gap-16 justify-between max-md:flex-col-reverse',
|
||||
'container flex min-h-[300px] items-center justify-between gap-16 max-md:flex-col-reverse',
|
||||
!even && 'md:flex-row-reverse'
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="flex w-full flex-col">
|
||||
<Heading3 className="mb-2">{title}</Heading3>
|
||||
<div className="prose-xl">{children}</div>
|
||||
</div>
|
||||
@@ -136,7 +136,7 @@ export function Feature({
|
||||
alt={title}
|
||||
width={600}
|
||||
height={400}
|
||||
className="border-8 border-black/5 rounded-xl w-full max-w-xl group-hover:rotate-1 group-hover:scale-[101%] transition-transform duration-500"
|
||||
className="w-full max-w-xl rounded-xl border-8 border-black/5 transition-transform duration-500 group-hover:rotate-1 group-hover:scale-[101%]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,20 +7,20 @@ import { JoinWaitlist } from './join-waitlist';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="bg-blue-darker text-white relative mt-40 relative">
|
||||
<div className="inset-0 absolute h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.2)_0%,rgba(255,255,255,0)_100%)]"></div>
|
||||
<div className="relative container flex flex-col items-center text-center">
|
||||
<footer className="bg-blue-darker relative relative mt-40 text-white">
|
||||
<div className="absolute inset-0 h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.2)_0%,rgba(255,255,255,0)_100%)]"></div>
|
||||
<div className="container relative flex flex-col items-center text-center">
|
||||
<div className="my-24">
|
||||
<Heading2 className="text-white mb-2">Get early access</Heading2>
|
||||
<Heading2 className="mb-2 text-white">Get early access</Heading2>
|
||||
<Lead2>Ready to set your analytics free? Get on our waitlist.</Lead2>
|
||||
|
||||
<div className="mt-8">
|
||||
<JoinWaitlist className="text-white bg-white/20 border-white/30 focus:ring-white" />
|
||||
<JoinWaitlist className="border-white/30 bg-white/20 text-white focus:ring-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-xl">
|
||||
<div className="p-2 bg-white/20">
|
||||
<div className="bg-white/20 p-2">
|
||||
<Image
|
||||
src="/demo-2/1.png"
|
||||
width={1080}
|
||||
@@ -31,13 +31,13 @@ export default function Footer() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="-mt-8 relative z-10">
|
||||
<div className="relative z-10 -mt-8">
|
||||
<div className="h-px w-full bg-[radial-gradient(circle,rgba(255,255,255,0.7)_0%,rgba(255,255,255,0.7)_50%,rgba(255,255,255,0)_100%)]"></div>
|
||||
<div className="p-4 bg-blue-darker">
|
||||
<div className="bg-blue-darker p-4">
|
||||
<div className="container">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:justify-between md:items-center text-sm">
|
||||
<div className="flex flex-col gap-4 text-sm md:flex-row md:items-center md:justify-between">
|
||||
<Logo />
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="flex flex-col gap-4 md:flex-row">
|
||||
<Link className="hover:underline" href="/terms">
|
||||
Terms and Conditions
|
||||
</Link>
|
||||
|
||||
@@ -6,8 +6,8 @@ export function Hero() {
|
||||
return (
|
||||
<div className="relative overflow-hidden">
|
||||
{/* <div className="bg-blue-50 w-2/5 h-full absolute top-0 right-0"></div> */}
|
||||
<div className="container md:h-screen min-h-[700px] flex flex-col md:flex-row items-center relative max-md:pt-32 gap-4 md:gap-8">
|
||||
<div className="flex-1 lg:min-w-[400px] sm:min-w-[350px] max-md:text-center">
|
||||
<div className="container relative flex min-h-[700px] flex-col items-center gap-4 max-md:pt-32 md:h-screen md:flex-row md:gap-8">
|
||||
<div className="flex-1 max-md:text-center sm:min-w-[350px] lg:min-w-[400px]">
|
||||
<Heading1 className="mb-4 text-slate-950">
|
||||
An open-source
|
||||
<br />
|
||||
@@ -20,11 +20,11 @@ export function Hero() {
|
||||
<JoinWaitlistHero />
|
||||
<SocialProofServer className="mt-6" />
|
||||
</div>
|
||||
<div className="mt-16 md:pt-16 w-full">
|
||||
<div className="bg-black/5 md:p-2 rounded-2xl h-[max(90vh,650px)] flex">
|
||||
<div className="mt-16 w-full md:pt-16">
|
||||
<div className="flex h-[max(90vh,650px)] rounded-2xl bg-black/5 md:p-2">
|
||||
<iframe
|
||||
src="https://dashboard.openpanel.dev/share/overview/ZQsEhG"
|
||||
className="w-full h-full rounded-xl h-[max(90vh,650px)]"
|
||||
className="h-[max(90vh,650px)] h-full w-full rounded-xl"
|
||||
title="Openpanel Dashboard"
|
||||
scrolling="no"
|
||||
/>
|
||||
|
||||
@@ -65,7 +65,7 @@ export function JoinWaitlistHero({ className }: JoinWaitlistProps) {
|
||||
<input
|
||||
placeholder="Enter your email"
|
||||
className={cn(
|
||||
'border border-slate-100 rounded-md shadow-sm bg-white h-12 w-full px-4 outline-none focus:ring-1 ring-black text-blue-darker',
|
||||
'text-blue-darker h-12 w-full rounded-md border border-slate-100 bg-white px-4 shadow-sm outline-none ring-black focus:ring-1',
|
||||
className
|
||||
)}
|
||||
value={value}
|
||||
|
||||
@@ -65,7 +65,7 @@ export function JoinWaitlist({ className }: JoinWaitlistProps) {
|
||||
<input
|
||||
placeholder="Enter your email"
|
||||
className={cn(
|
||||
'border border-slate-100 rounded-md shadow-sm bg-white h-12 w-full px-4 outline-none focus:ring-1 ring-black text-blue-darker',
|
||||
'text-blue-darker h-12 w-full rounded-md border border-slate-100 bg-white px-4 shadow-sm outline-none ring-black focus:ring-1',
|
||||
className
|
||||
)}
|
||||
value={value}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user