refactor: standardize env and analytics using enhanced @mintel/next-utils
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 4s
Build & Deploy / 🏗️ Build (push) Failing after 3m15s
Build & Deploy / 🧪 QA (push) Failing after 4m42s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-10 23:47:26 +01:00
parent d0c555736f
commit e3120e3ff8
2 changed files with 71 additions and 20 deletions

34
apps/web/lib/env.ts Normal file
View File

@@ -0,0 +1,34 @@
import { z } from "zod";
import { validateMintelEnv, mintelEnvSchema } from "@mintel/next-utils";
/**
* Environment variable schema for the main website.
* Extends the default Mintel environment schema.
*/
export const envSchema = z.object({
...mintelEnvSchema,
// Project specific overrides or additions
AUTH_COOKIE_NAME: z.string().default("mintel_gatekeeper_session"),
// Analytics provider toggle
NEXT_PUBLIC_ANALYTICS_PROVIDER: z
.enum(["plausible", "umami"])
.default("plausible"),
// Plausible specifics (to be standardized later if needed)
NEXT_PUBLIC_PLAUSIBLE_DOMAIN: z.string().default("mintel.me"),
NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL: z.string().url().optional(),
});
/**
* Validated environment object.
*/
export const env = validateMintelEnv(envSchema.shape);
/**
* For legacy compatibility with existing code.
*/
export function getRawEnv() {
return env;
}

View File

@@ -3,9 +3,13 @@
* Clean constructor-based dependency injection
*/
import type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig } from './interfaces';
import { PlausibleAdapter } from './plausible-adapter';
import { UmamiAdapter, type UmamiConfig } from './umami-adapter';
import type {
AnalyticsAdapter,
AnalyticsEvent,
AnalyticsConfig,
} from "./interfaces";
import { PlausibleAdapter } from "./plausible-adapter";
import { UmamiAdapter, type UmamiConfig } from "./umami-adapter";
export class AnalyticsService {
private adapter: AnalyticsAdapter;
@@ -30,7 +34,7 @@ export class AnalyticsService {
if (this.adapter.page) {
return this.adapter.page(path, props);
}
return this.track({ name: 'Pageview', props: { path, ...props } });
return this.track({ name: "Pageview", props: { path, ...props } });
}
async identify(userId: string, traits?: Record<string, any>): Promise<void> {
@@ -46,28 +50,34 @@ export class AnalyticsService {
async trackOutboundLink(url: string, text: string): Promise<void> {
return this.track({
name: 'Outbound Link',
props: { url, text }
name: "Outbound Link",
props: { url, text },
});
}
async trackSearch(query: string, path: string): Promise<void> {
return this.track({
name: 'Search',
props: { query, path }
name: "Search",
props: { query, path },
});
}
async trackPageLoad(loadTime: number, path: string, userAgent: string): Promise<void> {
async trackPageLoad(
loadTime: number,
path: string,
userAgent: string,
): Promise<void> {
return this.track({
name: 'Page Load',
props: { loadTime: Math.round(loadTime), path, userAgent }
name: "Page Load",
props: { loadTime: Math.round(loadTime), path, userAgent },
});
}
}
// Factory functions
export function createPlausibleAnalytics(config: AnalyticsConfig): AnalyticsService {
export function createPlausibleAnalytics(
config: AnalyticsConfig,
): AnalyticsService {
return new AnalyticsService(new PlausibleAdapter(config));
}
@@ -78,19 +88,23 @@ export function createUmamiAnalytics(config: UmamiConfig): AnalyticsService {
// Default singleton
let defaultAnalytics: AnalyticsService | null = null;
import { env } from "@/lib/env";
export function getDefaultAnalytics(): AnalyticsService {
if (!defaultAnalytics) {
const provider = process.env.NEXT_PUBLIC_ANALYTICS_PROVIDER || 'plausible';
const provider = env.NEXT_PUBLIC_ANALYTICS_PROVIDER;
if (provider === 'umami') {
if (provider === "umami") {
defaultAnalytics = createUmamiAnalytics({
websiteId: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || '',
hostUrl: process.env.NEXT_PUBLIC_UMAMI_HOST_URL,
websiteId: env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || "",
hostUrl: env.UMAMI_API_ENDPOINT,
});
} else {
defaultAnalytics = createPlausibleAnalytics({
domain: process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN || 'mintel.me',
scriptUrl: process.env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL || 'https://plausible.yourdomain.com/js/script.js'
domain: env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN,
scriptUrl:
env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL ||
"https://plausible.yourdomain.com/js/script.js",
});
}
}
@@ -98,10 +112,13 @@ export function getDefaultAnalytics(): AnalyticsService {
}
// Convenience function
export async function track(name: string, props?: Record<string, any>): Promise<void> {
export async function track(
name: string,
props?: Record<string, any>,
): Promise<void> {
return getDefaultAnalytics().trackEvent(name, props);
}
// Re-export for advanced usage
export type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig };
export { PlausibleAdapter };
export { PlausibleAdapter };