add documentation

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-03-11 13:05:28 +01:00
parent faafb71d88
commit 1ca95442b9
27 changed files with 3236 additions and 1 deletions

42
apps/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# database
/prisma/db.sqlite
/prisma/db.sqlite-journal
# next.js
/.next/
/out/
next-env.d.ts
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo

View File

@@ -0,0 +1 @@
./src/pages/docs/*

113
apps/docs/Dockerfile Normal file
View File

@@ -0,0 +1,113 @@
FROM --platform=linux/amd64 node:20-slim AS base
ARG NEXT_PUBLIC_DASHBOARD_URL
ENV NEXT_PUBLIC_DASHBOARD_URL=$NEXT_PUBLIC_DASHBOARD_URL
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL
ARG CLICKHOUSE_DB
ENV CLICKHOUSE_DB=$CLICKHOUSE_DB
ARG CLICKHOUSE_PASSWORD
ENV CLICKHOUSE_PASSWORD=$CLICKHOUSE_PASSWORD
ARG CLICKHOUSE_URL
ENV CLICKHOUSE_URL=$CLICKHOUSE_URL
ARG CLICKHOUSE_USER
ENV CLICKHOUSE_USER=$CLICKHOUSE_USER
ARG REDIS_URL
ENV REDIS_URL=$REDIS_URL
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ARG CLERK_SECRET_KEY
ENV CLERK_SECRET_KEY=$CLERK_SECRET_KEY
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
ARG NODE_VERSION=20
RUN apt update \
&& apt install -y curl \
&& curl -L https://raw.githubusercontent.com/tj/n/master/bin/n -o n \
&& bash n $NODE_VERSION \
&& rm n \
&& npm install -g n
WORKDIR /app
COPY package.json package.json
COPY pnpm-lock.yaml pnpm-lock.yaml
COPY pnpm-workspace.yaml pnpm-workspace.yaml
COPY apps/public/package.json apps/public/package.json
COPY packages/db/package.json packages/db/package.json
COPY packages/redis/package.json packages/redis/package.json
COPY packages/queue/package.json packages/queue/package.json
COPY packages/common/package.json packages/common/package.json
COPY packages/constants/package.json packages/constants/package.json
COPY packages/validation/package.json packages/validation/package.json
COPY packages/sdk/package.json packages/sdk/package.json
# BUILD
FROM base AS build
WORKDIR /app/apps/public
RUN pnpm install --frozen-lockfile --ignore-scripts
WORKDIR /app
COPY apps apps
COPY packages packages
COPY tooling tooling
RUN pnpm db:codegen
WORKDIR /app/apps/public
RUN pnpm run build
# PROD
FROM base AS prod
WORKDIR /app/apps/public
RUN pnpm install --frozen-lockfile --prod --ignore-scripts
# FINAL
FROM base AS runner
COPY --from=build /app/package.json /app/package.json
COPY --from=prod /app/node_modules /app/node_modules
# Apps
COPY --from=build /app/apps/public /app/apps/public
# Apps node_modules
COPY --from=prod /app/apps/public/node_modules /app/apps/public/node_modules
# Packages
COPY --from=build /app/packages/db /app/packages/db
COPY --from=build /app/packages/redis /app/packages/redis
COPY --from=build /app/packages/common /app/packages/common
COPY --from=build /app/packages/queue /app/packages/queue
COPY --from=build /app/packages/constants /app/packages/constants
COPY --from=build /app/packages/validation /app/packages/validation
COPY --from=build /app/packages/sdk /app/packages/sdk
# Packages node_modules
COPY --from=prod /app/packages/db/node_modules /app/packages/db/node_modules
COPY --from=prod /app/packages/redis/node_modules /app/packages/redis/node_modules
COPY --from=prod /app/packages/common/node_modules /app/packages/common/node_modules
COPY --from=prod /app/packages/queue/node_modules /app/packages/queue/node_modules
COPY --from=prod /app/packages/validation/node_modules /app/packages/validation/node_modules
RUN pnpm db:codegen
WORKDIR /app/apps/public
EXPOSE 3000
CMD ["pnpm", "start"]

1
apps/docs/README.md Normal file
View File

@@ -0,0 +1 @@
# Docs

33
apps/docs/next.config.mjs Normal file
View File

@@ -0,0 +1,33 @@
import nextra from 'nextra';
/** @type {import("next").NextConfig} */
const config = {
reactStrictMode: true,
transpilePackages: ['@mixan/queue'],
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
experimental: {
// Avoid "Critical dependency: the request of a dependency is an expression"
serverComponentsExternalPackages: ['bullmq'],
},
/**
* If you are using `appDir` then you must comment the below `i18n` config out.
*
* @see https://github.com/vercel/next.js/issues/41980
*/
i18n: {
locales: ['en'],
defaultLocale: 'en',
},
};
const withNextra = nextra({
theme: 'nextra-theme-docs',
themeConfig: './theme.config.jsx',
flexsearch: {
codeblocks: false,
},
defaultShowCopyCode: true,
});
export default withNextra(config);

