feature(public,docs): new public website and docs

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-11-13 21:15:46 +01:00
parent fc2a019e1d
commit a022cb4831
234 changed files with 9341 additions and 6154 deletions

View File

@@ -1,103 +0,0 @@
'use client';
import { ListPropertiesIcon } from '@/components/events/list-properties-icon';
import { Pagination } from '@/components/pagination';
import { ProfileAvatar } from '@/components/profiles/profile-avatar';
import { DialogContent } from '@/components/ui/dialog';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Tooltiper } from '@/components/ui/tooltip';
import { WidgetTable } from '@/components/widget-table';
import { useAppParams } from '@/hooks/useAppParams';
import { api } from '@/trpc/client';
import { getProfileName } from '@/utils/getters';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useEffect, useRef, useState } from 'react';
import type { IChartInput } from '@openpanel/validation';
import { popModal } from '.';
import { ModalHeader } from './Modal/Container';
interface Props extends IChartInput {
step: number;
}
function usePrevious(value: any) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
export default function FunnelStepDetails(props: Props) {
const [data] = api.chart.funnelStep.useSuspenseQuery(props);
const pathname = usePathname();
const prev = usePrevious(pathname);
const { organizationSlug, projectId } = useAppParams();
const [page, setPage] = useState(0);
useEffect(() => {
if (prev && prev !== pathname) {
popModal();
}
}, [pathname]);
return (
<DialogContent className="p-0">
<div className="p-4">
<ModalHeader title="Profiles" />
<Pagination
count={data.length}
take={50}
cursor={page}
setCursor={setPage}
/>
</div>
<ScrollArea className="max-h-[60vh]">
<WidgetTable
data={data.slice(page * 50, page * 50 + 50)}
keyExtractor={(item) => item.id}
columns={[
{
name: 'Name',
render(profile) {
return (
<Link
href={`/${organizationSlug}/${projectId}/profiles/${profile.id}`}
className="flex items-center gap-2 font-medium"
>
<ProfileAvatar size="sm" {...profile} />
{getProfileName(profile)}
</Link>
);
},
},
{
name: '',
render(profile) {
return <ListPropertiesIcon {...profile.properties} />;
},
},
{
name: 'Last seen',
render(profile) {
return (
<Tooltiper
asChild
content={profile.createdAt.toLocaleString()}
>
<div className=" text-muted-foreground">
{profile.createdAt.toLocaleTimeString()}
</div>
</Tooltiper>
);
},
},
]}
/>
</ScrollArea>
</DialogContent>
);
}

View File

