From ad3132478a3fccec018e54b126bbba1fa37bf1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Thu, 5 Sep 2024 21:14:42 +0200 Subject: [PATCH] feat(dashboard): edit events --- .../events/event-list/event-edit.tsx | 176 ----------------- .../events/event-list/setup-server.md | 41 ---- .../[projectId]/events/events.tsx | 3 +- .../[organizationSlug]/[projectId]/page.tsx | 2 +- .../realtime-live-events/live-events.tsx | 2 +- .../app/(auth)/live-events/live-events.tsx | 2 +- .../events}/event-icon.tsx | 0 .../events}/event-list-item.tsx | 0 .../events}/event-listener.tsx | 0 .../src/components/events/table/columns.tsx | 23 ++- apps/dashboard/src/components/ui/dialog.tsx | 4 +- apps/dashboard/src/components/ui/label.tsx | 2 +- apps/dashboard/src/modals/Modal/Container.tsx | 43 ++-- apps/dashboard/src/modals/edit-event.tsx | 187 ++++++++++++++++++ apps/dashboard/src/modals/index.tsx | 3 + packages/trpc/src/routers/chart.ts | 1 - packages/trpc/src/routers/event.ts | 5 +- 17 files changed, 248 insertions(+), 246 deletions(-) delete mode 100644 apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-edit.tsx delete mode 100644 apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/setup-server.md rename apps/dashboard/src/{app/(app)/[organizationSlug]/[projectId]/events/event-list => components/events}/event-icon.tsx (100%) rename apps/dashboard/src/{app/(app)/[organizationSlug]/[projectId]/events/event-list => components/events}/event-list-item.tsx (100%) rename apps/dashboard/src/{app/(app)/[organizationSlug]/[projectId]/events/event-list => components/events}/event-listener.tsx (100%) create mode 100644 apps/dashboard/src/modals/edit-event.tsx diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-edit.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-edit.tsx deleted file mode 100644 index 1d687e2a..00000000 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-edit.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import type { Dispatch, SetStateAction } from 'react'; -import { useEffect, useState } from 'react'; -import { Button } from '@/components/ui/button'; -import { Checkbox } from '@/components/ui/checkbox'; -import { Label } from '@/components/ui/label'; -import { - Sheet, - SheetContent, - SheetFooter, - SheetHeader, - SheetTitle, -} from '@/components/ui/sheet'; -import { api } from '@/trpc/client'; -import { cn } from '@/utils/cn'; -import { useRouter } from 'next/navigation'; -import { toast } from 'sonner'; - -import type { IServiceEvent } from '@openpanel/db'; - -import { - EventIconColors, - EventIconMapper, - EventIconRecords, -} from './event-icon'; - -interface Props { - event: IServiceEvent; - open: boolean; - setOpen: Dispatch>; -} - -export function EventEdit({ event, open, setOpen }: Props) { - const router = useRouter(); - - const { name, meta, projectId } = event; - - const [selectedIcon, setIcon] = useState( - meta?.icon ?? - EventIconRecords[name]?.icon ?? - EventIconRecords.default?.icon ?? - '' - ); - const [selectedColor, setColor] = useState( - meta?.color ?? - EventIconRecords[name]?.color ?? - EventIconRecords.default?.color ?? - '' - ); - const [conversion, setConversion] = useState(!!meta?.conversion); - - useEffect(() => { - if (meta?.icon) { - setIcon(meta.icon); - } - }, [meta?.icon]); - useEffect(() => { - if (meta?.color) { - setColor(meta.color); - } - }, [meta?.color]); - useEffect(() => { - setConversion(meta?.conversion ?? false); - }, [meta?.conversion]); - - const SelectedIcon = EventIconMapper[selectedIcon]!; - - const mutation = api.event.updateEventMeta.useMutation({ - onSuccess() { - setOpen(false); - toast('Event updated'); - router.refresh(); - }, - }); - const getBg = (color: string) => `bg-${color}-200`; - const getText = (color: string) => `text-${color}-700`; - - return ( - - - - Edit "{name}" - -
-
- - -
-
- -
- {Object.entries(EventIconMapper).map(([name, Icon]) => ( - - ))} -
-
-
- -
- {EventIconColors.map((color) => ( - - ))} -
-
-
- - - - -
-
- ); -} diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/setup-server.md b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/setup-server.md deleted file mode 100644 index c1e75dac..00000000 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/setup-server.md +++ /dev/null @@ -1,41 +0,0 @@ -# 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 diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/events.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/events.tsx index f28ddfe7..4285b6fb 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/events.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/events.tsx @@ -1,6 +1,7 @@ 'use client'; import { TableButtons } from '@/components/data-table'; +import EventListener from '@/components/events/event-listener'; import { EventsTable } from '@/components/events/table'; import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons'; import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer'; @@ -12,8 +13,6 @@ import { api } from '@/trpc/client'; import { Loader2Icon } from 'lucide-react'; import { parseAsInteger, useQueryState } from 'nuqs'; -import EventListener from './event-list/event-listener'; - type Props = { projectId: string; profileId?: string; diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/page.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/page.tsx index ad1f6a1f..acbf27b1 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/page.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/page.tsx @@ -1,6 +1,7 @@ import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons'; import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer'; import ServerLiveCounter from '@/components/overview/live-counter'; +import OverviewMetrics from '@/components/overview/overview-metrics'; import OverviewShareServer from '@/components/overview/overview-share'; import OverviewTopDevices from '@/components/overview/overview-top-devices'; import OverviewTopEvents from '@/components/overview/overview-top-events'; @@ -8,7 +9,6 @@ import OverviewTopGeo from '@/components/overview/overview-top-geo'; import OverviewTopPages from '@/components/overview/overview-top-pages'; import OverviewTopSources from '@/components/overview/overview-top-sources'; -import OverviewMetrics from '../../../../components/overview/overview-metrics'; import { OverviewReportRange } from './overview-sticky-header'; interface PageProps { diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/realtime/realtime-live-events/live-events.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/realtime/realtime-live-events/live-events.tsx index d4a63185..b570748c 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/realtime/realtime-live-events/live-events.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/realtime/realtime-live-events/live-events.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState } from 'react'; -import { EventListItem } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-list-item'; +import { EventListItem } from '@/components/events/event-list-item'; import useWS from '@/hooks/useWS'; import { AnimatePresence, motion } from 'framer-motion'; diff --git a/apps/dashboard/src/app/(auth)/live-events/live-events.tsx b/apps/dashboard/src/app/(auth)/live-events/live-events.tsx index 798910c6..f1ca4fb5 100644 --- a/apps/dashboard/src/app/(auth)/live-events/live-events.tsx +++ b/apps/dashboard/src/app/(auth)/live-events/live-events.tsx @@ -1,7 +1,7 @@ 'use client'; import { useEffect, useState } from 'react'; -import { EventListItem } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-list-item'; +import { EventListItem } from '@/components/events/event-list-item'; import { AnimatePresence, motion } from 'framer-motion'; import type { IServiceEventMinimal } from '@openpanel/db'; diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-icon.tsx b/apps/dashboard/src/components/events/event-icon.tsx similarity index 100% rename from apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-icon.tsx rename to apps/dashboard/src/components/events/event-icon.tsx diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-list-item.tsx b/apps/dashboard/src/components/events/event-list-item.tsx similarity index 100% rename from apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-list-item.tsx rename to apps/dashboard/src/components/events/event-list-item.tsx diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-listener.tsx b/apps/dashboard/src/components/events/event-listener.tsx similarity index 100% rename from apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-listener.tsx rename to apps/dashboard/src/components/events/event-listener.tsx diff --git a/apps/dashboard/src/components/events/table/columns.tsx b/apps/dashboard/src/components/events/table/columns.tsx index 0f36da45..b32ace32 100644 --- a/apps/dashboard/src/components/events/table/columns.tsx +++ b/apps/dashboard/src/components/events/table/columns.tsx @@ -1,4 +1,4 @@ -import { EventIcon } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-icon'; +import { EventIcon } from '@/components/events/event-icon'; import { ProjectLink } from '@/components/links'; import { SerieIcon } from '@/components/report/chart/SerieIcon'; import { TooltipComplete } from '@/components/tooltip-complete'; @@ -50,11 +50,22 @@ export function useColumns() { return (
- + + + +
")`, + backgroundSize: '100% 100%', + backgroundRepeat: 'repeat', + }} + > +
+
+
+
{title}
+ {!!text && ( +
{text}
+ )} +
+ {onClose !== false && ( + + )} +
); } diff --git a/apps/dashboard/src/modals/edit-event.tsx b/apps/dashboard/src/modals/edit-event.tsx new file mode 100644 index 00000000..7a6461fc --- /dev/null +++ b/apps/dashboard/src/modals/edit-event.tsx @@ -0,0 +1,187 @@ +import { useEffect, useState } from 'react'; +import { + EventIconColors, + EventIconMapper, + EventIconRecords, +} from '@/components/events/event-icon'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Label } from '@/components/ui/label'; +import { useAppParams } from '@/hooks/useAppParams'; +import { api } from '@/trpc/client'; +import { cn } from '@/utils/cn'; +import { useQueryClient } from '@tanstack/react-query'; +import { getQueryKey } from '@trpc/react-query'; +import { AnimatePresence, motion } from 'framer-motion'; +import { UndoIcon } from 'lucide-react'; +import { toast } from 'sonner'; + +import { popModal } from '.'; +import { ModalContent, ModalHeader } from './Modal/Container'; + +interface Props { + id: string; +} + +export default function EditEvent({ id }: Props) { + const { projectId } = useAppParams(); + const client = useQueryClient(); + + const { data: event } = api.event.byId.useQuery({ id, projectId }); + + const [selectedIcon, setIcon] = useState(EventIconRecords.default!.icon); + const [selectedColor, setColor] = useState(EventIconRecords.default!.color); + const [conversion, setConversion] = useState(false); + const [step, setStep] = useState<'icon' | 'color'>('icon'); + useEffect(() => { + if (event?.meta?.icon) { + setIcon(event.meta.icon); + } + if (event?.meta?.color) { + setColor(event.meta.color); + } + if (event?.meta?.conversion) { + setConversion(event.meta.conversion); + } + }, [event]); + + const SelectedIcon = EventIconMapper[selectedIcon]!; + + const mutation = api.event.updateEventMeta.useMutation({ + onSuccess() { + toast('Event updated'); + client.refetchQueries({ + queryKey: getQueryKey(api.event.events), + }); + popModal(); + }, + }); + const getBg = (color: string) => `bg-${color}-200`; + const getText = (color: string) => `text-${color}-700`; + const iconGrid = 'grid grid-cols-10 gap-4'; + return ( + + popModal()} + /> +
+
+ + +
+ + {step === 'icon' ? ( + + +
+ {Object.entries(EventIconMapper).map(([name, Icon]) => ( + + ))} +
+
+ ) : ( + +
+
Pick a color
+ +
+
+ {EventIconColors.map((color) => ( + + ))} +
+
+ )} +
+ + +
+
+ ); +} diff --git a/apps/dashboard/src/modals/index.tsx b/apps/dashboard/src/modals/index.tsx index 197e38cc..32f8abd4 100644 --- a/apps/dashboard/src/modals/index.tsx +++ b/apps/dashboard/src/modals/index.tsx @@ -14,6 +14,9 @@ const Loading = () => ( ); const modals = { + EditEvent: dynamic(() => import('./edit-event'), { + loading: Loading, + }), EventDetails: dynamic(() => import('./event-details'), { loading: Loading, }), diff --git a/packages/trpc/src/routers/chart.ts b/packages/trpc/src/routers/chart.ts index 15398908..85981e12 100644 --- a/packages/trpc/src/routers/chart.ts +++ b/packages/trpc/src/routers/chart.ts @@ -7,7 +7,6 @@ import { createSqlBuilder, db, formatClickhouseDate, - getPropertyKey, getSelectPropertyKey, TABLE_NAMES, toDate, diff --git a/packages/trpc/src/routers/event.ts b/packages/trpc/src/routers/event.ts index 28a30040..6f39910c 100644 --- a/packages/trpc/src/routers/event.ts +++ b/packages/trpc/src/routers/event.ts @@ -52,7 +52,10 @@ export const eventRouter = createTRPCRouter({ ) .query(async ({ input: { id, projectId } }) => { const res = await getEvents( - `SELECT * FROM ${TABLE_NAMES.events} WHERE id = ${escape(id)} AND project_id = ${escape(projectId)};` + `SELECT * FROM ${TABLE_NAMES.events} WHERE id = ${escape(id)} AND project_id = ${escape(projectId)};`, + { + meta: true, + } ); if (!res?.[0]) {