diff --git a/src/lib/featureUsageLog.ts b/src/lib/featureUsageLog.ts new file mode 100644 index 0000000..e73b2c9 --- /dev/null +++ b/src/lib/featureUsageLog.ts @@ -0,0 +1,90 @@ +import { NEXT_PUBLIC_API_URL } from "@/config/api"; +import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; + +export const FEATURE_USAGE = { + REPORT_MANAGEMENT: "REPORT_MANAGEMENT", + TRUCK_ROUTING_SUMMARY: "TRUCK_ROUTING_SUMMARY", +} as const; + +export const FEATURE_USAGE_ACTION = { + PAGE_VIEW: "PAGE_VIEW", + DOWNLOAD: "DOWNLOAD", + PRINT: "PRINT", +} as const; + +export type FeatureUsageSummaryRow = { + userId: number; + username: string; + pageViews: number; + downloads: number; + prints: number; +}; + +export type FeatureUsageSummaryPayload = { + reportManagement: FeatureUsageSummaryRow[]; + truckRoutingSummary: FeatureUsageSummaryRow[]; +}; + +function toInt(v: unknown): number { + if (v == null) return 0; + if (typeof v === "number") return Number.isFinite(v) ? v : 0; + const n = Number(v); + return Number.isFinite(n) ? n : 0; +} + +function normalizeRow(raw: Record): FeatureUsageSummaryRow { + return { + userId: toInt(raw.userId ?? raw.USER_ID), + username: String(raw.username ?? raw.USERNAME ?? ""), + pageViews: toInt(raw.pageViews ?? raw.PAGEVIEWS), + downloads: toInt(raw.downloads ?? raw.DOWNLOADS), + prints: toInt(raw.prints ?? raw.PRINTS), + }; +} + +/** Fire-and-forget client log; failures are ignored (console only). */ +export function logFeatureUsage( + featureCode: string, + actionType: string, + detail?: string | null, +): void { + void (async () => { + try { + const res = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/feature-usage/log`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + featureCode, + actionType, + detail: detail == null || detail === "" ? null : detail, + }), + }); + if (!res.ok) { + console.warn("feature-usage log failed", res.status); + } + } catch (e) { + console.warn("feature-usage log error", e); + } + })(); +} + +export async function fetchFeatureUsageSummary(): Promise { + const res = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/feature-usage/summary`, { + method: "GET", + headers: { Accept: "application/json" }, + }); + if (!res.ok) { + throw new Error(`feature-usage summary ${res.status}`); + } + const body = (await res.json()) as { + data?: { + reportManagement?: Record[]; + truckRoutingSummary?: Record[]; + }; + }; + const d = body.data; + return { + reportManagement: (d?.reportManagement ?? []).map((r) => normalizeRow(r)), + truckRoutingSummary: (d?.truckRoutingSummary ?? []).map((r) => normalizeRow(r)), + }; +}