a looooot

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-02-22 21:50:30 +01:00
parent 1d800835b8
commit 9c92803c4c
61 changed files with 2689 additions and 681 deletions

View File

@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -6,7 +6,12 @@
/** @type {import("next").NextConfig} */
const config = {
reactStrictMode: false,
transpilePackages: ['@mixan/types', '@mixan/sdk', '@mixan/web-sdk'],
transpilePackages: [
'@mixan/types',
'@mixan/sdk',
'@mixan/web-sdk',
'@mixan/next',
],
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
/**

View File

@@ -11,11 +11,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"next": "13.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"@mixan-test/next": "workspace:@mixan/next@*",
"@mixan-test/sdk": "workspace:@mixan/sdk@*",
"@mixan-test/sdk-web": "workspace:@mixan/sdk-web@*"
"@mixan-test/sdk-web": "workspace:@mixan/sdk-web@*",
"next": "~14.1.0",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@mixan/eslint-config": "workspace:*",

View File

@@ -1,2 +1,2 @@
"use strict";(()=>{function w(s){return Promise.all(Object.entries(s).map(async([t,e])=>[t,await e??""])).then(t=>Object.fromEntries(t))}function P(s){let t={"Content-Type":"application/json"};return{headers:t,async fetch(e,i,n){let o=`${s}${e}`,u,m=await w(t);return new Promise(c=>{let h=r=>{clearTimeout(u),fetch(o,{headers:m,method:"POST",body:JSON.stringify(i??{}),keepalive:!0,...n??{}}).then(async a=>{if(a.status!==200&&a.status!==202)return f(r,c);let g=await a.text();if(!g)return c(null);c(g)}).catch(()=>f(r,c))};function f(r,a){if(r>1)return a(null);u=setTimeout(()=>{h(r+1)},Math.pow(2,r)*500)}h(0)})}}}var l=class{options;api;state={properties:{}};constructor(t){this.options=t,this.api=P(t.url),this.api.headers["mixan-client-id"]=t.clientId,this.options.clientSecret&&(this.api.headers["mixan-client-secret"]=this.options.clientSecret)}init(t){this.state.properties=t??{}}setUser(t){this.api.fetch("/profile",{profileId:this.getProfileId(),...t,properties:{...this.state.properties,...t.properties}})}increment(t,e){this.api.fetch("/profile/increment",{property:t,value:e,profileId:this.getProfileId()})}decrement(t,e){this.api.fetch("/profile/decrement",{property:t,value:e,profileId:this.getProfileId()})}event(t,e){this.api.fetch("/event",{name:t,properties:{...this.state.properties,...e??{}},timestamp:this.timestamp(),profileId:this.getProfileId()}).then(i=>{this.options.setProfileId&&i&&this.options.setProfileId(i)})}setGlobalProperties(t){this.state.properties={...this.state.properties,...t}}clear(){this.state.profileId=void 0,this.options.removeProfileId&&this.options.removeProfileId()}timestamp(){return new Date().toISOString()}getProfileId(){if(this.state.profileId)return this.state.profileId;this.options.getProfileId&&(this.state.profileId=this.options.getProfileId()||void 0)}};var d=class extends l{lastPath="";constructor(t){super(t),this.isServer()||(this.setGlobalProperties({referrer:document.referrer}),this.options.trackOutgoingLinks&&this.trackOutgoingLinks(),this.options.trackScreenViews&&this.trackScreenViews())}isServer(){return typeof document>"u"}trackOutgoingLinks(){this.isServer()||document.addEventListener("click",t=>{let e=t.target,i=e.closest("a");if(i&&e){let n=i.getAttribute("href");n?.startsWith("http")&&super.event("link_out",{href:n,text:i.innerText||i.getAttribute("title")||e.getAttribute("alt")||e.getAttribute("title")})}})}trackScreenViews(){if(this.isServer())return;let t=history.pushState;history.pushState=function(...n){let o=t.apply(this,n);return window.dispatchEvent(new Event("pushstate")),window.dispatchEvent(new Event("locationchange")),o};let e=history.replaceState;history.replaceState=function(...n){let o=e.apply(this,n);return window.dispatchEvent(new Event("replacestate")),window.dispatchEvent(new Event("locationchange")),o},window.addEventListener("popstate",()=>window.dispatchEvent(new Event("locationchange"))),this.options.hash?window.addEventListener("hashchange",()=>this.screenView()):window.addEventListener("locationchange",()=>this.screenView()),this.screenView()}screenView(t){if(this.isServer())return;let e=window.location.href;this.lastPath!==e&&(this.lastPath=e,super.event("screen_view",{...t??{},path:e,title:document.title}))}};var p=document.currentScript;p&&(window.openpanel=new d({url:p?.getAttribute("data-url"),clientId:p?.getAttribute("data-client-id"),trackOutgoingLinks:!!p?.getAttribute("data-track-outgoing-links"),trackScreenViews:!!p?.getAttribute("data-track-screen-views")}));})();
"use strict";(()=>{function v(r){return Promise.all(Object.entries(r).map(async([t,e])=>[t,await e??""])).then(t=>Object.fromEntries(t))}function m(r){let t={"Content-Type":"application/json"};return{headers:t,async fetch(e,i,n){let s=`${r}${e}`,c,l=await v(t);return new Promise(o=>{let h=a=>{clearTimeout(c),fetch(s,{headers:l,method:"POST",body:JSON.stringify(i??{}),keepalive:!0,...n??{}}).then(async p=>{if(p.status!==200&&p.status!==202)return f(a,o);let g=await p.text();if(!g)return o(null);o(g)}).catch(()=>f(a,o))};function f(a,p){if(a>1)return p(null);c=setTimeout(()=>{h(a+1)},Math.pow(2,a)*500)}h(0)})}}}var d=class{options;api;state={properties:{}};constructor(t){this.options=t,this.api=m(t.url),this.api.headers["mixan-client-id"]=t.clientId,this.options.clientSecret&&(this.api.headers["mixan-client-secret"]=this.options.clientSecret)}init(t){this.state.properties=t??{}}setProfileId(t){this.state.profileId=t}setProfile(t){this.setProfileId(t.profileId),this.api.fetch("/profile",{...t,properties:{...this.state.properties,...t.properties}})}increment(t,e,i){let n=i?.profileId??this.state.profileId;if(!n)return console.log("No profile id");this.api.fetch("/profile/increment",{profileId:n,property:t,value:e})}decrement(t,e,i){let n=i?.profileId??this.state.profileId;if(!n)return console.log("No profile id");this.api.fetch("/profile/decrement",{profileId:n,property:t,value:e})}event(t,e){let i=e?.profileId??this.state.profileId;delete e?.profileId,this.api.fetch("/event",{name:t,properties:{...this.state.properties,...e??{}},timestamp:this.timestamp(),deviceId:this.getDeviceId(),profileId:i}).then(n=>{this.options.setDeviceId&&n&&this.options.setDeviceId(n)})}setGlobalProperties(t){this.state.properties={...this.state.properties,...t}}clear(){this.state.properties={},this.state.deviceId=void 0,this.options.removeDeviceId&&this.options.removeDeviceId()}timestamp(){return new Date().toISOString()}getDeviceId(){if(this.state.deviceId)return this.state.deviceId;this.options.getDeviceId&&(this.state.deviceId=this.options.getDeviceId()||void 0)}};function b(r){return r.replace(/([-_][a-z])/gi,t=>t.toUpperCase().replace("-","").replace("_",""))}var u=class extends d{lastPath="";constructor(t){super(t),this.isServer()||(this.setGlobalProperties({referrer:document.referrer}),this.options.trackOutgoingLinks&&this.trackOutgoingLinks(),this.options.trackScreenViews&&this.trackScreenViews(),this.options.trackAttributes&&this.trackAttributes())}isServer(){return typeof document>"u"}trackOutgoingLinks(){this.isServer()||document.addEventListener("click",t=>{let e=t.target,i=e.closest("a");if(i&&e){let n=i.getAttribute("href");n?.startsWith("http")&&super.event("link_out",{href:n,text:i.innerText||i.getAttribute("title")||e.getAttribute("alt")||e.getAttribute("title")})}})}trackScreenViews(){if(this.isServer())return;let t=history.pushState;history.pushState=function(...n){let s=t.apply(this,n);return window.dispatchEvent(new Event("pushstate")),window.dispatchEvent(new Event("locationchange")),s};let e=history.replaceState;history.replaceState=function(...n){let s=e.apply(this,n);return window.dispatchEvent(new Event("replacestate")),window.dispatchEvent(new Event("locationchange")),s},window.addEventListener("popstate",()=>window.dispatchEvent(new Event("locationchange"))),this.options.hash?window.addEventListener("hashchange",()=>this.screenView()):window.addEventListener("locationchange",()=>this.screenView()),setTimeout(()=>{this.screenView()},50)}trackAttributes(){this.isServer()||document.addEventListener("click",t=>{let e=t.target,i=e.closest("button"),n=e.closest("button"),s=i?.getAttribute("data-event")?i:n?.getAttribute("data-event")?n:null;if(s){let c={};for(let o of s.attributes)o.name.startsWith("data-")&&o.name!=="data-event"&&(c[b(o.name.replace(/^data-/,""))]=o.value);let l=s.getAttribute("data-event");l&&super.event(l,c)}})}screenView(t){if(this.isServer())return;let e=window.location.href;this.lastPath!==e&&(this.lastPath=e,super.event("screen_view",{...t??{},path:e,title:document.title}))}};(r=>{if(r.op&&"q"in r.op){let t=r.op.q||[],e=new u(t.shift()[1]);t.forEach(i=>{i[0]in e&&e[i[0]](...i.slice(1))}),r.op=(i,...n)=>{let s=e[i].bind(e);typeof s=="function"&&s(...n)}}})(window);})();
//# sourceMappingURL=cdn.global.js.map

View File

@@ -0,0 +1,12 @@
'use client';
export default function TestPage({ triggerEvent }: { triggerEvent: any }) {
return (
<>
<button onClick={() => triggerEvent()}>Event (server action)</button>
<button data-event="yolo" data-yolo="123" data-hihi="taaa-daaaaa">
Event (data-attributes)
</button>
</>
);
}

View File

@@ -0,0 +1,55 @@
import { OpenpanelProvider, SetProfileId, trackEvent } from '@mixan-test/next';
import { Mixan as Openpanel } from '@mixan-test/sdk';
const opServer = new Openpanel({
clientId: '4c9a28cb-73c3-429f-beaf-4b3fe91352ea',
clientSecret: '2701ada9-fcbf-414a-ac94-9511949ee44d',
url: 'https://api.openpanel.dev',
});
export default function Page() {
// Track event in server actions
async function create() {
'use server';
opServer.event('some-event', {
profileId: '1234',
});
}
return (
<div>
{/* In layout.tsx (app dir) or _app.tsx (pages) */}
<OpenpanelProvider
clientId="0acce97f-1126-4439-b7ee-5d384e2fc94b"
url="https://api.openpanel.dev"
trackScreenViews
trackAttributes
trackOutgoingLinks
/>
{/* Provide user id in React Server Components */}
<SetProfileId value="1234" />
<button
onClick={() =>
trackEvent('some-event', {
bar: 'bar',
foo: 'foo',
revenue: 1000,
})
}
>
Track event with method
</button>
<button
data-event="some-event"
data-bar="bar"
data-foo="foo"
data-revenue="1000"
>
Track event with attributes
</button>
</div>
);
}

