Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
import { API_PATHS } from "@/api/api-paths";
|
|
|
|
|
import { apiGet } from "@/api/api";
|
|
|
|
|
import { handleApiResponse } from "@/api/handle-api-response";
|
2026-09-16 19:37:15 -03:00
|
|
|
import type { Options } from "ky";
|
|
|
|
|
import type { DashboardRangeParams } from "@/domain/dashboard/types/dashboard-range";
|
|
|
|
|
import type {
|
|
|
|
|
DashboardBreakdownRow,
|
|
|
|
|
DashboardStats,
|
|
|
|
|
} from "@/domain/dashboard/types/dashboard-stats";
|
|
|
|
|
import type { DispatcherPerformanceRow } from "@/domain/dashboard/types/dashboard-performance";
|
|
|
|
|
import type { DashboardTrendParams } from "@/domain/dashboard/types/dashboard-trend";
|
|
|
|
|
import type {
|
|
|
|
|
DashboardTrend,
|
|
|
|
|
DashboardTrendGranularity,
|
|
|
|
|
} from "@/domain/dashboard/types/dashboard-trend";
|
|
|
|
|
import {
|
|
|
|
|
UNMAPPED_REGION_LABEL,
|
|
|
|
|
type RegionWorkOrdersRow,
|
|
|
|
|
} from "@/domain/dashboard/types/dashboard-regions";
|
|
|
|
|
import type { DispatcherWorkloadRow } from "@/domain/dashboard/types/dashboard-workload";
|
|
|
|
|
import type { VendorInsightRow } from "@/domain/dashboard/types/dashboard-vendor-insights";
|
2026-09-16 20:40:33 -03:00
|
|
|
import { WO_TYPES } from "@/domain/work-orders/types/work-order-wizard";
|
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
|
|
|
|
|
function toNumber(value: unknown): number {
|
|
|
|
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof value === "string" && value.trim() !== "") {
|
|
|
|
|
const parsed = Number(value);
|
|
|
|
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 19:37:15 -03:00
|
|
|
function asString(value: unknown): string {
|
|
|
|
|
if (typeof value === "string") {
|
|
|
|
|
return value.trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
|
|
|
return String(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function asRecords(value: unknown): Record<string, unknown>[] {
|
|
|
|
|
if (!Array.isArray(value)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value.filter(
|
|
|
|
|
(item): item is Record<string, unknown> =>
|
|
|
|
|
typeof item === "object" && item !== null && !Array.isArray(item),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readField(data: Record<string, unknown>, key: string): unknown {
|
|
|
|
|
const capitalized = key.charAt(0).toUpperCase() + key.slice(1);
|
|
|
|
|
const value = data[key] ?? data[capitalized];
|
|
|
|
|
return value === null ? undefined : value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toRecord(value: unknown): Record<string, unknown> {
|
|
|
|
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
|
|
|
? (value as Record<string, unknown>)
|
|
|
|
|
: {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readCollection(value: unknown, key: string): Record<string, unknown>[] {
|
|
|
|
|
const data = toRecord(value);
|
|
|
|
|
const collection = key ? (readField(data, key) ?? value) : value;
|
|
|
|
|
return asRecords(collection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function rangeSearchParams(range?: DashboardRangeParams): Options | undefined {
|
|
|
|
|
if (!range?.dateFrom || !range?.dateTo) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { searchParams: { dateFrom: range.dateFrom, dateTo: range.dateTo } };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function trendSearchParams(params?: DashboardTrendParams): Options | undefined {
|
|
|
|
|
if (
|
|
|
|
|
!params ||
|
|
|
|
|
(!params.dateFrom && !params.dateTo && !params.granularity && !params.range && !params.year)
|
|
|
|
|
) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const searchParams: Record<string, string | number> = {};
|
|
|
|
|
if (params.dateFrom) searchParams.dateFrom = params.dateFrom;
|
|
|
|
|
if (params.dateTo) searchParams.dateTo = params.dateTo;
|
|
|
|
|
if (params.granularity) searchParams.granularity = params.granularity;
|
|
|
|
|
if (params.range) searchParams.range = params.range;
|
|
|
|
|
if (params.year) searchParams.year = params.year;
|
|
|
|
|
|
|
|
|
|
return { searchParams };
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 20:40:33 -03:00
|
|
|
const BREAKDOWN_TYPE_BY_LOWER = new Map<string, string>(
|
|
|
|
|
WO_TYPES.map((type) => [type.toLowerCase(), type]),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The backend breakdown object (backend#138) serializes its keys with no
|
|
|
|
|
* JsonPropertyName, so `PM`/`Emergency`/`Reactive`/`Overdue`/`Other` arrive
|
|
|
|
|
* lowercase. The card uses the row label as both display text and the
|
|
|
|
|
* `types` drill-down id, and `workOrderTypeDrilldownSearch` only accepts
|
|
|
|
|
* canonical `WOType` values — so map lowercase type keys back to their
|
|
|
|
|
* canonical labels. `other` is not a WOType: keep it as a labelled,
|
|
|
|
|
* non-drillable "Other" row. Unknown values (e.g. lifecycle labels from the
|
|
|
|
|
* legacy array shape) pass through unchanged.
|
|
|
|
|
*/
|
|
|
|
|
function normalizeBreakdownStatus(raw: string): string {
|
|
|
|
|
const trimmed = raw.trim();
|
|
|
|
|
const canonical = BREAKDOWN_TYPE_BY_LOWER.get(trimmed.toLowerCase());
|
|
|
|
|
if (canonical) return canonical;
|
|
|
|
|
if (trimmed.toLowerCase() === "other") return "Other";
|
|
|
|
|
return trimmed;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 19:37:15 -03:00
|
|
|
function mapBreakdownRow(raw: Record<string, unknown>): DashboardBreakdownRow {
|
2026-09-16 20:40:33 -03:00
|
|
|
const status = asString(
|
|
|
|
|
readField(raw, "status") ?? readField(raw, "name") ?? readField(raw, "label"),
|
|
|
|
|
);
|
2026-09-16 19:37:15 -03:00
|
|
|
return {
|
2026-09-16 20:40:33 -03:00
|
|
|
status: status ? normalizeBreakdownStatus(status) : UNMAPPED_REGION_LABEL,
|
2026-09-16 19:37:15 -03:00
|
|
|
count: toNumber(readField(raw, "count") ?? readField(raw, "value")),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapBreakdown(value: unknown): DashboardBreakdownRow[] {
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
return asRecords(value).map(mapBreakdownRow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof value === "object" && value !== null) {
|
|
|
|
|
return Object.entries(value as Record<string, unknown>)
|
2026-09-16 20:40:33 -03:00
|
|
|
.map(([status, count]) => ({
|
|
|
|
|
status: normalizeBreakdownStatus(status),
|
|
|
|
|
count: toNumber(count),
|
|
|
|
|
}))
|
2026-09-16 19:37:15 -03:00
|
|
|
.filter((row) => row.status !== "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapDispatcherIdentity(raw: Record<string, unknown>): {
|
|
|
|
|
dispatcherId: string;
|
|
|
|
|
dispatcherName: string;
|
|
|
|
|
} {
|
|
|
|
|
return {
|
|
|
|
|
dispatcherId: asString(
|
|
|
|
|
readField(raw, "dispatcherId") ?? readField(raw, "id") ?? readField(raw, "userId"),
|
|
|
|
|
),
|
|
|
|
|
dispatcherName: asString(readField(raw, "dispatcherName") ?? readField(raw, "name")),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
function mapDashboardStats(raw: unknown): DashboardStats {
|
2026-09-16 19:37:15 -03:00
|
|
|
const data = toRecord(raw);
|
|
|
|
|
const average = readField(data, "averageResolutionDays");
|
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
|
|
|
|
|
return {
|
2026-09-16 19:37:15 -03:00
|
|
|
total: toNumber(readField(data, "total")),
|
|
|
|
|
open: toNumber(readField(data, "open")),
|
|
|
|
|
notDispatched: toNumber(readField(data, "notDispatched")),
|
|
|
|
|
completed: toNumber(readField(data, "completed")),
|
|
|
|
|
dueCount: toNumber(readField(data, "dueCount")),
|
|
|
|
|
completedDueCount: toNumber(readField(data, "completedDueCount")),
|
|
|
|
|
completionRate: toNumber(readField(data, "completionRate")),
|
|
|
|
|
averageResolutionDays: average == null ? null : toNumber(average),
|
|
|
|
|
scheduledTomorrow: toNumber(readField(data, "scheduledTomorrow")),
|
|
|
|
|
pendingUplifts: toNumber(readField(data, "pendingUplifts")),
|
|
|
|
|
avetaPending: toNumber(readField(data, "avetaPending")),
|
|
|
|
|
breakdown: mapBreakdown(readField(data, "breakdown") ?? readField(data, "workOrderBreakdown")),
|
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 19:37:15 -03:00
|
|
|
function mapDispatcherWorkload(raw: unknown): DispatcherWorkloadRow[] {
|
|
|
|
|
return readCollection(raw, "items").map((rawRow) => {
|
|
|
|
|
const identity = mapDispatcherIdentity(rawRow);
|
|
|
|
|
return {
|
|
|
|
|
...identity,
|
|
|
|
|
openWorkOrders: toNumber(
|
|
|
|
|
readField(rawRow, "openWorkOrders") ??
|
|
|
|
|
readField(rawRow, "openCount") ??
|
|
|
|
|
readField(rawRow, "open"),
|
|
|
|
|
),
|
|
|
|
|
totalWorkOrders: toNumber(
|
|
|
|
|
readField(rawRow, "totalWorkOrders") ??
|
|
|
|
|
readField(rawRow, "totalCount") ??
|
|
|
|
|
readField(rawRow, "scheduledWorkOrders") ??
|
|
|
|
|
readField(rawRow, "scheduled"),
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapDispatcherPerformance(raw: unknown): DispatcherPerformanceRow[] {
|
|
|
|
|
return readCollection(raw, "items").map((rawRow) => {
|
|
|
|
|
const identity = mapDispatcherIdentity(rawRow);
|
|
|
|
|
return {
|
|
|
|
|
...identity,
|
|
|
|
|
completionRate: toNumber(
|
|
|
|
|
readField(rawRow, "completionRate") ?? readField(rawRow, "onTimeRate"),
|
|
|
|
|
),
|
|
|
|
|
averageResolutionDays:
|
|
|
|
|
readField(rawRow, "averageResolutionDays") == null
|
|
|
|
|
? null
|
|
|
|
|
: toNumber(readField(rawRow, "averageResolutionDays")),
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapRegions(raw: unknown): RegionWorkOrdersRow[] {
|
|
|
|
|
return readCollection(raw, "items").map((rawRow) => {
|
|
|
|
|
const region =
|
|
|
|
|
asString(readField(rawRow, "region") ?? readField(rawRow, "name")) || UNMAPPED_REGION_LABEL;
|
|
|
|
|
return {
|
|
|
|
|
region,
|
|
|
|
|
workOrderCount: toNumber(
|
|
|
|
|
readField(rawRow, "workOrderCount") ??
|
|
|
|
|
readField(rawRow, "count") ??
|
|
|
|
|
readField(rawRow, "total"),
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapVendorInsights(raw: unknown): VendorInsightRow[] {
|
|
|
|
|
return readCollection(raw, "items").map((rawRow) => ({
|
|
|
|
|
vendorId: asString(
|
|
|
|
|
readField(rawRow, "vendorId") ??
|
|
|
|
|
readField(rawRow, "vendorCompanyId") ??
|
|
|
|
|
readField(rawRow, "id"),
|
|
|
|
|
),
|
|
|
|
|
vendorName: asString(
|
|
|
|
|
readField(rawRow, "vendorName") ??
|
|
|
|
|
readField(rawRow, "vendorCompanyName") ??
|
|
|
|
|
readField(rawRow, "companyName") ??
|
|
|
|
|
readField(rawRow, "name"),
|
|
|
|
|
),
|
|
|
|
|
completionRate: toNumber(readField(rawRow, "completionRate")),
|
|
|
|
|
rescheduleRate: toNumber(readField(rawRow, "rescheduleRate")),
|
|
|
|
|
averageResolutionDays:
|
|
|
|
|
readField(rawRow, "averageResolutionDays") == null
|
|
|
|
|
? null
|
|
|
|
|
: toNumber(readField(rawRow, "averageResolutionDays")),
|
|
|
|
|
totalJobs: toNumber(readField(rawRow, "totalJobs")),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapTrendGranularity(value: unknown): DashboardTrendGranularity | null {
|
|
|
|
|
const granularity = asString(value).toLowerCase();
|
|
|
|
|
return granularity === "day" || granularity === "week" || granularity === "month"
|
|
|
|
|
? granularity
|
|
|
|
|
: null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapDashboardTrend(raw: unknown): DashboardTrend {
|
|
|
|
|
const data = toRecord(raw);
|
|
|
|
|
const buckets = readCollection(data, "buckets");
|
|
|
|
|
const legacyPoints = readCollection(data, "points");
|
|
|
|
|
const points = (buckets.length > 0 ? buckets : legacyPoints).map((rawPoint) => ({
|
|
|
|
|
date: asString(readField(rawPoint, "date")),
|
|
|
|
|
label: asString(
|
|
|
|
|
readField(rawPoint, "label") ?? readField(rawPoint, "period") ?? readField(rawPoint, "date"),
|
|
|
|
|
),
|
|
|
|
|
total: toNumber(
|
|
|
|
|
readField(rawPoint, "total") ?? readField(rawPoint, "count") ?? readField(rawPoint, "value"),
|
|
|
|
|
),
|
|
|
|
|
open: toNumber(readField(rawPoint, "open")),
|
|
|
|
|
completed: toNumber(readField(rawPoint, "completed")),
|
|
|
|
|
canceled: toNumber(readField(rawPoint, "canceled")),
|
|
|
|
|
overdue: toNumber(readField(rawPoint, "overdue")),
|
|
|
|
|
isCurrent: readField(rawPoint, "isCurrent") === true,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
return { granularity: mapTrendGranularity(readField(data, "granularity")), points };
|
|
|
|
|
}
|
|
|
|
|
|
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
export const dashboardApi = {
|
2026-09-16 19:37:15 -03:00
|
|
|
getStats: async (range?: DashboardRangeParams): Promise<DashboardStats> => {
|
|
|
|
|
const data = await apiGet<unknown>(API_PATHS.dashboard.stats, rangeSearchParams(range));
|
|
|
|
|
return mapDashboardStats(handleApiResponse<unknown>(data));
|
|
|
|
|
},
|
|
|
|
|
getWorkload: async (range?: DashboardRangeParams): Promise<DispatcherWorkloadRow[]> => {
|
|
|
|
|
const data = await apiGet<unknown>(API_PATHS.dashboard.workload, rangeSearchParams(range));
|
|
|
|
|
return mapDispatcherWorkload(handleApiResponse<unknown>(data));
|
|
|
|
|
},
|
|
|
|
|
getPerformance: async (range?: DashboardRangeParams): Promise<DispatcherPerformanceRow[]> => {
|
|
|
|
|
const data = await apiGet<unknown>(API_PATHS.dashboard.performance, rangeSearchParams(range));
|
|
|
|
|
return mapDispatcherPerformance(handleApiResponse<unknown>(data));
|
|
|
|
|
},
|
|
|
|
|
getRegions: async (range?: DashboardRangeParams): Promise<RegionWorkOrdersRow[]> => {
|
|
|
|
|
const data = await apiGet<unknown>(API_PATHS.dashboard.regions, rangeSearchParams(range));
|
|
|
|
|
return mapRegions(handleApiResponse<unknown>(data));
|
|
|
|
|
},
|
|
|
|
|
getVendorInsights: async (): Promise<VendorInsightRow[]> => {
|
|
|
|
|
const data = await apiGet<unknown>(API_PATHS.dashboard.vendorInsights);
|
|
|
|
|
return mapVendorInsights(handleApiResponse<unknown>(data));
|
|
|
|
|
},
|
|
|
|
|
getTrend: async (params?: DashboardTrendParams): Promise<DashboardTrend> => {
|
|
|
|
|
const data = await apiGet<unknown>(API_PATHS.dashboard.trend, trendSearchParams(params));
|
|
|
|
|
return mapDashboardTrend(handleApiResponse<unknown>(data));
|
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling
* ci: add GitHub Actions workflow for lint and build
* build: migrate from CRA to Vite with TypeScript config
* docs: add architecture plan and design system documentation
* fix: scope ESLint to new components and hooks directories
* feat: add HTTP client, query cache and shared utilities
* feat: add theme system and global application styles
* feat: add shared UI, layout and domain badge components
* feat: add auth domain, provider and login page
* feat: add app shell, file-based routing and bootstrap
* feat: add protected layout and dashboard module
* feat: add accounts CRUD module
* feat: add assets CRUD module
* feat: add contacts CRUD module
* feat: add employees CRUD module
* feat: add locations CRUD module
* feat: add calendar events module
* feat: add follow-ups CRUD module
* feat: add PM schedules CRUD module
* feat: add work orders module with dispatch modals
* feat: add vendors CRUD and portal token panel
* feat: add vendor purchase orders module
* feat: add uplifts queue module
* feat: add settings for dropdowns and task templates
* feat: add vendor portal routes and signature capture
* test: add Vitest setup and Playwright login e2e spec
* chore: remove legacy CRA pages, Redux store and JS hooks
* chore: update gitignore and env example for Vite
* docs: translate pt-BR docs and Cursor rules to English
* fix(ci): fix login e2e session mock and prettier formatting
* fix(build): use mjs router script for Node 20 CI compatibility
* chore: remove migration scripts and unused generouted router
* fix(auth): restore JWT and align CRUD with backend routes
* fix(api): add no-content helpers and align paths with backend
* fix(accounts): align detail and mutation payloads with backend
* fix(contacts): resolve detail via GetContacts and map address DTOs
* fix(work-orders): align routes, delete body, and create payload
* fix(vendors): delete vendors via REST route
* fix(pm-schedules): handle empty save and delete responses
* fix(employees): add fallbacks for detail and dropdown calls
* chore(calendar): disable routes until backend exists
* chore(env): switch tracked env vars to Vite prefixes
* fix(api): align frontend contracts with backend review findings
Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.
* fix(employees,pm-schedules): align forms with backend API contracts
Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.
* fix(pm-schedules): gate edit/delete for Dev API contract
Production Dev API exposes only GetList and Save.
Hide unsupported edit/delete UI and block the edit route.
Add regression tests for disabled actions.
* docs(env): document VITE_API_URL must include /api suffix
* refactor(api): extract shared API prefix URL resolution
* feat(build): fail build on misconfigured absolute VITE_API_URL
* test(api): cover API URL contract and prefix resolution
* feat(auth): disable login submit until email and password are valid
* test(auth): align login tests with disabled submit behavior
* Feat/ab/menu-and-header (#17)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
* ci: add frontend PR quality baseline (#18)
* chore(deps): add lucide-react for layout icon migration
* feat(auth): add getPrimaryUserRole helper for header display
* style(theme): add sidebar active tokens and nav group typography
* refactor(menu): migrate nav icons to lucide and trim menu groups
* feat(layout): redesign sidebar, topbar, and admin shell viewport
* chore(menu): hide Reports and Documents from sidebar
* ci: add PR quality baseline checks
* ci: avoid self-matching standards guard
* ci: split frontend quality gates
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
---------
Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00
|
|
|
},
|
|
|
|
|
};
|