46
apps/docs/package.json Normal file
View File

@@ -0,0 +1,46 @@
{
"name": "@mixan/docs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "rm -rf .next && pnpm with-env next dev",
"build": "next build",
"start": "next start",
"lint": "eslint .",
"format": "prettier --write \"**/*.{tsx,mjs,ts,md,json}\"",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env -c --"
},
"dependencies": {
"next": "~14.0.4",
"nextra": "^2.13.4",
"nextra-theme-docs": "^2.13.4",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@mixan/eslint-config": "workspace:*",
"@mixan/prettier-config": "workspace:*",
"@mixan/tsconfig": "workspace:*",
"@types/node": "^18.16.0",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/parser": "^6.6.0",
"autoprefixer": "^10.4.18",
"eslint": "^8.48.0",
"postcss": "^8.4.35",
"prettier": "^3.0.3",
"tailwindcss": "^3.4.1",
"typescript": "^5.2.2"
},
"eslintConfig": {
"root": true,
"extends": [
"@mixan/eslint-config/base",
"@mixan/eslint-config/react",
"@mixan/eslint-config/nextjs"
]
},
"prettier": "@mixan/prettier-config"
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -0,0 +1,12 @@
import Link from 'next/link';
import { Callout } from 'nextra/components';
export function DeviceIdWarning() {
return (
<Callout>
Read more about{' '}
<Link href="/docs/device-id">device id and why you might want it</Link>.
**We recommend not to but it's up to you.**
</Callout>
);
}

View File

@@ -0,0 +1,10 @@
import { Callout } from 'nextra/components';
export function PersonalDataWarning() {
return (
<Callout emoji="⚠️">
Keep in mind that this is considered personal data. Make sure you have the
users consent before calling this!
</Callout>
);
}

View File

@@ -0,0 +1,5 @@
- `url` - The url of the openpanel API or your self-hosted instance
- `clientId` - The client id of your application
- `trackScreenViews` - If true, the library will automatically track screen views
- `trackOutgoingLinks` - If true, the library will automatically track outgoing links
- `trackAttributes` - If true, the library will automatically track attributes

View File

@@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
article main a {
@apply underline;
}

View File

@@ -0,0 +1,11 @@
import type { AppProps } from 'next/app';
import 'src/globals.css';
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
</>
);
}

View File

@@ -0,0 +1,10 @@
{
"index": {
"title": "Introduction",
"type": "page"
},
"docs": {
"title": "Documentation",
"type": "page"
}
}

View File

@@ -0,0 +1,14 @@
{
"index": "Get Started",
"-- Implementation": {
"type": "separator",
"title": "Implementation"
},
"script": "Script",
"javascript": "Javascript SDK",
"nextjs": "Next.js",
"react": "React",
"react-native": "React-Native",
"remix": "Remix",
"vue": "Vue"
}

