give dark mode some love 🖤

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-05-31 22:25:07 +02:00
parent ff31cc506c
commit 1665924073
77 changed files with 256 additions and 199 deletions

View File

@@ -1,6 +1,7 @@
'use client';
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
import { FullPageEmptyState } from '@/components/full-page-empty-state';
import { useOverviewOptions } from '@/components/overview/useOverviewOptions';
import { LazyChart } from '@/components/report/chart/LazyChart';
import { Button } from '@/components/ui/button';
@@ -14,7 +15,13 @@ import {
import { useAppParams } from '@/hooks/useAppParams';
import { api, handleError } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { ChevronRight, MoreHorizontal, PlusIcon, Trash } from 'lucide-react';
import {
ChevronRight,
LayoutPanelTopIcon,
MoreHorizontal,
PlusIcon,
Trash,
} from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { toast } from 'sonner';
@@ -138,6 +145,26 @@ export function ListReports({ reports }: ListReportsProps) {
</div>
);
})}
{reports.length === 0 && (
<FullPageEmptyState title="No reports" icon={LayoutPanelTopIcon}>
<p>You can visualize your data with a report</p>
<Button
onClick={() =>
router.push(
`/${params.organizationSlug}/${
params.projectId
}/reports?${new URLSearchParams({
dashboardId: params.dashboardId,
}).toString()}`
)
}
className="mt-14"
icon={PlusIcon}
>
Create report
</Button>
</FullPageEmptyState>
)}
</div>
</>
);

View File

