| @@ -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<string, unknown>): 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<FeatureUsageSummaryPayload> { | |||
| 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<string, unknown>[]; | |||
| truckRoutingSummary?: Record<string, unknown>[]; | |||
| }; | |||
| }; | |||
| const d = body.data; | |||
| return { | |||
| reportManagement: (d?.reportManagement ?? []).map((r) => normalizeRow(r)), | |||
| truckRoutingSummary: (d?.truckRoutingSummary ?? []).map((r) => normalizeRow(r)), | |||
| }; | |||
| } | |||