FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

107 строки
3.0 KiB

  1. import { NEXT_PUBLIC_API_URL } from "@/config/api";
  2. import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
  3. export const FEATURE_USAGE = {
  4. REPORT_MANAGEMENT: "REPORT_MANAGEMENT",
  5. TRUCK_ROUTING_SUMMARY: "TRUCK_ROUTING_SUMMARY",
  6. } as const;
  7. export const FEATURE_USAGE_ACTION = {
  8. PAGE_VIEW: "PAGE_VIEW",
  9. DOWNLOAD: "DOWNLOAD",
  10. PRINT: "PRINT",
  11. } as const;
  12. export type FeatureUsageSummaryRow = {
  13. userId: number;
  14. username: string;
  15. pageViews: number;
  16. downloads: number;
  17. prints: number;
  18. };
  19. export type FeatureUsageSummaryPayload = {
  20. reportManagement: FeatureUsageSummaryRow[];
  21. truckRoutingSummary: FeatureUsageSummaryRow[];
  22. };
  23. export type FeatureUsageSummaryFilters = {
  24. startDate?: string;
  25. endDate?: string;
  26. };
  27. function toInt(v: unknown): number {
  28. if (v == null) return 0;
  29. if (typeof v === "number") return Number.isFinite(v) ? v : 0;
  30. const n = Number(v);
  31. return Number.isFinite(n) ? n : 0;
  32. }
  33. function normalizeRow(raw: Record<string, unknown>): FeatureUsageSummaryRow {
  34. return {
  35. userId: toInt(raw.userId ?? raw.USER_ID),
  36. username: String(raw.username ?? raw.USERNAME ?? ""),
  37. pageViews: toInt(raw.pageViews ?? raw.PAGEVIEWS),
  38. downloads: toInt(raw.downloads ?? raw.DOWNLOADS),
  39. prints: toInt(raw.prints ?? raw.PRINTS),
  40. };
  41. }
  42. /** Fire-and-forget client log; failures are ignored (console only). */
  43. export function logFeatureUsage(
  44. featureCode: string,
  45. actionType: string,
  46. detail?: string | null,
  47. ): void {
  48. void (async () => {
  49. try {
  50. const res = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/feature-usage/log`, {
  51. method: "POST",
  52. headers: { "Content-Type": "application/json" },
  53. body: JSON.stringify({
  54. featureCode,
  55. actionType,
  56. detail: detail == null || detail === "" ? null : detail,
  57. }),
  58. });
  59. if (!res.ok) {
  60. console.warn("feature-usage log failed", res.status);
  61. }
  62. } catch (e) {
  63. console.warn("feature-usage log error", e);
  64. }
  65. })();
  66. }
  67. export async function fetchFeatureUsageSummary(
  68. filters?: FeatureUsageSummaryFilters,
  69. ): Promise<FeatureUsageSummaryPayload> {
  70. const params = new URLSearchParams();
  71. if (filters?.startDate) {
  72. params.set("startDate", filters.startDate);
  73. }
  74. if (filters?.endDate) {
  75. params.set("endDate", filters.endDate);
  76. }
  77. const query = params.toString();
  78. const url = `${NEXT_PUBLIC_API_URL}/feature-usage/summary${query ? `?${query}` : ""}`;
  79. const res = await clientAuthFetch(url, {
  80. method: "GET",
  81. headers: { Accept: "application/json" },
  82. });
  83. if (!res.ok) {
  84. throw new Error(`feature-usage summary ${res.status}`);
  85. }
  86. const body = (await res.json()) as {
  87. data?: {
  88. reportManagement?: Record<string, unknown>[];
  89. truckRoutingSummary?: Record<string, unknown>[];
  90. };
  91. };
  92. const d = body.data;
  93. return {
  94. reportManagement: (d?.reportManagement ?? []).map((r) => normalizeRow(r)),
  95. truckRoutingSummary: (d?.truckRoutingSummary ?? []).map((r) => normalizeRow(r)),
  96. };
  97. }