fix(registratura): prevent duplicate numbers, add upload progress, submission lock, unified close/resolve, backdating support
- generateRegistryNumber: parse max existing number instead of counting entries - addEntry: fetch fresh entries before generating number (race condition fix) - Form: isSubmitting lock prevents double-click submission - Form: uploadingCount tracks FileReader progress, blocks submit while uploading - Form: submit button shows Loader2 spinner during save/upload - CloseGuardDialog: added ClosureResolution selector (finalizat/aprobat-tacit/respins/retras/altele) - ClosureBanner: displays resolution badge - Types: ClosureResolution type, registrationDate field on RegistryEntry - Date field renamed 'Data document' with tooltip explaining backdating - Registry table shows '(înr. DATE)' when registrationDate differs from document date
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { CompanyId } from '@/core/auth/types';
|
||||
import type { RegistryEntry } from '../types';
|
||||
import type { CompanyId } from "@/core/auth/types";
|
||||
import type { RegistryEntry } from "../types";
|
||||
|
||||
const STORAGE_PREFIX = 'entry:';
|
||||
const STORAGE_PREFIX = "entry:";
|
||||
|
||||
export interface RegistryStorage {
|
||||
get<T>(key: string): Promise<T | null>;
|
||||
@@ -10,7 +10,9 @@ export interface RegistryStorage {
|
||||
list(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export async function getAllEntries(storage: RegistryStorage): Promise<RegistryEntry[]> {
|
||||
export async function getAllEntries(
|
||||
storage: RegistryStorage,
|
||||
): Promise<RegistryEntry[]> {
|
||||
const keys = await storage.list();
|
||||
const entries: RegistryEntry[] = [];
|
||||
for (const key of keys) {
|
||||
@@ -23,42 +25,56 @@ export async function getAllEntries(storage: RegistryStorage): Promise<RegistryE
|
||||
return entries;
|
||||
}
|
||||
|
||||
export async function saveEntry(storage: RegistryStorage, entry: RegistryEntry): Promise<void> {
|
||||
export async function saveEntry(
|
||||
storage: RegistryStorage,
|
||||
entry: RegistryEntry,
|
||||
): Promise<void> {
|
||||
await storage.set(`${STORAGE_PREFIX}${entry.id}`, entry);
|
||||
}
|
||||
|
||||
export async function deleteEntry(storage: RegistryStorage, id: string): Promise<void> {
|
||||
export async function deleteEntry(
|
||||
storage: RegistryStorage,
|
||||
id: string,
|
||||
): Promise<void> {
|
||||
await storage.delete(`${STORAGE_PREFIX}${id}`);
|
||||
}
|
||||
|
||||
const COMPANY_PREFIXES: Record<CompanyId, string> = {
|
||||
beletage: 'B',
|
||||
'urban-switch': 'US',
|
||||
'studii-de-teren': 'SDT',
|
||||
group: 'G',
|
||||
beletage: "B",
|
||||
"urban-switch": "US",
|
||||
"studii-de-teren": "SDT",
|
||||
group: "G",
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate company-specific registry number: B-0001/2026
|
||||
* Uses the next sequential number for that company in that year.
|
||||
* Uses the highest existing number + 1 for that company in that year.
|
||||
* Parses actual numbers from entries to prevent duplicates.
|
||||
*/
|
||||
export function generateRegistryNumber(
|
||||
company: CompanyId,
|
||||
date: string,
|
||||
existingEntries: RegistryEntry[]
|
||||
_date: string,
|
||||
existingEntries: RegistryEntry[],
|
||||
): string {
|
||||
const d = new Date(date);
|
||||
const year = d.getFullYear();
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const prefix = COMPANY_PREFIXES[company];
|
||||
|
||||
// Count existing entries for this company in this year
|
||||
const sameCompanyYear = existingEntries.filter((e) => {
|
||||
const entryYear = new Date(e.date).getFullYear();
|
||||
return e.company === company && entryYear === year;
|
||||
});
|
||||
// Parse the numeric part from existing numbers for this company+year
|
||||
// Pattern: PREFIX-NNNN/YYYY
|
||||
const regex = new RegExp(`^${prefix}-(\\d+)/${year}$`);
|
||||
let maxNum = 0;
|
||||
|
||||
const nextIndex = sameCompanyYear.length + 1;
|
||||
const padded = String(nextIndex).padStart(4, '0');
|
||||
for (const e of existingEntries) {
|
||||
const match = e.number.match(regex);
|
||||
if (match?.[1]) {
|
||||
const num = parseInt(match[1], 10);
|
||||
if (num > maxNum) maxNum = num;
|
||||
}
|
||||
}
|
||||
|
||||
const nextIndex = maxNum + 1;
|
||||
const padded = String(nextIndex).padStart(4, "0");
|
||||
return `${prefix}-${padded}/${year}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user