fix(number-fields): избегаем потери точности длинных чисел

- number-поля теперь рендерятся как text + inputMode=numeric,
  чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage

Closes: искажение расчётного счёта и других длинных числовых полей
This commit is contained in:
2026-07-07 21:03:40 +03:00
commit 1f5ecb6da4
1089 changed files with 237546 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
import { createContext, useContext } from 'react';
import { useOfflineSync } from '@/hooks/useOfflineSync';
import type { SyncStatus } from '@/hooks/useOfflineSync';
import type { OfflineQueueItem } from '@/lib/offlineQueue';
import type { AttachmentQueueItem } from '@/lib/attachmentQueue';
import { useAuth } from '@/hooks/useAuth';
interface OfflineSyncContextValue {
isOnline: boolean;
isVisible: boolean;
pendingCount: number;
pendingChatCount: number;
pendingAttachmentCount: number;
pendingUrls: Set<string>;
pendingItems: OfflineQueueItem[];
pendingAttachments: AttachmentQueueItem[];
isPending: (url: string) => boolean;
getPendingBody: (url: string) => Record<string, unknown> | null;
status: SyncStatus;
conflicts: OfflineQueueItem[];
attachmentStatus: SyncStatus;
attachmentConflicts: AttachmentQueueItem[];
sync: () => Promise<void>;
resolveConflict: (id: string, resolution: 'force' | 'discard') => Promise<void>;
retryAttachment: (id: string) => Promise<void>;
discardAttachment: (id: string) => Promise<void>;
}
const OfflineSyncContext = createContext<OfflineSyncContextValue | null>(null);
export function OfflineSyncProvider({ children }: { children: React.ReactNode }) {
const { user } = useAuth();
const value = useOfflineSync(user?.id);
return (
<OfflineSyncContext.Provider value={value}>
{children}
</OfflineSyncContext.Provider>
);
}
export function useOfflineSyncContext(): OfflineSyncContextValue {
const ctx = useContext(OfflineSyncContext);
if (!ctx) {
throw new Error('useOfflineSyncContext must be used inside OfflineSyncProvider');
}
return ctx;
}