View File

@@ -0,0 +1,22 @@
import { OpenpanelProvider } from '@mixan-test/next';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<OpenpanelProvider
clientId="0acce97f-1126-4439-b7ee-5d384e2fc94b"
url="http://localhost:3333"
trackScreenViews
trackAttributes
trackOutgoingLinks
/>
{children}
</body>
</html>
);
}

View File

@@ -1,15 +1,15 @@
import { useEffect } from 'react';
// import { mixan } from '@/analytics';
import { OpenpanelProvider } from '@mixan-test/next';
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
export default function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();
// useEffect(() => {
// mixan.screenView();
// return router.events.on('routeChangeComplete', () => {
// mixan.screenView();
// });
// }, []);
return <Component {...pageProps} />;
return (
<>
<OpenpanelProvider
clientId="0acce97f-1126-4439-b7ee-5d384e2fc94b"
url="http://localhost:3333"
trackScreenViews
/>
<Component {...pageProps} />
</>
);
}

View File

@@ -1,22 +0,0 @@
import { Head, Html, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head>
<script
async
src="/op.js"
data-url="http://localhost:3333"
data-client-id="0acce97f-1126-4439-b7ee-5d384e2fc94b"
data-track-screen-views="1"
data-track-outgoing-links="1"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@@ -1,17 +1,68 @@
import { useEffect, useState } from 'react';
import {
clear,
decrement,
increment,
setProfile,
trackEvent,
} from '@mixan-test/next';
import Link from 'next/link';
export default function Test() {
const [id, setId] = useState('');
const [auth, setAuth] = useState<string | null>(null);
function handleLogin() {
if (id) {
localStorage.setItem('auth', id);
setAuth(id);
}
}
function handleLogout() {
localStorage.removeItem('auth');
setAuth(null);
}
useEffect(() => {
setAuth(localStorage.getItem('auth') ?? null);
}, []);
useEffect(() => {
console.log('auth', auth);
if (auth) {
console.log('set profile?', auth);
setProfile({
profileId: auth,
});
} else {
clear();
}
}, [auth]);
if (auth === null) {
return (
<div>
<input
type="text"
placeholder="Login with user id"
onChange={(e) => setId(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
</div>
);
}
return (
<div>
<Link href="/">Home</Link>
<button
onClick={() => {
// @ts-expect-error
window.openpanel.setUser({
first_name: 'John',
last_name: 'Doe',
email: 'john.doe@gmail.com',
id: '1234',
setProfile({
firstName: 'Maja',
lastName: 'Klara',
profileId: auth,
});
}}
>
@@ -19,16 +70,14 @@ export default function Test() {
</button>
<button
onClick={() => {
// @ts-expect-error
window.openpanel.increment('app_open', 1);
increment('app_open', 1);
}}
>
Increment
</button>
<button
onClick={() => {
// @ts-expect-error
window.openpanel.decrement('app_open', 1);
decrement('app_open', 1);
}}
>
Decrement
@@ -43,8 +92,7 @@ export default function Test() {
</button>
<button
onClick={() => {
// @ts-ignore
window.openpanel.event('custom_click', {
trackEvent('custom_click', {
custom_string: 'test',
custom_number: 1,
});
@@ -52,14 +100,7 @@ export default function Test() {
>
Trigger event
</button>
<button
onClick={() => {
// @ts-expect-error
window.openpanel.clear();
}}
>
Logout
</button>
<button onClick={handleLogout}>Logout</button>
</div>
);
}

View File

@@ -3,15 +3,23 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
},
"plugins": [
{
"name": "next"
}
],
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
"strictNullChecks": true
},
"include": [".", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
".",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}