@@ -96,7 +96,7 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
</div>
<div
className={cn(
'mt-2 grid gap-4',
'mt-4 grid gap-4',
'grid-cols-2 @xs:grid-cols-3 @lg:grid-cols-4'
)}
>
@@ -114,19 +114,19 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
return (
<div
className="flex flex-col items-center justify-center rounded-xl bg-slate-50 p-4"
className="bg-def-200 flex flex-col rounded-xl p-4"
key={report.id}
>
<Icon size={24} className="text-blue-600" />
<div className="mt-2 w-full overflow-hidden text-ellipsis whitespace-nowrap text-center text-xs">
<Icon size={24} className="text-highlight" />
<div className="mt-2 w-full overflow-hidden text-ellipsis whitespace-nowrap text-xs">
{report.name}
</div>
</div>
);
})}
{item.reports.length > 6 && (
<div className="flex flex-col items-center justify-center rounded-xl bg-slate-50 p-4">
<PlusIcon size={24} className="text-blue-600" />
<div className="bg-def-200 flex flex-col rounded-xl p-4">
<PlusIcon size={24} className="text-highlight" />
<div className="mt-2 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap text-xs">
{item.reports.length - 5} more
</div>

View File

@@ -28,7 +28,7 @@ export function EventConversionsList({ data }: EventListProps) {
{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="flex h-8 items-center gap-2 rounded border border-slate-300 bg-slate-100 px-3 text-sm font-medium leading-none">
<div className="bg-def-200 border-def-200 flex h-8 items-center gap-2 rounded border px-3 text-sm font-medium leading-none">
{item.createdAt.toLocaleDateString()}
</div>
</div>

View File

@@ -106,7 +106,7 @@ export function EventEdit({ event, open, setOpen }: Props) {
setIcon(name);
}}
className={cn(
'inline-flex h-8 w-8 flex-shrink-0 cursor-pointer items-center justify-center rounded-md bg-slate-100 transition-all',
'bg-def-200 inline-flex h-8 w-8 flex-shrink-0 cursor-pointer items-center justify-center rounded-md transition-all',
name === selectedIcon
? 'scale-110 ring-1 ring-black'
: '[&_svg]:opacity-50'

View File

@@ -63,7 +63,7 @@ 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="flex h-8 items-center gap-2 rounded border border-slate-300 bg-slate-100 px-3 text-sm font-medium leading-none">
<div className="bg-def-200 border-def-200 flex h-8 items-center gap-2 rounded border px-3 text-sm font-medium leading-none">
{item.createdAt.toLocaleDateString()}
</div>
{index === 0 && (

View File

@@ -38,7 +38,7 @@ export default function EventListener() {
setCounter(0);
router.refresh();
}}
className="flex h-8 items-center gap-2 rounded border border-border bg-background px-3 text-sm font-medium leading-none"
className="flex h-8 items-center gap-2 rounded border border-border bg-card px-3 text-sm font-medium leading-none"
>
<div className="relative">
<div

View File

@@ -0,0 +1,41 @@
# setup caprover
## Firewall
ufw allow 22,80,443,3000,996,7946,4789,2377/tcp; ufw allow 22,7946,4789,2377/udp;
## Install docker
```
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# Install Docker
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify
sudo docker run hello-world
```
## Install caprover
`docker run -p 80:80 -p 443:443 -p 3000:3000 -e ACCEPTED_TERMS=true -v /var/run/docker.sock:/var/run/docker.sock -v /captain:/captain caprover/caprover`
## Point domain to server
`*.apps.example.com -> server ip`
## Setup caprover
caprover serversetup

View File

@@ -42,8 +42,8 @@ function LinkWithIcon({
return (
<Link
className={cn(
'text-text flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-all transition-colors hover:bg-slate-100',
active && 'bg-slate-100',
'text-text hover:bg-def-200 flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-all transition-colors',
active && 'bg-def-200',
className
)}
href={href}

View File

@@ -46,7 +46,7 @@ export function LayoutSidebar({
/>
<div
className={cn(
'fixed left-0 top-0 z-50 flex h-screen w-72 flex-col border-r border-border bg-background transition-transform',
'fixed left-0 top-0 z-50 flex h-screen w-72 flex-col border-r border-border bg-card transition-transform',
'-translate-x-72 lg:-translate-x-0', // responsive
active && 'translate-x-0' // force active on mobile
)}
@@ -65,8 +65,8 @@ export function LayoutSidebar({
<div className="block h-32 shrink-0"></div>
</div>
<div className="fixed bottom-0 left-0 right-0">
<div className="h-8 w-full bg-gradient-to-t from-background to-background/0"></div>
<div className="flex flex-col gap-2 bg-background p-4 pt-0">
<div className="h-8 w-full bg-gradient-to-t from-card to-card/0"></div>
<div className="flex flex-col gap-2 bg-card p-4 pt-0">
<Link
className={cn('flex gap-2', buttonVariants())}
href={`/${organizationSlug}/${projectId}/reports`}

View File

@@ -12,7 +12,7 @@ export function StickyBelowHeader({
return (
<div
className={cn(
'top-0 z-20 border-b border-border bg-background md:sticky [[id=dashboard]_&]:top-16 [[id=dashboard]_&]:rounded-none',
'top-0 z-20 border-b border-border bg-card md:sticky [[id=dashboard]_&]:top-16 [[id=dashboard]_&]:rounded-none',
className
)}
>

View File

@@ -15,7 +15,7 @@ async function PageLayout({ title, organizationSlug }: PageLayoutProps) {
return (
<>
<div className="sticky top-0 z-20 flex h-16 flex-shrink-0 items-center justify-between border-b border-border bg-background px-4 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-card px-4 pl-12 lg:pl-4">
<div className="text-xl font-medium">{title}</div>
<div className="flex gap-2">
<div>
@@ -30,7 +30,7 @@ async function PageLayout({ title, organizationSlug }: PageLayoutProps) {
const Loading = ({ title }: PageLayoutProps) => (
<>
<div className="sticky top-0 z-20 flex h-16 flex-shrink-0 items-center justify-between border-b border-border bg-background px-4 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-card px-4 pl-12 lg:pl-4">
<div className="text-xl font-medium">{title}</div>
</div>
</>

View File

@@ -18,7 +18,7 @@ const MostEvents = ({ data }: Props) => {
{data.slice(0, 5).map((item) => (
<div key={item.name} className="relative px-3 py-2">
<div
className="absolute bottom-0 left-0 top-0 rounded bg-slate-100"
className="bg-def-200 absolute bottom-0 left-0 top-0 rounded"
style={{
width: `${(item.count / max) * 100}%`,
}}

View File

@@ -18,7 +18,7 @@ const PopularRoutes = ({ data }: Props) => {
{data.slice(0, 5).map((item) => (
<div key={item.path} className="relative px-3 py-2">
<div
className="absolute bottom-0 left-0 top-0 rounded bg-slate-100"
className="bg-def-200 absolute bottom-0 left-0 top-0 rounded"
style={{
width: `${(item.count / max) * 100}%`,
}}

View File

@@ -70,7 +70,7 @@ const ProfileActivity = ({ data }: Props) => {
key={date.toISOString()}
className={cn(
'aspect-square w-full rounded',
hit ? 'bg-blue-600' : 'bg-slate-100'
hit ? 'bg-highlight' : 'bg-def-200'
)}
></div>
);
@@ -94,7 +94,7 @@ const ProfileActivity = ({ data }: Props) => {
key={date.toISOString()}
className={cn(
'aspect-square w-full rounded',
hit ? 'bg-blue-600' : 'bg-slate-100'
hit ? 'bg-highlight' : 'bg-def-200'
)}
></div>
);

View File

@@ -37,7 +37,7 @@ export default async function ProfileLastSeenServer({ projectId }: Props) {
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn('aspect-square w-full shrink-0 rounded bg-blue-600')}
className={cn('bg-highlight aspect-square w-full shrink-0 rounded')}
style={{
opacity: calculateRatio(item.count),
}}

View File

@@ -5,6 +5,7 @@ import { useFullscreen } from '@/components/fullscreen-toggle';
import { Tooltiper } from '@/components/ui/tooltip';
import { cn } from '@/utils/cn';
import { bind } from 'bind-event-listener';
import { useTheme } from 'next-themes';
import {
ComposableMap,
Geographies,
@@ -91,6 +92,9 @@ const Map = ({ markers }: Props) => {
return size * multiplier;
};
const theme = useTheme();
console.log(theme.theme);
return (
<div
className={cn(
@@ -119,8 +123,8 @@ const Map = ({ markers }: Props) => {
<Geography
key={geo.rsmKey}
geography={geo}
fill="#EAEAEC"
stroke="black"
fill={theme.theme === 'dark' ? '#0f0f0f' : '#F0F4F9'}
stroke={theme.theme === 'dark' ? '#262626' : '#DDE3E9'}
pointerEvents={'none'}
/>
))
@@ -149,13 +153,17 @@ const Map = ({ markers }: Props) => {
<Marker coordinates={coordinates}>
<circle
r={size}
fill="#2463EB"
fill={theme.theme === 'dark' ? '#3d79ff' : '#2266ec'}
className="animate-ping opacity-20"
/>
</Marker>
<Tooltiper asChild content={`${marker.count} visitors`}>
<Marker coordinates={coordinates}>
<circle r={size} fill="#2463EB" fillOpacity={0.5} />
<circle
r={size}
fill={theme.theme === 'dark' ? '#3d79ff' : '#2266ec'}
fillOpacity={0.5}
/>
</Marker>
</Tooltiper>
</Fragment>

View File

@@ -29,7 +29,7 @@ export default function Page({
<RealtimeMap projectId={projectId} />
</Suspense>
<div className="relative z-10 grid min-h-[calc(100vh-theme(spacing.16))] items-start gap-4 overflow-hidden p-8 md:grid-cols-3">
<div className="card bg-background/80 p-4">
<div className="card bg-card/80 p-4">
<RealtimeLiveHistogram projectId={projectId} />
</div>
<div className="col-span-2">

View File

@@ -85,7 +85,7 @@ export function RealtimeLiveHistogram({
{staticArray.map((percent, i) => (
<div
key={i}
className="flex-1 animate-pulse rounded-md bg-slate-200 dark:bg-slate-800"
className="bg-def-200 flex-1 animate-pulse rounded-md"
style={{ height: `${percent}%` }}
/>
))}
@@ -106,7 +106,7 @@ export function RealtimeLiveHistogram({
<div
className={cn(
'flex-1 rounded-md transition-all ease-in-out hover:scale-110',
minute.count === 0 ? 'bg-slate-200' : 'bg-blue-600'
minute.count === 0 ? 'bg-def-200' : 'bg-highlight'
)}
style={{
height:

View File

@@ -23,7 +23,7 @@ function Tooltip(props: any) {
return null;
}
return (
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-background p-3 text-sm shadow-xl">
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-card p-3 text-sm shadow-xl">
<div>
<div className="text-xs text-muted-foreground">
Days since last seen

View File

@@ -29,7 +29,7 @@ function Tooltip(props: any) {
return null;
}
return (
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-background p-3 text-sm shadow-xl">
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-card p-3 text-sm shadow-xl">
<div className="text-xs text-muted-foreground">{payload.date}</div>
<div>
<div className="text-xs text-muted-foreground">

View File

@@ -33,7 +33,7 @@ function Tooltip({ payload }: any) {
return null;
}
return (
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-background p-3 text-sm shadow-xl">
<div className="flex min-w-[180px] flex-col gap-2 rounded-xl border bg-card p-3 text-sm shadow-xl">
<div className="flex justify-between gap-8">
<div>{formatDate(new Date(date))}</div>
</div>

View File

@@ -15,7 +15,7 @@ const Cell = ({ value, ratio }: { value: number; ratio: number }) => {
className={cn('relative h-8 border', ratio !== 0 && 'border-background')}
>
<div
className="absolute inset-0 z-0 bg-blue-600"
className="bg-highlight absolute inset-0 z-0"
style={{ opacity: ratio }}
></div>
<div className="relative z-10">{value}</div>
@@ -76,7 +76,7 @@ const WeeklyCohortsServer = async ({ projectId }: Props) => {
<tbody>
{res.map((row) => (
<tr key={row.first_seen}>
<td className="bg-slate-50 text-sm font-medium text-slate-500">
<td className="bg-def-100 text-def-1000 text-sm font-medium">
{row.first_seen}
</td>
<Cell

View File

@@ -7,9 +7,9 @@ type Props = {
const Page = ({ children }: Props) => {
return (
<>
<div className="bg-slate-100">
<div className="bg-def-200">
<div className="grid h-full md:grid-cols-[min(400px,40vw)_1fr]">
<div className="min-h-screen border-r border-r-background bg-gradient-to-r from-background to-slate-200 max-md:hidden">
<div className="to-def-200 min-h-screen border-r border-r-background bg-gradient-to-r from-background max-md:hidden">
<LiveEventsServer />
</div>
<div className="min-h-screen">{children}</div>

View File

@@ -12,15 +12,15 @@ const Page = ({ children }: Props) => {
<>
<div className="fixed inset-0 hidden md:grid md:grid-cols-[30vw_1fr] lg:grid-cols-[30vw_1fr]">
<div className=""></div>
<div className="border-l border-border bg-background"></div>
<div className="border-l border-border bg-card"></div>
</div>
<div className="relative min-h-screen bg-background md:bg-transparent">
<div className="relative min-h-screen bg-card md:bg-transparent">
<FullWidthNavbar>
<SkipOnboarding />
</FullWidthNavbar>
<div className="mx-auto w-full md:max-w-[95vw] lg:max-w-[80vw]">
<div className="grid md:grid-cols-[25vw_1fr] lg:grid-cols-[20vw_1fr]">
<div className="max-w-screen flex flex-col gap-4 overflow-hidden bg-slate-100 p-4 pr-0 md:bg-transparent md:py-14">
<div className="max-w-screen bg-def-200 flex flex-col gap-4 overflow-hidden p-4 pr-0 md:bg-transparent md:py-14">
<div>
<div className="text-xs font-bold uppercase text-[#7b94ac]">
Welcome to Openpanel

View File

@@ -30,7 +30,7 @@ const ConnectApp = ({ client }: Props) => {
className="flex items-center gap-4 rounded-md border p-2 py-2 text-left"
key={framework.name}
>
<div className="h-10 w-10 rounded-md bg-slate-200 p-2">
<div className="bg-def-200 h-10 w-10 rounded-md p-2">
<img
className="h-full w-full object-contain"
src={framework.logo}

View File

@@ -30,7 +30,7 @@ const ConnectBackend = ({ client }: Props) => {
className="flex items-center gap-4 rounded-md border p-2 py-2 text-left"
key={framework.name}
>
<div className="h-10 w-10 rounded-md bg-slate-200 p-2">
<div className="bg-def-200 h-10 w-10 rounded-md p-2">
<img
className="h-full w-full object-contain"
src={framework.logo}

View File

@@ -30,7 +30,7 @@ const ConnectWeb = ({ client }: Props) => {
className="flex items-center gap-4 rounded-md border p-2 py-2 text-left"
key={framework.name}
>
<div className="h-10 w-10 rounded-md bg-slate-200 p-2">
<div className="bg-def-200 h-10 w-10 rounded-md p-2">
<img
className="h-full w-full object-contain"
src={framework.logo}

View File

@@ -36,7 +36,7 @@ const Connect = ({ project }: Props) => {
</OnboardingDescription>
}
>
<div className="flex flex-col gap-4 rounded-xl border bg-slate-100 p-4 md:p-6">
<div className="bg-def-200 flex flex-col gap-4 rounded-xl border p-4 md:p-6">
<div className="flex items-center gap-2 text-2xl capitalize">
<LockIcon />
Credentials

View File

@@ -54,7 +54,7 @@ const VerifyListener = ({ client, events: _events, onVerified }: Props) => {
}
return (
<Loader2 size={40} className="shrink-0 animate-spin text-blue-600" />
<Loader2 size={40} className="text-highlight shrink-0 animate-spin" />
);
};
@@ -70,7 +70,7 @@ const VerifyListener = ({ client, events: _events, onVerified }: Props) => {
<div
className={cn(
'my-6 flex gap-6 rounded-xl p-4 md:p-6',
isConnected ? 'bg-emerald-100' : 'bg-blue-100'
isConnected ? 'bg-emerald-100' : 'bg-highlight'
)}
>
{renderIcon()}

View File

@@ -59,9 +59,9 @@ const Steps = ({ className }: Props) => {
const currentIndex = steps.findIndex((i) => i.status === 'current');
return (
<div className="relative">
<div className="absolute bottom-4 left-4 top-4 w-px bg-slate-300"></div>
<div className="bg-def-200 absolute bottom-4 left-4 top-4 w-px"></div>
<div
className="absolute left-4 top-4 w-px bg-blue-600"
className="bg-highlight absolute left-4 top-4 w-px"
style={{
height: `calc(${((currentIndex + 1) / steps.length) * 100}% - 3.5rem)`,
}}
@@ -77,7 +77,7 @@ const Steps = ({ className }: Props) => {
className={cn(
'flex flex-shrink-0 items-center gap-2 self-start px-3 py-1.5',
step.status === 'current' &&
'rounded-xl border border-border bg-background',
'rounded-xl border border-border bg-card',
step.status === 'completed' &&
index !== currentIndex - 1 &&
'max-md:hidden'
@@ -87,18 +87,16 @@ const Steps = ({ className }: Props) => {
<div
className={cn(
'relative flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm text-white'
// step.status === 'completed' && 'bg-blue-600 ring-blue-500/50',
// step.status === 'pending' && 'bg-slate-400 ring-slate-500/50'
)}
>
<div
className={cn(
'absolute inset-0 z-0 rounded-full bg-blue-600',
step.status === 'pending' && 'bg-slate-400'
'bg-highlight absolute inset-0 z-0 rounded-full',
step.status === 'pending' && 'bg-def-400'
)}
></div>
{step.status === 'current' && (
<div className="absolute inset-1 z-0 animate-ping-slow rounded-full bg-blue-600"></div>
<div className="bg-highlight absolute inset-1 z-0 animate-ping-slow rounded-full"></div>
)}
<div className="relative">
{step.status === 'completed' && <CheckCheckIcon size={14} />}

View File

@@ -25,7 +25,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<body
className={cn('grainy min-h-screen bg-secondary font-sans antialiased')}
className={cn('grainy bg-def-100 min-h-screen font-sans antialiased')}
>
<NextTopLoader
showSpinner={false}