Files

164 lines
4.7 KiB
TypeScript
Raw Permalink Normal View History

import { apiFetch, type PaginatedResponse } from './api';
export interface ContactPersonResponse {
id: string;
contact_id: string;
name: string;
function?: string;
phone?: string;
email?: string;
created_at?: string;
}
export interface ContactResponse {
id: string;
company_name: string;
legal_form?: string;
address_street?: string;
address_zip?: string;
address_city?: string;
address_country: string;
vat_id?: string;
phone?: string;
email?: string;
website?: string;
role: string;
vat_id_status: string;
is_private: boolean;
created_at?: string;
updated_at?: string;
deleted_at?: string | null;
contact_persons: ContactPersonResponse[];
}
export interface ContactCreateData {
company_name: string;
legal_form?: string;
address_street?: string;
address_zip?: string;
address_city?: string;
address_country: string;
vat_id?: string;
phone?: string;
email?: string;
website?: string;
role: string;
is_private?: boolean;
contact_persons?: ContactPersonCreateData[];
}
export interface ContactUpdateData {
company_name?: string;
legal_form?: string;
address_street?: string;
address_zip?: string;
address_city?: string;
address_country?: string;
vat_id?: string;
phone?: string;
email?: string;
website?: string;
role?: string;
is_private?: boolean;
}
export interface ContactPersonCreateData {
name: string;
function?: string;
phone?: string;
email?: string;
}
export interface ContactListParams {
page?: number;
page_size?: number;
search?: string;
role?: string;
is_eu?: boolean;
is_private?: boolean;
sort?: string;
}
export async function listContacts(params: ContactListParams = {}): Promise<PaginatedResponse<ContactResponse>> {
const query = new URLSearchParams();
if (params.page) query.set('page', String(params.page));
if (params.page_size) query.set('page_size', String(params.page_size));
if (params.search) query.set('search', params.search);
if (params.role) query.set('role', params.role);
if (params.is_eu !== undefined) query.set('is_eu', String(params.is_eu));
if (params.is_private !== undefined) query.set('is_private', String(params.is_private));
if (params.sort) query.set('sort', params.sort);
const qs = query.toString();
return apiFetch<PaginatedResponse<ContactResponse>>(`/contacts/${qs ? `?${qs}` : ''}`);
}
export async function getContact(id: string): Promise<ContactResponse> {
return apiFetch<ContactResponse>(`/contacts/${id}`);
}
export async function createContact(data: ContactCreateData): Promise<ContactResponse> {
return apiFetch<ContactResponse>('/contacts/', {
method: 'POST',
body: JSON.stringify(data),
});
}
export async function updateContact(id: string, data: ContactUpdateData): Promise<ContactResponse> {
return apiFetch<ContactResponse>(`/contacts/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
});
}
export async function deleteContact(id: string): Promise<ContactResponse> {
return apiFetch<ContactResponse>(`/contacts/${id}`, { method: 'DELETE' });
}
export async function addContactPerson(contactId: string, data: ContactPersonCreateData): Promise<ContactPersonResponse> {
return apiFetch<ContactPersonResponse>(`/contacts/${contactId}/persons`, {
method: 'POST',
body: JSON.stringify(data),
});
}
export async function removeContactPerson(contactId: string, personId: string): Promise<void> {
await apiFetch<void>(`/contacts/${contactId}/persons/${personId}`, { method: 'DELETE' });
}
/**
* Validate VAT ID format on the frontend (mirrors backend ust_validation.py).
* Returns error message or null if valid.
*/
export function validateVatIdFormat(vatId: string, countryCode: string): string | null {
if (!vatId) return null;
const normalized = vatId.trim().toUpperCase().replace(/\s+/g, '');
const patterns: Record<string, RegExp> = {
DE: /^DE\d{9}$/,
AT: /^ATU\d{8}$/,
FR: /^FR[A-Za-z0-9]{2}\d{9}$/,
NL: /^NL\d{9}B\d{2}$/,
PL: /^PL\d{10}$/,
CZ: /^CZ\d{8,10}$/,
IT: /^IT\d{11}$/,
ES: /^ES[A-Za-z0-9]\d{7}[A-Za-z0-9]$/,
BE: /^BE\d{10}$/,
DK: /^DK\d{8}$/,
SE: /^SE\d{10}$/,
};
const euCountries = new Set([
'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR',
'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO',
'SE', 'SI', 'SK',
]);
const cc = normalized.substring(0, 2);
if (!cc.match(/^[A-Z]{2}$/)) return 'Invalid country code';
const pattern = patterns[cc];
if (pattern) {
return pattern.test(normalized) ? null : `Invalid VAT ID format for ${cc}`;
}
if (euCountries.has(cc)) {
return /^[A-Z]{2}[A-Za-z0-9]{5,15}$/.test(normalized) ? null : `Invalid VAT ID format for ${cc}`;
}
return `VAT ID validation not supported for country ${cc}`;
}