@@ -16,10 +16,7 @@ import { popModal } from '.';
type Props = {
client: IServiceClient | null;
framework:
| (typeof frameworks.website)[number]
| (typeof frameworks.app)[number]
| (typeof frameworks.backend)[number];
framework: (typeof frameworks)[number];
};
const Header = ({ framework }: Pick<Props, 'framework'>) => (
@@ -29,7 +26,7 @@ const Header = ({ framework }: Pick<Props, 'framework'>) => (
);
const Footer = ({ framework }: Pick<Props, 'framework'>) => (
<SheetFooter>
<SheetFooter className="absolute bottom-0 left-0 right-0 p-4">
<Button
variant={'secondary'}
className="flex-1"
@@ -49,350 +46,21 @@ const Footer = ({ framework }: Pick<Props, 'framework'>) => (
</SheetFooter>
);
const Instructions = ({ framework, client }: Props) => {
const { name } = framework;
const clientId = client?.id || 'REPLACE_WITH_YOUR_CLIENT';
const clientSecret = client?.secret || 'REPLACE_WITH_YOUR_SECRET';
if (
name === 'HTML / Script' ||
name === 'React' ||
name === 'Astro' ||
name === 'Remix' ||
name === 'Vue'
) {
return (
<div className="flex flex-col gap-4">
<p>Copy the code below and insert it to you website</p>
<Syntax
code={`<script>
window.op = window.op||function(...args){(window.op.q=window.op.q||[]).push(args);};
window.op('init', {
clientId: '${clientId}',
trackScreenViews: true,
trackOutgoingLinks: true,
trackAttributes: true,
});
</script>
<script src="https://openpanel.dev/op1.js" defer async></script>`}
/>
<Alert>
<AlertDescription>
We have already added your client id to the snippet.
</AlertDescription>
</Alert>
</div>
);
}
if (name === 'Next.js') {
return (
<div className="flex flex-col gap-4">
<p>Install dependencies</p>
<Syntax code={'pnpm install @openpanel/nextjs'} />
<p>Add OpenPanelComponent to your root layout</p>
<Syntax
code={`import { OpenPanelComponent } from '@openpanel/nextjs';
export default RootLayout({ children }) {
return (
<>
<OpenPanelComponent
clientId="${clientId}"
trackScreenViews={true}
trackAttributes={true}
trackOutgoingLinks={true}
// If you have a user id, you can pass it here to identify the user
// profileId={'123'}
/>
{children}
</>
)
}`}
/>
<p>
This will track regular page views and outgoing links. You can also
track custom events.
</p>
<Syntax
code={`import { useOpenPanel } from '@openpanel/nextjs';
// Sends an event with payload foo: bar
useOpenPanel().track('my_event', { foo: 'bar' });
`}
/>
</div>
);
}
if (name === 'Laravel') {
return (
<div className="flex flex-col gap-4">
<p>Install dependencies</p>
<Syntax code={'composer require bleckert/openpanel-laravel'} />
<p>Add environment variables</p>
<Syntax
code={`OPENPANEL_CLIENT_ID=${clientId}
OPENPANEL_CLIENT_SECRET=${clientSecret}`}
/>
<p>Usage</p>
<Syntax
code={`use Bleckert\\OpenpanelLaravel\\Openpanel;
$openpanel = app(Openpanel::class);
// Identify user
$openpanel->setProfileId(1);
// Update user profile
$openpanel->setProfile(
id: 1,
firstName: 'John Doe',
// ...
);
// Track event
$openpanel->event(
name: 'User registered',
);
`}
/>
<Alert>
<AlertTitle>Shoutout!</AlertTitle>
<AlertDescription>
Huge shoutout to{' '}
<a
href="https://twitter.com/tbleckert"
target="_blank"
className="underline"
rel="noreferrer"
>
@tbleckert
</a>{' '}
for creating this package.
</AlertDescription>
</Alert>
</div>
);
}
if (name === 'Rest API') {
return (
<div className="flex flex-col gap-4">
<strong>Authentication</strong>
<p>You will need to pass your client ID and secret via headers.</p>
<strong>Usage</strong>
<p>Create a custom event called &quot;my_event&quot;.</p>
<Syntax
code={`curl '${process.env.NEXT_PUBLIC_API_URL}/track' \\
-H 'content-type: application/json' \\
-H 'openpanel-client-id: ${clientId}' \\
-H 'openpanel-client-secret: ${clientSecret}' \\
--data-raw '{
"type": "track",
"payload": {
"name": "my_event",
"properties": {
"foo": "bar"
}
}
}'`}
/>
<p>The payload should be a JSON object with the following fields:</p>
<ul className="list-inside list-disc">
<li>
&quot;type&quot; (string): track | identify | alias | increment |
decrement
</li>
<li>&quot;payload.name&quot; (string): The name of the event.</li>
<li>
&quot;payload.properties&quot; (object): The properties of the
event.
</li>
</ul>
</div>
);
}
if (name === 'Express') {
return (
<div className="flex flex-col gap-4">
<strong>Install dependencies</strong>
<Syntax code={'npm install @openpanel/express'} />
<strong>Usage</strong>
<p>Connect the middleware to your app.</p>
<Syntax
code={`import express from 'express';
import createOpenpanelMiddleware from '@openpanel/express';
const app = express();
app.use(
createOpenpanelMiddleware({
clientId: '${clientId}',
clientSecret: '${clientSecret}',
// trackRequest(url) {
// return url.includes('/v1')
// },
// getProfileId(req) {
// return req.user.id
// }
})
);
app.get('/sign-up', (req, res) => {
// track sign up events
req.op.track('sign-up', {
email: req.body.email,
});
res.send('Hello World');
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});`}
/>
</div>
);
}
if (name === 'Node') {
return (
<div className="flex flex-col gap-4">
<strong>Install dependencies</strong>
<Syntax code={'pnpm install @openpanel/sdk'} />
<strong>Create a instance</strong>
<p>
Create a new instance of OpenPanel. You can use this SDK in any JS
environment. You should omit clientSecret if you use this on web!
</p>
<Syntax
code={`import { OpenPanel } from '@openpanel/sdk';
const op = new OpenPanel({
clientId: '${clientId}',
// mostly for backend and apps that can't rely on CORS
clientSecret: '${clientSecret}',
});`}
/>
<strong>Usage</strong>
<Syntax
code={`import { op } from './openpanel';
// Sends an event with payload foo: bar
op.track('my_event', { foo: 'bar' });
// Identify with profile id
op.identify({ profileId: '123' });
// or with additional data
op.identify({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment({ name: 'app_opened', profile_id: '123' }); // increment by 1
op.increment({ name: 'app_opened', profile_id: '123', value: 5 }); // increment by 5
// Decrement a property
op.decrement({ name: 'app_opened', profile_id: '123' }); // decrement by 1
op.decrement({ name: 'app_opened', profile_id: '123', value: 5 }); // decrement by 5`}
/>
</div>
);
}
if (name === 'React-Native') {
return (
<div className="flex flex-col gap-4">
<strong>Install dependencies</strong>
<p>Don&apos;t forget to install the peer dependencies as well!</p>
<Syntax
code={`pnpm install @openpanel/react-native
npx expo install --pnpm expo-application expo-constants`}
/>
<strong>Create a instance</strong>
<p>
Create a new instance of OpenpanelSdk. You can use this SDK in any JS
environment. You should omit clientSecret if you use this on web!
</p>
<Syntax
code={`import { OpenPanel } from '@openpanel/react-native';
const op = new OpenPanel({
clientId: '${clientId}',
clientSecret: '${clientSecret}',
});`}
/>
<strong>Usage</strong>
<Syntax
code={`import { op } from './openpanel';
// Sends an event with payload foo: bar
op.track('my_event', { foo: 'bar' });
// Identify with profile id
op.identify({ profileId: '123' });
// or with additional data
op.identify({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment({ name: 'app_opened', profile_id: '123' }); // increment by 1
op.increment({ name: 'app_opened', profile_id: '123', value: 5 }); // increment by 5
// Decrement a property
op.decrement({ name: 'app_opened', profile_id: '123' }); // decrement by 1
op.decrement({ name: 'app_opened', profile_id: '123', value: 5 }); // decrement by 5`}
/>
<strong>Navigation</strong>
<p>
Check out our{' '}
<a
href="https://github.com/Openpanel-dev/examples/tree/main/expo-app"
target="_blank"
className="underline"
rel="noreferrer"
>
example app
</a>{' '}
. See below for a quick demo.
</p>
<Syntax
code={`function RootLayoutNav() {
const pathname = usePathname()
useEffect(() => {
op.screenView(pathname)
}, [pathname])
const Instructions = ({ framework }: Props) => {
return (
<Stack>
{/*... */}
</Stack>
<iframe
className="w-full h-full"
src={framework.href}
title={framework.name}
/>
);
}`}
/>
</div>
);
}
};
export default function InsdtructionsWithModalContent(props: Props) {
export default function InstructionsWithModalContent(props: Props) {
return (
<SheetContent>
<Header framework={props.framework} />
<SheetContent className="p-0">
<Instructions {...props} />
<Footer framework={props.framework} />
<Footer {...props} />
</SheetContent>
);
}

View File

@@ -62,9 +62,6 @@ const modals = {
VerifyEmail: dynamic(() => import('./VerifyEmail'), {
loading: Loading,
}),
FunnelStepDetails: dynamic(() => import('./FunnelStepDetails'), {
loading: Loading,
}),
DateRangerPicker: dynamic(() => import('./DateRangerPicker'), {
loading: Loading,
}),