- Create frontend/src/lib/shared/ with formatters, userUtils, paginationUtils (formatDate, formatMoney, displayUserName, userProfileLink, extractNextPage, loadAllPages) - Create frontend/src/components/shared/SearchableSelect.tsx (SearchableSelectSingle + SearchableSelectMulti, replaces 3 inline copies) - Extract requests page data/state/actions into useRequestsPage hook (requests/page.tsx: 1580 → ~280 lines of pure JSX) - Replace duplicated helpers in equipment/page.tsx with shared imports (~80 lines removed: formatDate, formatMoney, displayUserName, extractNextPage, loadAllPages, inline SearchableSelectSingle) - Replace duplicated helpers in procurement/page.tsx with shared imports (~90 lines removed: fmt, money, displayUserName, extractNextPage, inline departments loader, inline SearchableSelectSingle) - 609 backend tests pass, TypeScript typecheck clean, ESLint clean
22 lines
700 B
TypeScript
22 lines
700 B
TypeScript
/**
|
|
* Shared formatters used across list pages (requests, equipment, procurement).
|
|
*/
|
|
|
|
export function formatDate(value?: string | null): string {
|
|
if (!value) return "";
|
|
const date = new Date(value);
|
|
if (Number.isNaN(date.getTime())) return "";
|
|
return date.toLocaleDateString("ru-RU", {
|
|
day: "2-digit",
|
|
month: "2-digit",
|
|
year: "numeric",
|
|
});
|
|
}
|
|
|
|
export function formatMoney(value?: string | number | null): string {
|
|
if (value === null || value === undefined || value === "") return "—";
|
|
const amount = Number(value);
|
|
if (!Number.isFinite(amount)) return "—";
|
|
return `${amount.toLocaleString("ru-RU", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ₽`;
|
|
}
|