feat: add tracking code on project settings

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-02-27 23:27:06 +01:00
parent 2501ee1eef
commit 1272466235
6 changed files with 163 additions and 27 deletions

View File

@@ -1,7 +1,7 @@
import { formatDateTime, formatTime } from '@/utils/date'; import { DropdownMenuSeparator } from '@radix-ui/react-dropdown-menu';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import type { ColumnDef } from '@tanstack/react-table'; import type { ColumnDef } from '@tanstack/react-table';
import { isToday } from 'date-fns'; import { toast } from 'sonner';
import { ColumnCreatedAt } from '@/components/column-created-at'; import { ColumnCreatedAt } from '@/components/column-created-at';
import CopyInput from '@/components/forms/copy-input'; import CopyInput from '@/components/forms/copy-input';
import { createActionColumn } from '@/components/ui/data-table/data-table-helpers'; import { createActionColumn } from '@/components/ui/data-table/data-table-helpers';
@@ -10,9 +10,6 @@ import { handleError, useTRPC } from '@/integrations/trpc/react';
import { pushModal, showConfirm } from '@/modals'; import { pushModal, showConfirm } from '@/modals';
import type { RouterOutputs } from '@/trpc/client'; import type { RouterOutputs } from '@/trpc/client';
import { clipboard } from '@/utils/clipboard'; import { clipboard } from '@/utils/clipboard';
import { DropdownMenuSeparator } from '@radix-ui/react-dropdown-menu';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
export function useColumns() { export function useColumns() {
const columns: ColumnDef<RouterOutputs['client']['list'][number]>[] = [ const columns: ColumnDef<RouterOutputs['client']['list'][number]>[] = [
@@ -51,7 +48,7 @@ export function useColumns() {
queryClient.invalidateQueries(trpc.client.list.pathFilter()); queryClient.invalidateQueries(trpc.client.list.pathFilter());
}, },
onError: handleError, onError: handleError,
}), })
); );
return ( return (
<> <>
@@ -67,7 +64,6 @@ export function useColumns() {
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem <DropdownMenuItem
variant="destructive"
onClick={() => { onClick={() => {
showConfirm({ showConfirm({
title: 'Revoke client', title: 'Revoke client',
@@ -79,6 +75,7 @@ export function useColumns() {
}, },
}); });
}} }}
variant="destructive"
> >
Revoke Revoke
</DropdownMenuItem> </DropdownMenuItem>

View File

@@ -1,17 +1,16 @@
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { PlusIcon } from 'lucide-react';
import { useColumns } from './columns';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { DataTable } from '@/components/ui/data-table/data-table'; import { DataTable } from '@/components/ui/data-table/data-table';
import { DataTableToolbar } from '@/components/ui/data-table/data-table-toolbar'; import { DataTableToolbar } from '@/components/ui/data-table/data-table-toolbar';
import { useTable } from '@/components/ui/data-table/use-table'; import { useTable } from '@/components/ui/data-table/use-table';
import { pushModal } from '@/modals'; import { pushModal } from '@/modals';
import type { RouterOutputs } from '@/trpc/client'; import type { RouterOutputs } from '@/trpc/client';
import { PlusIcon } from 'lucide-react';
import { useColumns } from './columns';
type Props = { interface Props {
query: UseQueryResult<RouterOutputs['client']['list'], unknown>; query: UseQueryResult<RouterOutputs['client']['list'], unknown>;
}; }
export const ClientsTable = ({ query }: Props) => { export const ClientsTable = ({ query }: Props) => {
const columns = useColumns(); const columns = useColumns();
@@ -30,13 +29,13 @@ export const ClientsTable = ({ query }: Props) => {
<DataTableToolbar table={table}> <DataTableToolbar table={table}>
<Button <Button
icon={PlusIcon} icon={PlusIcon}
responsive
onClick={() => pushModal('AddClient')} onClick={() => pushModal('AddClient')}
responsive
> >
Create client Create client
</Button> </Button>
</DataTableToolbar> </DataTableToolbar>
<DataTable table={table} loading={isLoading} /> <DataTable loading={isLoading} table={table} />
</> </>
); );
}; };

View File

@@ -27,7 +27,7 @@ const ConnectWeb = ({ client }: Props) => {
</script> </script>
<script src="https://openpanel.dev/op1.js" defer async></script>`; <script src="https://openpanel.dev/op1.js" defer async></script>`;
return ( return (
<> <div className="col gap-4">
<div className="col gap-2"> <div className="col gap-2">
<div className="row items-center justify-between gap-4"> <div className="row items-center justify-between gap-4">
<div className="flex items-center gap-2 font-bold text-xl capitalize"> <div className="flex items-center gap-2 font-bold text-xl capitalize">
@@ -44,7 +44,7 @@ const ConnectWeb = ({ client }: Props) => {
</Button> </Button>
</div> </div>
</div> </div>
<Syntax className="border" code={code} /> <Syntax className="border" code={code} copyable={false} />
</div> </div>
<div className="col gap-4"> <div className="col gap-4">
<p className="text-center text-muted-foreground text-sm"> <p className="text-center text-muted-foreground text-sm">
@@ -80,7 +80,7 @@ const ConnectWeb = ({ client }: Props) => {
</a> </a>
</p> </p>
</div> </div>
</> </div>
); );
}; };

View File

@@ -69,6 +69,7 @@ import { Route as AppOrganizationIdProjectIdProfilesTabsIndexRouteImport } from
import { Route as AppOrganizationIdProjectIdNotificationsTabsIndexRouteImport } from './routes/_app.$organizationId.$projectId.notifications._tabs.index' import { Route as AppOrganizationIdProjectIdNotificationsTabsIndexRouteImport } from './routes/_app.$organizationId.$projectId.notifications._tabs.index'
import { Route as AppOrganizationIdProjectIdEventsTabsIndexRouteImport } from './routes/_app.$organizationId.$projectId.events._tabs.index' import { Route as AppOrganizationIdProjectIdEventsTabsIndexRouteImport } from './routes/_app.$organizationId.$projectId.events._tabs.index'
import { Route as AppOrganizationIdProjectIdSettingsTabsWidgetsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.widgets' import { Route as AppOrganizationIdProjectIdSettingsTabsWidgetsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.widgets'
import { Route as AppOrganizationIdProjectIdSettingsTabsTrackingRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.tracking'
import { Route as AppOrganizationIdProjectIdSettingsTabsImportsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.imports' import { Route as AppOrganizationIdProjectIdSettingsTabsImportsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.imports'
import { Route as AppOrganizationIdProjectIdSettingsTabsEventsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.events' import { Route as AppOrganizationIdProjectIdSettingsTabsEventsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.events'
import { Route as AppOrganizationIdProjectIdSettingsTabsDetailsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.details' import { Route as AppOrganizationIdProjectIdSettingsTabsDetailsRouteImport } from './routes/_app.$organizationId.$projectId.settings._tabs.details'
@@ -475,6 +476,12 @@ const AppOrganizationIdProjectIdSettingsTabsWidgetsRoute =
path: '/widgets', path: '/widgets',
getParentRoute: () => AppOrganizationIdProjectIdSettingsTabsRoute, getParentRoute: () => AppOrganizationIdProjectIdSettingsTabsRoute,
} as any) } as any)
const AppOrganizationIdProjectIdSettingsTabsTrackingRoute =
AppOrganizationIdProjectIdSettingsTabsTrackingRouteImport.update({
id: '/tracking',
path: '/tracking',
getParentRoute: () => AppOrganizationIdProjectIdSettingsTabsRoute,
} as any)
const AppOrganizationIdProjectIdSettingsTabsImportsRoute = const AppOrganizationIdProjectIdSettingsTabsImportsRoute =
AppOrganizationIdProjectIdSettingsTabsImportsRouteImport.update({ AppOrganizationIdProjectIdSettingsTabsImportsRouteImport.update({
id: '/imports', id: '/imports',
@@ -634,6 +641,7 @@ export interface FileRoutesByFullPath {
'/$organizationId/$projectId/settings/details': typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute '/$organizationId/$projectId/settings/details': typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute
'/$organizationId/$projectId/settings/events': typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute '/$organizationId/$projectId/settings/events': typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute
'/$organizationId/$projectId/settings/imports': typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute '/$organizationId/$projectId/settings/imports': typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute
'/$organizationId/$projectId/settings/tracking': typeof AppOrganizationIdProjectIdSettingsTabsTrackingRoute
'/$organizationId/$projectId/settings/widgets': typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute '/$organizationId/$projectId/settings/widgets': typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute
'/$organizationId/$projectId/events/': typeof AppOrganizationIdProjectIdEventsTabsIndexRoute '/$organizationId/$projectId/events/': typeof AppOrganizationIdProjectIdEventsTabsIndexRoute
'/$organizationId/$projectId/notifications/': typeof AppOrganizationIdProjectIdNotificationsTabsIndexRoute '/$organizationId/$projectId/notifications/': typeof AppOrganizationIdProjectIdNotificationsTabsIndexRoute
@@ -701,6 +709,7 @@ export interface FileRoutesByTo {
'/$organizationId/$projectId/settings/details': typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute '/$organizationId/$projectId/settings/details': typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute
'/$organizationId/$projectId/settings/events': typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute '/$organizationId/$projectId/settings/events': typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute
'/$organizationId/$projectId/settings/imports': typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute '/$organizationId/$projectId/settings/imports': typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute
'/$organizationId/$projectId/settings/tracking': typeof AppOrganizationIdProjectIdSettingsTabsTrackingRoute
'/$organizationId/$projectId/settings/widgets': typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute '/$organizationId/$projectId/settings/widgets': typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute
'/$organizationId/$projectId/profiles/$profileId/events': typeof AppOrganizationIdProjectIdProfilesProfileIdTabsEventsRoute '/$organizationId/$projectId/profiles/$profileId/events': typeof AppOrganizationIdProjectIdProfilesProfileIdTabsEventsRoute
'/$organizationId/$projectId/profiles/$profileId/sessions': typeof AppOrganizationIdProjectIdProfilesProfileIdTabsSessionsRoute '/$organizationId/$projectId/profiles/$profileId/sessions': typeof AppOrganizationIdProjectIdProfilesProfileIdTabsSessionsRoute
@@ -781,6 +790,7 @@ export interface FileRoutesById {
'/_app/$organizationId/$projectId/settings/_tabs/details': typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute '/_app/$organizationId/$projectId/settings/_tabs/details': typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute
'/_app/$organizationId/$projectId/settings/_tabs/events': typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute '/_app/$organizationId/$projectId/settings/_tabs/events': typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute
'/_app/$organizationId/$projectId/settings/_tabs/imports': typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute '/_app/$organizationId/$projectId/settings/_tabs/imports': typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute
'/_app/$organizationId/$projectId/settings/_tabs/tracking': typeof AppOrganizationIdProjectIdSettingsTabsTrackingRoute
'/_app/$organizationId/$projectId/settings/_tabs/widgets': typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute '/_app/$organizationId/$projectId/settings/_tabs/widgets': typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute
'/_app/$organizationId/$projectId/events/_tabs/': typeof AppOrganizationIdProjectIdEventsTabsIndexRoute '/_app/$organizationId/$projectId/events/_tabs/': typeof AppOrganizationIdProjectIdEventsTabsIndexRoute
'/_app/$organizationId/$projectId/notifications/_tabs/': typeof AppOrganizationIdProjectIdNotificationsTabsIndexRoute '/_app/$organizationId/$projectId/notifications/_tabs/': typeof AppOrganizationIdProjectIdNotificationsTabsIndexRoute
@@ -855,6 +865,7 @@ export interface FileRouteTypes {
| '/$organizationId/$projectId/settings/details' | '/$organizationId/$projectId/settings/details'
| '/$organizationId/$projectId/settings/events' | '/$organizationId/$projectId/settings/events'
| '/$organizationId/$projectId/settings/imports' | '/$organizationId/$projectId/settings/imports'
| '/$organizationId/$projectId/settings/tracking'
| '/$organizationId/$projectId/settings/widgets' | '/$organizationId/$projectId/settings/widgets'
| '/$organizationId/$projectId/events/' | '/$organizationId/$projectId/events/'
| '/$organizationId/$projectId/notifications/' | '/$organizationId/$projectId/notifications/'
@@ -922,6 +933,7 @@ export interface FileRouteTypes {
| '/$organizationId/$projectId/settings/details' | '/$organizationId/$projectId/settings/details'
| '/$organizationId/$projectId/settings/events' | '/$organizationId/$projectId/settings/events'
| '/$organizationId/$projectId/settings/imports' | '/$organizationId/$projectId/settings/imports'
| '/$organizationId/$projectId/settings/tracking'
| '/$organizationId/$projectId/settings/widgets' | '/$organizationId/$projectId/settings/widgets'
| '/$organizationId/$projectId/profiles/$profileId/events' | '/$organizationId/$projectId/profiles/$profileId/events'
| '/$organizationId/$projectId/profiles/$profileId/sessions' | '/$organizationId/$projectId/profiles/$profileId/sessions'
@@ -1001,6 +1013,7 @@ export interface FileRouteTypes {
| '/_app/$organizationId/$projectId/settings/_tabs/details' | '/_app/$organizationId/$projectId/settings/_tabs/details'
| '/_app/$organizationId/$projectId/settings/_tabs/events' | '/_app/$organizationId/$projectId/settings/_tabs/events'
| '/_app/$organizationId/$projectId/settings/_tabs/imports' | '/_app/$organizationId/$projectId/settings/_tabs/imports'
| '/_app/$organizationId/$projectId/settings/_tabs/tracking'
| '/_app/$organizationId/$projectId/settings/_tabs/widgets' | '/_app/$organizationId/$projectId/settings/_tabs/widgets'
| '/_app/$organizationId/$projectId/events/_tabs/' | '/_app/$organizationId/$projectId/events/_tabs/'
| '/_app/$organizationId/$projectId/notifications/_tabs/' | '/_app/$organizationId/$projectId/notifications/_tabs/'
@@ -1493,6 +1506,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRouteImport preLoaderRoute: typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRouteImport
parentRoute: typeof AppOrganizationIdProjectIdSettingsTabsRoute parentRoute: typeof AppOrganizationIdProjectIdSettingsTabsRoute
} }
'/_app/$organizationId/$projectId/settings/_tabs/tracking': {
id: '/_app/$organizationId/$projectId/settings/_tabs/tracking'
path: '/tracking'
fullPath: '/$organizationId/$projectId/settings/tracking'
preLoaderRoute: typeof AppOrganizationIdProjectIdSettingsTabsTrackingRouteImport
parentRoute: typeof AppOrganizationIdProjectIdSettingsTabsRoute
}
'/_app/$organizationId/$projectId/settings/_tabs/imports': { '/_app/$organizationId/$projectId/settings/_tabs/imports': {
id: '/_app/$organizationId/$projectId/settings/_tabs/imports' id: '/_app/$organizationId/$projectId/settings/_tabs/imports'
path: '/imports' path: '/imports'
@@ -1766,6 +1786,7 @@ interface AppOrganizationIdProjectIdSettingsTabsRouteChildren {
AppOrganizationIdProjectIdSettingsTabsDetailsRoute: typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute AppOrganizationIdProjectIdSettingsTabsDetailsRoute: typeof AppOrganizationIdProjectIdSettingsTabsDetailsRoute
AppOrganizationIdProjectIdSettingsTabsEventsRoute: typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute AppOrganizationIdProjectIdSettingsTabsEventsRoute: typeof AppOrganizationIdProjectIdSettingsTabsEventsRoute
AppOrganizationIdProjectIdSettingsTabsImportsRoute: typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute AppOrganizationIdProjectIdSettingsTabsImportsRoute: typeof AppOrganizationIdProjectIdSettingsTabsImportsRoute
AppOrganizationIdProjectIdSettingsTabsTrackingRoute: typeof AppOrganizationIdProjectIdSettingsTabsTrackingRoute
AppOrganizationIdProjectIdSettingsTabsWidgetsRoute: typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute AppOrganizationIdProjectIdSettingsTabsWidgetsRoute: typeof AppOrganizationIdProjectIdSettingsTabsWidgetsRoute
AppOrganizationIdProjectIdSettingsTabsIndexRoute: typeof AppOrganizationIdProjectIdSettingsTabsIndexRoute AppOrganizationIdProjectIdSettingsTabsIndexRoute: typeof AppOrganizationIdProjectIdSettingsTabsIndexRoute
} }
@@ -1780,6 +1801,8 @@ const AppOrganizationIdProjectIdSettingsTabsRouteChildren: AppOrganizationIdProj
AppOrganizationIdProjectIdSettingsTabsEventsRoute, AppOrganizationIdProjectIdSettingsTabsEventsRoute,
AppOrganizationIdProjectIdSettingsTabsImportsRoute: AppOrganizationIdProjectIdSettingsTabsImportsRoute:
AppOrganizationIdProjectIdSettingsTabsImportsRoute, AppOrganizationIdProjectIdSettingsTabsImportsRoute,
AppOrganizationIdProjectIdSettingsTabsTrackingRoute:
AppOrganizationIdProjectIdSettingsTabsTrackingRoute,
AppOrganizationIdProjectIdSettingsTabsWidgetsRoute: AppOrganizationIdProjectIdSettingsTabsWidgetsRoute:
AppOrganizationIdProjectIdSettingsTabsWidgetsRoute, AppOrganizationIdProjectIdSettingsTabsWidgetsRoute,
AppOrganizationIdProjectIdSettingsTabsIndexRoute: AppOrganizationIdProjectIdSettingsTabsIndexRoute:

View File

@@ -0,0 +1,116 @@
import type { IServiceClient } from '@openpanel/db';
import { frameworks } from '@openpanel/sdk-info';
import { useQuery } from '@tanstack/react-query';
import { createFileRoute } from '@tanstack/react-router';
import { CopyIcon } from 'lucide-react';
import { useEffect, useState } from 'react';
import Syntax from '@/components/syntax';
import { Button } from '@/components/ui/button';
import { Combobox } from '@/components/ui/combobox';
import { useAppContext } from '@/hooks/use-app-context';
import { useAppParams } from '@/hooks/use-app-params';
import { useTRPC } from '@/integrations/trpc/react';
import { pushModal } from '@/modals';
import { clipboard } from '@/utils/clipboard';
export const Route = createFileRoute(
'/_app/$organizationId/$projectId/settings/_tabs/tracking'
)({
component: Component,
});
function Component() {
const { projectId } = useAppParams();
const trpc = useTRPC();
const query = useQuery(trpc.client.list.queryOptions({ projectId }));
return <ConnectWeb clients={query.data ?? []} />;
}
interface Props {
clients: IServiceClient[];
}
const ConnectWeb = ({ clients }: Props) => {
const [client, setClient] = useState<IServiceClient | null>(null);
useEffect(() => {
if (!client && clients && clients.length > 0) {
setClient(clients[0]);
}
}, [clients]);
const context = useAppContext();
const code = `<script>
window.op=window.op||function(){var n=[];return new Proxy(function(){arguments.length&&n.push([].slice.call(arguments))},{get:function(t,r){return"q"===r?n:function(){n.push([r].concat([].slice.call(arguments)))}} ,has:function(t,r){return"q"===r}}) }();
window.op('init', {${context.isSelfHosted ? `\n\tapiUrl: '${context.apiUrl}',` : ''}
clientId: '${client?.id ?? 'YOUR_CLIENT_ID'}',
trackScreenViews: true,
trackOutgoingLinks: true,
trackAttributes: true,
// sessionReplay: {
// enabled: true,
// },
});
</script>
<script src="https://openpanel.dev/op1.js" defer async></script>`;
return (
<div className="col gap-4">
<div className="col gap-2">
<div className="row items-center justify-between gap-4">
<Combobox
items={clients.map((c) => ({
value: c.id,
label: c.name,
}))}
onChange={(id) =>
setClient(clients.find((c) => c.id === id) ?? null)
}
placeholder="Select client"
searchable
value={client?.id ?? null}
/>
<Button
icon={CopyIcon}
onClick={() => clipboard(code, null)}
variant="outline"
>
Copy
</Button>
</div>
<Syntax className="border" code={code} copyable={false} />
</div>
<div className="col gap-4">
<p className="text-center text-muted-foreground text-sm">
Or pick a framework below to get started.
</p>
<div className="grid gap-4 md:grid-cols-2">
{frameworks.map((framework) => (
<button
className="flex items-center gap-4 rounded-md border p-2 text-left"
key={framework.name}
onClick={() =>
pushModal('Instructions', {
framework,
client,
})
}
type="button"
>
<div className="h-10 w-10 rounded-md bg-def-200 p-2">
<framework.IconComponent className="h-full w-full" />
</div>
<div className="flex-1 font-semibold">{framework.name}</div>
</button>
))}
</div>
<p className="text-center text-muted-foreground text-sm">
Missing a framework?{' '}
<a
className="text-foreground underline"
href="mailto:hello@openpanel.dev"
>
Let us know!
</a>
</p>
</div>
</div>
);
};

View File

@@ -1,16 +1,16 @@
import FullPageLoadingState from '@/components/full-page-loading-state';
import { PageHeader } from '@/components/page-header';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { PAGE_TITLES, createProjectTitle } from '@/utils/title';
import { import {
Outlet,
createFileRoute, createFileRoute,
Outlet,
useLocation, useLocation,
useRouter, useRouter,
} from '@tanstack/react-router'; } from '@tanstack/react-router';
import FullPageLoadingState from '@/components/full-page-loading-state';
import { PageHeader } from '@/components/page-header';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { createProjectTitle, PAGE_TITLES } from '@/utils/title';
export const Route = createFileRoute( export const Route = createFileRoute(
'/_app/$organizationId/$projectId/settings/_tabs', '/_app/$organizationId/$projectId/settings/_tabs'
)({ )({
component: ProjectDashboard, component: ProjectDashboard,
head: () => { head: () => {
@@ -27,7 +27,7 @@ export const Route = createFileRoute(
await queryClient.prefetchQuery( await queryClient.prefetchQuery(
trpc.project.getProjectWithClients.queryOptions({ trpc.project.getProjectWithClients.queryOptions({
projectId: params.projectId, projectId: params.projectId,
}), })
); );
}, },
pendingComponent: FullPageLoadingState, pendingComponent: FullPageLoadingState,
@@ -42,6 +42,7 @@ function ProjectDashboard() {
{ id: 'details', label: 'Details' }, { id: 'details', label: 'Details' },
{ id: 'events', label: 'Events' }, { id: 'events', label: 'Events' },
{ id: 'clients', label: 'Clients' }, { id: 'clients', label: 'Clients' },
{ id: 'tracking', label: 'Tracking script' },
{ id: 'widgets', label: 'Widgets' }, { id: 'widgets', label: 'Widgets' },
{ id: 'imports', label: 'Imports' }, { id: 'imports', label: 'Imports' },
]; ];
@@ -56,11 +57,11 @@ function ProjectDashboard() {
return ( return (
<div className="container p-8"> <div className="container p-8">
<PageHeader <PageHeader
title="Project settings"
description="Manage your project settings here" description="Manage your project settings here"
title="Project settings"
/> />
<Tabs value={tab} onValueChange={handleTabChange} className="mt-2 mb-8"> <Tabs className="mt-2 mb-8" onValueChange={handleTabChange} value={tab}>
<TabsList> <TabsList>
{settingsTabs.map((tab) => ( {settingsTabs.map((tab) => (
<TabsTrigger key={tab.id} value={tab.id}> <TabsTrigger key={tab.id} value={tab.id}>