View File

@@ -0,0 +1,80 @@
import { Card, Cards } from 'nextra/components';
# Get started
Create an account on [Openpanel](https://openpanel.dev) and setup your first project (takes 2 minutes). You will get a `clientId` that you will use to initialize Openpanel in your app.
<Cards>
<Card
icon={
<img
style={{ width: 32, height: 32, objectFit: 'contain' }}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/240px-HTML5_logo_and_wordmark.svg.png"
/>
}
title="General / Script"
href="/docs/general"
>
{' '}
</Card>
<Card
icon={
<img
style={{ width: 32, height: 32, objectFit: 'contain' }}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png"
/>
}
title="React"
href="/docs/react"
>
{' '}
</Card>
<Card
icon={
<img
style={{ width: 32, height: 32, objectFit: 'contain' }}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png"
/>
}
title="React-Native"
href="/docs/react-native"
>
{' '}
</Card>
<Card
icon={
<img
style={{ width: 32, height: 32, objectFit: 'contain' }}
src="https://static-00.iconduck.com/assets.00/nextjs-icon-512x512-y563b8iq.png"
/>
}
title="Next.js"
href="/docs/nextjs"
>
{' '}
</Card>
<Card
icon={
<img
style={{ width: 32, height: 32, objectFit: 'contain' }}
src="https://www.datocms-assets.com/205/1642515307-square-logo.svg"
/>
}
title="Remix"
href="/docs/remix"
>
{' '}
</Card>
<Card
icon={
<img
style={{ width: 32, height: 32, objectFit: 'contain' }}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1024px-Vue.js_Logo_2.svg.png"
/>
}
title="Vue"
href="/docs/vue"
>
{' '}
</Card>
</Cards>

View File

@@ -0,0 +1,139 @@
import Link from 'next/link';
import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx';
# Javascript SDK
This is the base SDK for Openpanel. All other SDKs are built on top of this one.
## Installation
<Steps>
### Install dependencies
```bash
pnpm install @openpanel/sdk
```
### Initialize
```tsx
import { Openpanel } from '@openpanel/sdk';
const op = new Openpanel({
url: 'https://api.openpanel.dev',
clientId: '{YOUR_CLIENT_ID}',
// mostly for backend and apps that can't rely on CORS
clientSecret: '{YOUR_CLIENT_SECRET}',
});
```
#### Config
- `url` - The url of the openpanel API or your self-hosted instance
- `clientId` - The client id of your application
- `clientSecret` - The client secret of your application (mostly for backend and apps that can't rely on CORS)
### Ready!
You're now ready to use the library.
```typescript
// Sends an event with payload foo: bar
op.event('my_event', { foo: 'bar' });
// Identify with profile id
op.setProfileId('123');
// or with additional data
op.setProfile({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment('app_opened'); // increment by 1
op.increment('app_opened', 5); // increment by 5
// Decrement a property
op.decrement('app_opened'); // decrement by 1
op.decrement('app_opened', 5); // decrement by 5
```
</Steps>
## Usage
### Track event
```typescript
op.event('my_event', { foo: 'bar' });
```
### Identify
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```typescript
import { setProfileId } from '@openpanel/nextjs';
const profileId = '123';
setProfileId(profileId);
```
#### Additional data
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```typescript
import { setProfile } from '@openpanel/nextjs';
const profileId = '123';
setProfile({
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
});
```
#### Increment property
Increment a property on the profile.
```typescript
import { increment } from '@openpanel/nextjs';
// Increment by 1
increment('app_opened');
// Increment by 5
increment('app_opened', 5);
```
#### Decrement property
Decrement a property on the profile.
```typescript
import { decrement } from '@openpanel/nextjs';
// Increment by 1
decrement('app_opened');
// Increment by 5
decrement('app_opened', 5);
```

View File

@@ -0,0 +1,172 @@
import Link from 'next/link';
import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx';
# Next.js
Keep in mind that all tracking here happens on the client! If you want to track server-side events, you should use the <Link href="/docs/node">node SDK</Link> 👀.
## Installation
<Steps>
### Install dependencies
```bash
pnpm install @openpanel/nextjs
```
### Initialize
Add `OpenpanelProvider` to your root layout component.
```tsx
import { OpenpanelProvider } from '@openpanel/nextjs';
export default RootLayout({ children }) {
return (
<>
<OpenpanelProvider
url="https://api.openpanel.dev"
clientId="your-client-id"
trackScreenViews={true}
// trackAttributes={true}
// trackOutgoingLinks={true}
/>
{children}
</>
)
}
```
#### Config
<SdkConfig />
### Ready!
You're now ready to use the library.
```typescript
import {
decrement,
increment,
setProfile,
setProfileId,
trackEvent,
trackScreenView,
} from '@openpanel/nextjs';
// Sends an event with payload foo: bar
trackEvent('my_event', { foo: 'bar' });
// Identify with profile id
setProfileId('123');
// or with additional data
setProfile({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
increment('app_opened'); // increment by 1
increment('app_opened', 5); // increment by 5
// Decrement a property
decrement('app_opened'); // decrement by 1
decrement('app_opened', 5); // decrement by 5
```
</Steps>
## Usage
### Track event
<Tabs items={['JS', 'window', 'HTML']}>
<Tabs.Tab>
```typescript
import { trackEvent } from '@openpanel/nextjs';
trackEvent('my_event', { foo: 'bar' });
```
</Tabs.Tab>
<Tabs.Tab>
```javascript
window.op('event', 'my_event', { foo: 'bar' });
```
</Tabs.Tab>
<Tabs.Tab>
For this to work you need to enable `trackAttributes` in the config.
```html
<button data-event="my_event" data-foo="bar">Track event</button>
```
</Tabs.Tab>
</Tabs>
### Identify
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```typescript
import { setProfileId } from '@openpanel/nextjs';
const profileId = '123';
setProfileId(profileId);
```
#### Additional data
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```typescript
import { setProfile } from '@openpanel/nextjs';
const profileId = '123';
setProfile({
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
});
```
#### Increment property
Increment a property on the profile.
```typescript
import { increment } from '@openpanel/nextjs';
// Increment by 1
increment('app_opened');
// Increment by 5
increment('app_opened', 5);
```
#### Decrement property
Decrement a property on the profile.
```typescript
import { decrement } from '@openpanel/nextjs';
// Increment by 1
decrement('app_opened');
// Increment by 5
decrement('app_opened', 5);
```

View File

@@ -0,0 +1,196 @@
import Link from 'next/link';
import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx';
# React-Native
## Installation
<Steps>
### Install dependencies
We're dependent on `expo-application` for `buildNumber`, `versionNumber` (and `referrer` on android) and `expo-constants` to get the `user-agent`.
```bash
pnpm install @openpanel/react-native
expo install --pnpm expo-application expo-constants
```
### Initialize
On native we use a clientSecret to authenticate the app.
```typescript
const op = new Openpanel({
url: 'https://api.openpanel.dev',
clientId: '{YOUR_CLIENT_ID}',
clientSecret: '{YOUR_CLIENT_SECRET}',
});
```
#### Config
- `url` - The url of the openpanel API or your self-hosted instance
- `clientId` - The client id of your application
- `clientSecret` - The client secret of your application
### Ready!
You're now ready to use the library.
```typescript
// Sends an event with payload foo: bar
op.event('my_event', { foo: 'bar' });
// Identify with profile id
op.setProfileId('123');
// or with additional data
op.setProfile({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment('app_opened'); // increment by 1
op.increment('app_opened', 5); // increment by 5
// Decrement a property
op.decrement('app_opened'); // decrement by 1
op.decrement('app_opened', 5); // decrement by 5
```
</Steps>
## Usage
### Track event
```typescript
op.event('my_event', { foo: 'bar' });
```
### Navigation / Screen views
<Tabs items={['expo-router', 'react-navigation (simple)']}>
<Tabs.Tab>
```typescript
import { usePathname, useSegments } from 'expo-router';
function RootLayout() {
// ...
const pathname = usePathname()
// Segments is optional but can be nice to have if you
// want to group routes together
// pathname = /posts/123
// segements = ['posts', '[id]']
const segments = useSegments()
useEffect(() => {
// Simple
op.screenView(pathname)
// With extra data
op.screenView(pathname, {
// segments is optional but nice to have
segments: segments.join('/'),
// other optional data you want to send with the screen view
})
}, [pathname,segments])
// ...
}
```
</Tabs.Tab>
<Tabs.Tab>
```tsx
import { createNavigationContainerRef } from '@react-navigation/native'
import { Openpanel } from '@openpanel/react-native'
const op = new Openpanel({ /* ... */ })
const navigationRef = createNavigationContainerRef()
export function NavigationRoot() {
const handleNavigationStateChange = () => {
const current = navigationRef.getCurrentRoute()
if (current) {
op.screenView(current.name, {
params: current.params,
})
}
}
return (
<NavigationContainer
ref={navigationRef}
onReady={handleNavigationStateChange}
onStateChange={handleNavigationStateChange}
>
<Stack.Navigator />
</NavigationContainer>
)
}
```
</Tabs.Tab>
</Tabs>
### Identify
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfileId(profileId);
```
#### Additional data
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfile({
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
});
```
#### Increment property
Increment a property on the profile.
```typescript
// Increment by 1
op.increment('app_opened');
// Increment by 5
op.increment('app_opened', 5);
```
#### Decrement property
Decrement a property on the profile.
```typescript
// Increment by 1
op.decrement('app_opened');
// Increment by 5
op.decrement('app_opened', 5);
```

View File

@@ -0,0 +1,5 @@
import Link from 'next/link';
# React
Use <Link href="/docs/general">script tag</Link> or <Link href="/docs/general">Javascript SDK</Link> for now. We'll add a dedicated react sdk soon.

View File

@@ -0,0 +1,5 @@
import Link from 'next/link';
# Remix
Use <Link href="/docs/general">script tag</Link> or <Link href="/docs/general">Javascript SDK</Link> for now. We'll add a dedicated react sdk soon.

View File

@@ -0,0 +1,122 @@
import { Callout, Tabs } from 'nextra/components';
import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx';
# Script tag
Just insert this snippet and replace `{YOUR_CLIENT_ID}` with your client id.
```html
<script src="https://openpanel.dev/op.js"></script>
<script>
window.op =
window.op ||
function (...args) {
(window.op.q = window.op.q || []).push(args);
};
window.op('ctor', {
url: 'https://api.openpanel.dev',
clientId: '{YOUR_CLIENT_ID}',
trackScreenViews: true,
// trackOutgoingLinks: true,
// trackAttributes: true,
});
</script>
```
### Config
<SdkConfig />
## Usage
You can let the library track screen views, outgoing links and attributes tracking by setting the `trackScreenViews`, `trackOutgoingLinks` and `trackAttributes` options to `true`.
### Track event
<Tabs items={['JS', 'HTML']}>
<Tabs.Tab>
```javascript
window.op('event', 'my_event', { foo: 'bar' });
```
</Tabs.Tab>
<Tabs.Tab>
For this to work you need to enable `trackAttributes` in the config.
```html
<button data-event="my_event" data-foo="bar">Track event</button>
```
</Tabs.Tab>
</Tabs>
### Identify
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```javascript
const profileId = '123';
window.op('setProfileId', profileId);
```
#### Additional data
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```javascript
const profileId = '123';
window.op('setProfile', {
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
});
```
#### Increment property
Increment a property on the profile.
```javascript
// Increment by 1
window.op('increment', 'app_opened');
// Increment by 5
window.op('increment', 'app_opened', 5);
```
#### Decrement property
Decrement a property on the profile.
```javascript
// Increment by 1
window.op('decrement', 'app_opened');
// Increment by 5
window.op('decrement', 'app_opened', 5);
```
### Typescript
Is your IDE mad at you for not using typescript? We got you covered.
Add this and it will stop complain about `window.op` not being defined.
```typescript
declare global {
interface Window {
op: {
q?: [string, ...any[]];
(method: string, ...args: any[]): void;
};
}
}
```

View File

@@ -0,0 +1,5 @@
import Link from 'next/link';
# Vue
Use <Link href="/docs/general">script tag</Link> or <Link href="/docs/general">Javascript SDK</Link> for now. We'll add a dedicated react sdk soon.

View File

@@ -0,0 +1,14 @@
# Introduction
Openpanel is an open-source alternative to Mixpanel. Combining the power of Mixpanel with the ease of Plausible, Openpanel is a privacy-focused analytics tool that gives you the insights you need to make data-driven decisions.
## Features
- ✅ **Privacy-focused**: Openpanel is built with privacy in mind. We don't track any personal data and we don't use cookies.
- ✅ **Open-source**: Openpanel is open-source and you can host it yourself.
- ✅ **Cloud-hosted**: You can choose our cloud hosting if you don't want to host it yourself. We take care of the infrastructure and you can focus on your business.
- ✅ **Real-time analytics**: Everything is updated in real-time, no delays to see your insights.
- ✅ **Event-based tracking**: You can track any event you want, and you can track as many events as you want.
- ✅ **Custom properties**: You can add custom properties to your events to track more data.
- ✅ **Funnel analysis**: You can create funnels to see how your users are interacting with your product.
- ✅ **Reports**: Create as many reports as you want to visualize the data you need.

View File

@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -0,0 +1,75 @@
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useConfig } from 'nextra-theme-docs';
export default {
logo: (
<>
<Image
src="https://dashboard.openpanel.dev/logo.svg"
alt="next-international logo"
height="32"
width="32"
/>
<strong style={{ marginLeft: '8px' }}>openpanel</strong>
</>
),
head: (props) => {
const router = useRouter();
const config = useConfig();
const title = config.title;
const description =
config.description || 'An open-source alternative to Mixpanel';
const domain = 'https://docs.openpanel.dev';
const canonicalUrl =
`${domain}${router.asPath === '/' ? '' : router.asPath}`.split('?')[0];
return (
<>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta property="description" content={description} />
<link rel="canonical" href={canonicalUrl} />
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:site:domain"
content={domain.replace('https://', '')}
/>
<meta name="twitter:url" content={domain} />
<meta name="og:type" content={`site`} />
<meta name="og:url" content={canonicalUrl} />
<meta name="og:title" content={`${title} - Openpanel Docs`} />
<meta property="og:description" content={description} />
<meta name="og:image" content={'https://openpanel.dev/ogimage.png'} />
<meta name="title" content={title} />
<meta name="description" content={description} />
</>
);
},
search: {
placeholder: 'Search',
},
project: {
link: 'https://github.com/openpanel-dev/openpanel',
},
docsRepositoryBase:
'https://github.com/openpanel-dev/openpanel/blob/main/apps/docs',
useNextSeoProps() {
return {
titleTemplate: '%s - Openpanel',
};
},
footer: {
text: (
<span>
Made with by{' '}
<a
href="https://twitter.com/CarlLindesvard"
target="_blank"
rel="nofollow"
>
Carl
</a>
</span>
),
},
};

25
apps/docs/tsconfig.json Normal file
View File

@@ -0,0 +1,25 @@
{
"extends": "@mixan/tsconfig/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
},
"plugins": [
{
"name": "next"
}
],
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
"strictNullChecks": true
},
"include": [
".",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

2080
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff