fix: feat: basic analytics funnel tracking for launch readiness (#1101)

Add self-hosted Umami analytics to replace the third-party cloud.umami.is
tracker. Creates @harb/analytics package with typed event helpers and
instruments the conversion funnel: CTA clicks (landing), wallet connect,
swap initiated, and stake created (web-app).

- Add Umami Docker service sharing existing postgres (separate DB)
- Add Caddy /analytics route to proxy Umami dashboard
- Configure via VITE_UMAMI_URL and VITE_UMAMI_WEBSITE_ID env vars
- Document setup and funnel events in docs/ENVIRONMENT.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
johba 2026-03-23 13:04:24 +00:00
parent 8d67e61c17
commit ca2bc03567
17 changed files with 195 additions and 6 deletions

View file

@ -33,6 +33,7 @@
"vue-router": "^4.2.5",
"vue-tippy": "^6.6.0",
"vue-toastification": "^2.0.0-rc.5",
"@harb/analytics": "*",
"@harb/web3": "*",
"@harb/utils": "*"
},

View file

@ -11,6 +11,7 @@ import { createPermitObject, getSignatureRSV } from '@/utils/blockchain';
import { weiToNumber, compactNumber } from 'kraiken-lib/format';
import { getTaxRateOptionByIndex } from '@/composables/useAdjustTaxRates';
import { useContractToast } from './useContractToast';
import { trackStakeCreated } from '@harb/analytics';
const wallet = useWallet();
const contractToast = useContractToast();
const wagmiConfig: Config = config;
@ -129,6 +130,7 @@ export function useStake() {
const eventArgs: PositionCreatedArgs = topics.args;
const amount = compactNumber(weiToNumber(eventArgs.harbergDeposit, wallet.balance.decimals));
trackStakeCreated();
contractToast.showSuccessToast(amount, 'Success!', 'You Staked', 'Check your positions on the<br /> Staker Dashboard', '$KRK');
waiting.value = false;

View file

@ -7,6 +7,7 @@ import { config as wagmiConfig } from '@/wagmi';
import { getChain, DEFAULT_CHAIN_ID } from '@/config';
import { useWallet } from '@/composables/useWallet';
import { getErrorMessage, ensureAddress } from '@harb/utils';
import { trackSwapInitiated } from '@harb/analytics';
export const WETH_ABI = [
{
@ -217,6 +218,7 @@ export function useSwapKrk() {
});
}
trackSwapInitiated('buy');
await writeContract(wagmiConfig, {
abi: SWAP_ROUTER_ABI,
address: router,
@ -327,6 +329,7 @@ export function useSwapKrk() {
}
sellPhase.value = 'selling';
trackSwapInitiated('sell');
await writeContract(wagmiConfig, {
abi: SWAP_ROUTER_ABI,
address: router,

View file

@ -9,6 +9,7 @@ import { setStakeContract } from '@/contracts/stake';
import { chainsData, DEFAULT_CHAIN_ID } from '@/config';
import { createPublicClient, custom, formatUnits, type EIP1193Provider, type PublicClient, type Transport, type WalletClient } from 'viem';
import { getWalletPublicClient, setWalletPublicClient } from '@/services/walletRpc';
import { trackWalletConnect } from '@harb/analytics';
const balance = ref<GetBalanceReturnType>({
value: 0n,
@ -118,6 +119,7 @@ export function useWallet() {
};
} else if (account.value.address !== data.address || account.value.chainId !== data.chainId) {
logger.info(`Account changed!:`, data.address);
if (!account.value.address && data.address) trackWalletConnect();
account.value = data;
await syncWalletPublicClient(data);
await loadBalance();

View file

@ -2,6 +2,7 @@ import { WagmiPlugin } from '@wagmi/vue';
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query';
import { createApp } from 'vue';
import { config } from './wagmi';
import { initAnalytics } from '@harb/analytics';
import ClickOutSide from '@/directives/ClickOutsideDirective';
import router from './router';
@ -9,6 +10,9 @@ import App from './App.vue';
import './assets/styles/main.sass';
import Toast from 'vue-toastification';
import 'vue-toastification/dist/index.css';
initAnalytics(import.meta.env.VITE_UMAMI_URL, import.meta.env.VITE_UMAMI_WEBSITE_ID);
const queryClient = new QueryClient();
const app = createApp(App);