opencrm/frontend/src/services/api.ts

1183 lines
44 KiB
TypeScript

import axios from 'axios';
import type { ApiResponse, Customer, Contract, ContractTask, ContractTaskSubtask, ContractTaskStatus, SalesPlatform, CancellationPeriod, ContractDuration, ContractCategory, Provider, Tariff, User, Address, BankCard, IdentityDocument, Meter, MeterReading, Invoice, Role, PortalSettings, CustomerRepresentative, CustomerSummary } from '../types';
const api = axios.create({
baseURL: '/api',
headers: {
'Content-Type': 'application/json',
},
});
// Add auth token to requests
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Handle auth errors and extract error messages
api.interceptors.response.use(
(response) => response,
(error) => {
// Bei 401 nur dann zur Login-Seite umleiten, wenn wir NICHT gerade auf der Login-Seite sind
// Login-Endpunkte ausschließen, da 401 dort "falsches Passwort" bedeutet
const isLoginEndpoint = error.config?.url?.includes('/auth/login') ||
error.config?.url?.includes('/auth/customer-login');
if (error.response?.status === 401 && !isLoginEndpoint) {
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/login';
}
// Extract error message from response
const message = error.response?.data?.error || error.message || 'Ein Fehler ist aufgetreten';
const enhancedError = new Error(message);
return Promise.reject(enhancedError);
}
);
// Auth
export const authApi = {
login: async (email: string, password: string) => {
const res = await api.post<ApiResponse<{ token: string; user: User }>>('/auth/login', { email, password });
return res.data;
},
customerLogin: async (email: string, password: string) => {
const res = await api.post<ApiResponse<{ token: string; user: User }>>('/auth/customer-login', { email, password });
return res.data;
},
me: async () => {
const res = await api.get<ApiResponse<User>>('/auth/me');
return res.data;
},
};
// Customers
export const customerApi = {
getAll: async (params?: { search?: string; type?: string; page?: number; limit?: number }) => {
const res = await api.get<ApiResponse<Customer[]>>('/customers', { params });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<Customer>>(`/customers/${id}`);
return res.data;
},
create: async (data: Partial<Customer>) => {
const res = await api.post<ApiResponse<Customer>>('/customers', data);
return res.data;
},
update: async (id: number, data: Partial<Customer>) => {
const res = await api.put<ApiResponse<Customer>>(`/customers/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/customers/${id}`);
return res.data;
},
// Portal-Einstellungen
getPortalSettings: async (customerId: number) => {
const res = await api.get<ApiResponse<PortalSettings>>(`/customers/${customerId}/portal`);
return res.data;
},
updatePortalSettings: async (customerId: number, data: { portalEnabled?: boolean; portalEmail?: string | null }) => {
const res = await api.put<ApiResponse<PortalSettings>>(`/customers/${customerId}/portal`, data);
return res.data;
},
setPortalPassword: async (customerId: number, password: string) => {
const res = await api.post<ApiResponse<void>>(`/customers/${customerId}/portal/password`, { password });
return res.data;
},
getPortalPassword: async (customerId: number) => {
const res = await api.get<ApiResponse<{ password: string | null }>>(`/customers/${customerId}/portal/password`);
return res.data;
},
// Vertreter-Verwaltung
getRepresentatives: async (customerId: number) => {
const res = await api.get<ApiResponse<CustomerRepresentative[]>>(`/customers/${customerId}/representatives`);
return res.data;
},
addRepresentative: async (customerId: number, representativeId: number, notes?: string) => {
const res = await api.post<ApiResponse<CustomerRepresentative>>(`/customers/${customerId}/representatives`, { representativeId, notes });
return res.data;
},
removeRepresentative: async (customerId: number, representativeId: number) => {
const res = await api.delete<ApiResponse<void>>(`/customers/${customerId}/representatives/${representativeId}`);
return res.data;
},
searchForRepresentative: async (customerId: number, search: string) => {
const res = await api.get<ApiResponse<CustomerSummary[]>>(`/customers/${customerId}/representatives/search`, { params: { search } });
return res.data;
},
};
// Addresses
export const addressApi = {
getByCustomer: async (customerId: number) => {
const res = await api.get<ApiResponse<Address[]>>(`/customers/${customerId}/addresses`);
return res.data;
},
create: async (customerId: number, data: Partial<Address>) => {
const res = await api.post<ApiResponse<Address>>(`/customers/${customerId}/addresses`, data);
return res.data;
},
update: async (id: number, data: Partial<Address>) => {
const res = await api.put<ApiResponse<Address>>(`/addresses/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/addresses/${id}`);
return res.data;
},
};
// Bank Cards
export const bankCardApi = {
getByCustomer: async (customerId: number, showInactive = false) => {
const res = await api.get<ApiResponse<BankCard[]>>(`/customers/${customerId}/bank-cards`, { params: { showInactive } });
return res.data;
},
create: async (customerId: number, data: Partial<BankCard>) => {
const res = await api.post<ApiResponse<BankCard>>(`/customers/${customerId}/bank-cards`, data);
return res.data;
},
update: async (id: number, data: Partial<BankCard>) => {
const res = await api.put<ApiResponse<BankCard>>(`/bank-cards/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/bank-cards/${id}`);
return res.data;
},
};
// Identity Documents
export const documentApi = {
getByCustomer: async (customerId: number, showInactive = false) => {
const res = await api.get<ApiResponse<IdentityDocument[]>>(`/customers/${customerId}/documents`, { params: { showInactive } });
return res.data;
},
create: async (customerId: number, data: Partial<IdentityDocument>) => {
const res = await api.post<ApiResponse<IdentityDocument>>(`/customers/${customerId}/documents`, data);
return res.data;
},
update: async (id: number, data: Partial<IdentityDocument>) => {
const res = await api.put<ApiResponse<IdentityDocument>>(`/documents/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/documents/${id}`);
return res.data;
},
};
// Meters
export const meterApi = {
getByCustomer: async (customerId: number, showInactive = false) => {
const res = await api.get<ApiResponse<Meter[]>>(`/customers/${customerId}/meters`, { params: { showInactive } });
return res.data;
},
create: async (customerId: number, data: Partial<Meter>) => {
const res = await api.post<ApiResponse<Meter>>(`/customers/${customerId}/meters`, data);
return res.data;
},
update: async (id: number, data: Partial<Meter>) => {
const res = await api.put<ApiResponse<Meter>>(`/meters/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/meters/${id}`);
return res.data;
},
getReadings: async (meterId: number) => {
const res = await api.get<ApiResponse<MeterReading[]>>(`/meters/${meterId}/readings`);
return res.data;
},
addReading: async (meterId: number, data: Partial<MeterReading>) => {
const res = await api.post<ApiResponse<MeterReading>>(`/meters/${meterId}/readings`, data);
return res.data;
},
updateReading: async (meterId: number, readingId: number, data: Partial<MeterReading>) => {
const res = await api.put<ApiResponse<MeterReading>>(`/meters/${meterId}/readings/${readingId}`, data);
return res.data;
},
deleteReading: async (meterId: number, readingId: number) => {
const res = await api.delete<ApiResponse<void>>(`/meters/${meterId}/readings/${readingId}`);
return res.data;
},
};
// Invoice API
export const invoiceApi = {
getInvoices: async (ecdId: number) => {
const res = await api.get<ApiResponse<Invoice[]>>(`/energy-details/${ecdId}/invoices`);
return res.data;
},
addInvoice: async (ecdId: number, data: Partial<Invoice>) => {
const res = await api.post<ApiResponse<Invoice>>(`/energy-details/${ecdId}/invoices`, data);
return res.data;
},
updateInvoice: async (ecdId: number, invoiceId: number, data: Partial<Invoice>) => {
const res = await api.put<ApiResponse<Invoice>>(`/energy-details/${ecdId}/invoices/${invoiceId}`, data);
return res.data;
},
deleteInvoice: async (ecdId: number, invoiceId: number) => {
const res = await api.delete<ApiResponse<void>>(`/energy-details/${ecdId}/invoices/${invoiceId}`);
return res.data;
},
uploadDocument: async (invoiceId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(
`/upload/invoices/${invoiceId}`,
formData,
{ headers: { 'Content-Type': 'multipart/form-data' } }
);
return res.data;
},
deleteDocument: async (invoiceId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/invoices/${invoiceId}`);
return res.data;
},
};
// Stressfrei-Wechseln E-Mail-Adressen
export interface StressfreiEmail {
id: number;
customerId: number;
email: string;
platform?: string;
notes?: string;
isActive: boolean;
hasMailbox: boolean;
createdAt: string;
updatedAt: string;
}
// Mailbox-Konto (für Dropdown-Auswahl)
export interface MailboxAccount {
id: number;
email: string;
notes?: string;
hasMailbox: boolean;
_count: {
cachedEmails: number;
};
}
// Gecachte E-Mail
export interface CachedEmail {
id: number;
stressfreiEmailId: number;
folder: 'INBOX' | 'SENT';
messageId: string;
uid: number;
subject?: string;
fromAddress: string;
fromName?: string;
toAddresses: string; // JSON Array
ccAddresses?: string; // JSON Array
receivedAt: string;
textBody?: string;
htmlBody?: string;
hasAttachments: boolean;
attachmentNames?: string; // JSON Array
contractId?: number;
assignedAt?: string;
assignedBy?: number;
isAutoAssigned?: boolean; // true = automatisch beim Senden aus Vertrag zugeordnet
isRead: boolean;
isStarred: boolean;
isDeleted: boolean;
deletedAt?: string;
createdAt: string;
updatedAt: string;
stressfreiEmail?: {
id: number;
email: string;
customerId: number;
};
contract?: {
id: number;
contractNumber: string;
} | null;
}
export interface SyncResult {
newEmails: number;
totalEmails: number;
}
export interface EmailAttachment {
filename: string;
content: string; // Base64-kodierter Inhalt
contentType?: string; // MIME-Type (z.B. 'application/pdf')
}
export interface SendEmailParams {
to: string | string[];
cc?: string | string[];
subject: string;
text?: string;
html?: string;
inReplyTo?: string;
references?: string[];
attachments?: EmailAttachment[];
contractId?: number; // Vertrag dem die gesendete E-Mail zugeordnet wird
}
// Anhang-Speicher-Ziele
export interface AttachmentTargetSlot {
key: string;
label: string;
field: string;
hasDocument: boolean;
currentPath?: string;
}
export interface AttachmentEntityWithSlots {
id: number;
label: string;
slots: AttachmentTargetSlot[];
}
export interface AttachmentTargetsResponse {
customer: {
id: number;
name: string;
type: 'PRIVATE' | 'BUSINESS';
slots: AttachmentTargetSlot[];
};
identityDocuments: AttachmentEntityWithSlots[];
bankCards: AttachmentEntityWithSlots[];
contract?: {
id: number;
contractNumber: string;
type: string;
energyDetailsId?: number;
slots: AttachmentTargetSlot[];
};
}
export const stressfreiEmailApi = {
getByCustomer: async (customerId: number, includeInactive = false) => {
const res = await api.get<ApiResponse<StressfreiEmail[]>>(`/customers/${customerId}/stressfrei-emails`, { params: { includeInactive } });
return res.data;
},
create: async (customerId: number, data: {
email: string;
platform?: string;
notes?: string;
provisionAtProvider?: boolean;
createMailbox?: boolean;
}) => {
const res = await api.post<ApiResponse<StressfreiEmail>>(`/customers/${customerId}/stressfrei-emails`, data);
return res.data;
},
update: async (id: number, data: Partial<StressfreiEmail>) => {
const res = await api.put<ApiResponse<StressfreiEmail>>(`/stressfrei-emails/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/stressfrei-emails/${id}`);
return res.data;
},
// Mailbox nachträglich aktivieren
enableMailbox: async (id: number) => {
const res = await api.post<ApiResponse<null>>(`/stressfrei-emails/${id}/enable-mailbox`);
return res.data;
},
// Mailbox-Status mit Provider synchronisieren
syncMailboxStatus: async (id: number) => {
const res = await api.post<ApiResponse<{ hasMailbox: boolean; wasUpdated: boolean }>>(`/stressfrei-emails/${id}/sync-mailbox-status`);
return res.data;
},
// Mailbox-Zugangsdaten abrufen (IMAP/SMTP)
getMailboxCredentials: async (id: number) => {
const res = await api.get<ApiResponse<{
email: string;
password: string;
imap: { server: string; port: number; encryption: string } | null;
smtp: { server: string; port: number; encryption: string } | null;
}>>(`/stressfrei-emails/${id}/credentials`);
return res.data;
},
// Passwort zurücksetzen (generiert neues Passwort beim Provider)
resetPassword: async (id: number) => {
const res = await api.post<ApiResponse<{ password: string }>>(`/stressfrei-emails/${id}/reset-password`);
return res.data;
},
// E-Mails synchronisieren
syncEmails: async (id: number, fullSync = false) => {
const res = await api.post<ApiResponse<SyncResult>>(`/stressfrei-emails/${id}/sync`, {}, { params: { full: fullSync } });
return res.data;
},
// E-Mail senden
sendEmail: async (id: number, params: SendEmailParams) => {
const res = await api.post<ApiResponse<{ messageId: string }>>(`/stressfrei-emails/${id}/send`, params);
return res.data;
},
// Ordner-Anzahlen abrufen (total und ungelesen pro Ordner)
getFolderCounts: async (id: number) => {
const res = await api.get<ApiResponse<{
inbox: number;
inboxUnread: number;
sent: number;
sentUnread: number;
trash: number;
trashUnread: number;
}>>(`/stressfrei-emails/${id}/folder-counts`);
return res.data;
},
};
// Cached Email API (E-Mail-Client)
export const cachedEmailApi = {
// E-Mails für Kunden abrufen
getForCustomer: async (customerId: number, options?: { accountId?: number; folder?: 'INBOX' | 'SENT'; limit?: number; offset?: number }) => {
const res = await api.get<ApiResponse<CachedEmail[]>>(`/customers/${customerId}/emails`, { params: options });
return res.data;
},
// E-Mails für Vertrag abrufen
getForContract: async (contractId: number, options?: { folder?: 'INBOX' | 'SENT'; limit?: number; offset?: number }) => {
const res = await api.get<ApiResponse<CachedEmail[]>>(`/contracts/${contractId}/emails`, { params: options });
return res.data;
},
// Ordner-Anzahlen für Vertrag abrufen (zugeordnete E-Mails)
getContractFolderCounts: async (contractId: number) => {
const res = await api.get<ApiResponse<{
inbox: number;
inboxUnread: number;
sent: number;
sentUnread: number;
}>>(`/contracts/${contractId}/emails/folder-counts`);
return res.data;
},
// Mailbox-Konten eines Kunden abrufen
getMailboxAccounts: async (customerId: number) => {
const res = await api.get<ApiResponse<MailboxAccount[]>>(`/customers/${customerId}/mailbox-accounts`);
return res.data;
},
// Einzelne E-Mail abrufen (mit Body)
getById: async (id: number) => {
const res = await api.get<ApiResponse<CachedEmail>>(`/emails/${id}`);
return res.data;
},
// E-Mail-Thread abrufen
getThread: async (id: number) => {
const res = await api.get<ApiResponse<CachedEmail[]>>(`/emails/${id}/thread`);
return res.data;
},
// Als gelesen/ungelesen markieren
markAsRead: async (id: number, isRead: boolean) => {
const res = await api.patch<ApiResponse<void>>(`/emails/${id}/read`, { isRead });
return res.data;
},
// Stern umschalten
toggleStar: async (id: number) => {
const res = await api.post<ApiResponse<{ isStarred: boolean }>>(`/emails/${id}/star`);
return res.data;
},
// Vertrag zuordnen
assignToContract: async (emailId: number, contractId: number) => {
const res = await api.post<ApiResponse<CachedEmail>>(`/emails/${emailId}/assign`, { contractId });
return res.data;
},
// Zuordnung aufheben
unassignFromContract: async (emailId: number) => {
const res = await api.delete<ApiResponse<CachedEmail>>(`/emails/${emailId}/assign`);
return res.data;
},
// E-Mail löschen (nur Admin)
delete: async (emailId: number) => {
const res = await api.delete<ApiResponse<void>>(`/emails/${emailId}`);
return res.data;
},
// Anhang-URL (view=true für inline anzeigen, sonst download)
getAttachmentUrl: (emailId: number, filename: string, view?: boolean) => {
const token = localStorage.getItem('token');
const encodedFilename = encodeURIComponent(filename);
const viewParam = view ? '&view=true' : '';
return `${api.defaults.baseURL}/emails/${emailId}/attachments/${encodedFilename}?token=${token}${viewParam}`;
},
// Ungelesene E-Mails zählen
getUnreadCount: async (params: { customerId?: number; contractId?: number }) => {
const res = await api.get<ApiResponse<{ count: number }>>('/emails/unread-count', { params });
return res.data;
},
// ==================== PAPIERKORB ====================
// Papierkorb-E-Mails für Kunden abrufen
getTrash: async (customerId: number) => {
const res = await api.get<ApiResponse<CachedEmail[]>>(`/customers/${customerId}/emails/trash`);
return res.data;
},
// Papierkorb-Anzahl für Kunden
getTrashCount: async (customerId: number) => {
const res = await api.get<ApiResponse<{ count: number }>>(`/customers/${customerId}/emails/trash/count`);
return res.data;
},
// E-Mail aus Papierkorb wiederherstellen
restore: async (emailId: number) => {
const res = await api.post<ApiResponse<void>>(`/emails/${emailId}/restore`);
return res.data;
},
// E-Mail endgültig löschen (aus Papierkorb)
permanentDelete: async (emailId: number) => {
const res = await api.delete<ApiResponse<void>>(`/emails/${emailId}/permanent`);
return res.data;
},
// ==================== ANHANG-SPEICHERUNG ====================
// Verfügbare Dokumenten-Ziele für Anhänge abrufen
getAttachmentTargets: async (emailId: number) => {
const res = await api.get<ApiResponse<AttachmentTargetsResponse>>(`/emails/${emailId}/attachment-targets`);
return res.data;
},
// Anhang in Dokumentenfeld speichern
saveAttachmentTo: async (emailId: number, filename: string, params: { entityType: string; entityId?: number; targetKey: string }) => {
const encodedFilename = encodeURIComponent(filename);
const res = await api.post<ApiResponse<{ path: string; filename: string; originalName: string; size: number }>>(
`/emails/${emailId}/attachments/${encodedFilename}/save-to`,
params
);
return res.data;
},
// E-Mail als PDF speichern
saveEmailAsPdf: async (emailId: number, params: { entityType: string; entityId?: number; targetKey: string }) => {
const res = await api.post<ApiResponse<{ path: string; filename: string; size: number }>>(
`/emails/${emailId}/save-as-pdf`,
params
);
return res.data;
},
// E-Mail als Rechnung speichern (für Energieverträge)
saveEmailAsInvoice: async (emailId: number, params: { invoiceDate: string; invoiceType: string; notes?: string }) => {
const res = await api.post<ApiResponse<{ id: number; invoiceDate: string; invoiceType: string; documentPath: string }>>(
`/emails/${emailId}/save-as-invoice`,
params
);
return res.data;
},
// Anhang als Rechnung speichern (für Energieverträge)
saveAttachmentAsInvoice: async (emailId: number, filename: string, params: { invoiceDate: string; invoiceType: string; notes?: string }) => {
const encodedFilename = encodeURIComponent(filename);
const res = await api.post<ApiResponse<{ id: number; invoiceDate: string; invoiceType: string; documentPath: string }>>(
`/emails/${emailId}/attachments/${encodedFilename}/save-as-invoice`,
params
);
return res.data;
},
};
// Contracts - Vertragsbaum für Kundenansicht
export interface ContractTreeNodeContract {
id: number;
contractNumber: string;
type: string;
status: string;
startDate: string | null;
endDate: string | null;
providerName: string | null;
tariffName: string | null;
previousContractId: number | null;
provider?: { id: number; name: string } | null;
tariff?: { id: number; name: string } | null;
contractCategory?: { id: number; name: string } | null;
}
export interface ContractTreeNode {
contract: ContractTreeNodeContract;
predecessors: ContractTreeNode[];
hasHistory: boolean;
}
export const contractApi = {
getAll: async (params?: { customerId?: number; type?: string; status?: string; search?: string; page?: number; limit?: number }) => {
const res = await api.get<ApiResponse<Contract[]>>('/contracts', { params });
return res.data;
},
getTreeForCustomer: async (customerId: number) => {
const res = await api.get<ApiResponse<ContractTreeNode[]>>('/contracts', { params: { customerId, tree: 'true' } });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<Contract>>(`/contracts/${id}`);
return res.data;
},
create: async (data: Partial<Contract> & { [key: string]: unknown }) => {
const res = await api.post<ApiResponse<Contract>>('/contracts', data);
return res.data;
},
update: async (id: number, data: Partial<Contract> & { [key: string]: unknown }) => {
const res = await api.put<ApiResponse<Contract>>(`/contracts/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/contracts/${id}`);
return res.data;
},
createFollowUp: async (id: number) => {
const res = await api.post<ApiResponse<Contract>>(`/contracts/${id}/follow-up`);
return res.data;
},
getPassword: async (id: number) => {
const res = await api.get<ApiResponse<{ password: string }>>(`/contracts/${id}/password`);
return res.data;
},
getSimCardCredentials: async (simCardId: number) => {
const res = await api.get<ApiResponse<{ pin: string | null; puk: string | null }>>(`/contracts/simcard/${simCardId}/credentials`);
return res.data;
},
getInternetCredentials: async (contractId: number) => {
const res = await api.get<ApiResponse<{ password: string | null }>>(`/contracts/${contractId}/internet-credentials`);
return res.data;
},
getSipCredentials: async (phoneNumberId: number) => {
const res = await api.get<ApiResponse<{ password: string | null }>>(`/contracts/phonenumber/${phoneNumberId}/sip-credentials`);
return res.data;
},
// Vertrags-Cockpit
getCockpit: async () => {
const res = await api.get<ApiResponse<import('../types').CockpitResult>>('/contracts/cockpit');
return res.data;
},
};
// Contract Tasks (Aufgaben)
export const contractTaskApi = {
// Alle Tasks über alle Verträge (für Task-Liste & Dashboard)
getAll: async (params?: { status?: ContractTaskStatus; customerId?: number }) => {
const res = await api.get<ApiResponse<ContractTask[]>>('/tasks', { params });
return res.data;
},
// Task-Statistik (offene Aufgaben)
getStats: async () => {
const res = await api.get<ApiResponse<{ openCount: number }>>('/tasks/stats');
return res.data;
},
// Tasks für einen spezifischen Vertrag
getByContract: async (contractId: number, status?: ContractTaskStatus) => {
const res = await api.get<ApiResponse<ContractTask[]>>(`/contracts/${contractId}/tasks`, { params: { status } });
return res.data;
},
create: async (contractId: number, data: { title: string; description?: string; visibleInPortal?: boolean }) => {
const res = await api.post<ApiResponse<ContractTask>>(`/contracts/${contractId}/tasks`, data);
return res.data;
},
update: async (taskId: number, data: { title?: string; description?: string; visibleInPortal?: boolean }) => {
const res = await api.put<ApiResponse<ContractTask>>(`/tasks/${taskId}`, data);
return res.data;
},
complete: async (taskId: number) => {
const res = await api.post<ApiResponse<ContractTask>>(`/tasks/${taskId}/complete`);
return res.data;
},
reopen: async (taskId: number) => {
const res = await api.post<ApiResponse<ContractTask>>(`/tasks/${taskId}/reopen`);
return res.data;
},
delete: async (taskId: number) => {
const res = await api.delete<ApiResponse<void>>(`/tasks/${taskId}`);
return res.data;
},
// Subtasks
createSubtask: async (taskId: number, title: string) => {
const res = await api.post<ApiResponse<ContractTaskSubtask>>(`/tasks/${taskId}/subtasks`, { title });
return res.data;
},
// Kundenportal: Antwort auf eigenes Ticket
createReply: async (taskId: number, title: string) => {
const res = await api.post<ApiResponse<ContractTaskSubtask>>(`/tasks/${taskId}/reply`, { title });
return res.data;
},
updateSubtask: async (subtaskId: number, title: string) => {
const res = await api.put<ApiResponse<ContractTaskSubtask>>(`/subtasks/${subtaskId}`, { title });
return res.data;
},
completeSubtask: async (subtaskId: number) => {
const res = await api.post<ApiResponse<ContractTaskSubtask>>(`/subtasks/${subtaskId}/complete`);
return res.data;
},
reopenSubtask: async (subtaskId: number) => {
const res = await api.post<ApiResponse<ContractTaskSubtask>>(`/subtasks/${subtaskId}/reopen`);
return res.data;
},
deleteSubtask: async (subtaskId: number) => {
const res = await api.delete<ApiResponse<void>>(`/subtasks/${subtaskId}`);
return res.data;
},
// Support-Ticket erstellen (für Kundenportal)
createSupportTicket: async (contractId: number, data: { title: string; description?: string }) => {
const res = await api.post<ApiResponse<ContractTask>>(`/contracts/${contractId}/support-ticket`, data);
return res.data;
},
};
// App Settings
export const appSettingsApi = {
getPublic: async () => {
const res = await api.get<ApiResponse<Record<string, string>>>('/settings/public');
return res.data;
},
getAll: async () => {
const res = await api.get<ApiResponse<Record<string, string>>>('/settings');
return res.data;
},
update: async (settings: Record<string, string>) => {
const res = await api.put<ApiResponse<void>>('/settings', settings);
return res.data;
},
updateOne: async (key: string, value: string) => {
const res = await api.put<ApiResponse<void>>(`/settings/${key}`, { value });
return res.data;
},
};
// Backup & Restore
export interface BackupInfo {
name: string;
timestamp: string;
totalRecords: number;
tables: { table: string; count: number }[];
sizeBytes: number;
hasUploads: boolean;
uploadSizeBytes: number;
}
export const backupApi = {
list: async () => {
const res = await api.get<ApiResponse<BackupInfo[]>>('/settings/backups');
return res.data;
},
create: async () => {
const res = await api.post<ApiResponse<{ backupName: string }>>('/settings/backup');
return res.data;
},
restore: async (name: string) => {
const res = await api.post<ApiResponse<{ restoredRecords: number; restoredFiles: number }>>(`/settings/backup/${name}/restore`);
return res.data;
},
delete: async (name: string) => {
const res = await api.delete<ApiResponse<void>>(`/settings/backup/${name}`);
return res.data;
},
getDownloadUrl: (name: string) => {
return `/api/settings/backup/${name}/download`;
},
upload: async (file: File) => {
const formData = new FormData();
formData.append('backup', file);
const res = await api.post<ApiResponse<{ backupName: string }>>('/settings/backup/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
factoryReset: async () => {
const res = await api.post<ApiResponse<void>>('/settings/factory-reset');
return res.data;
},
};
// Platforms
export const platformApi = {
getAll: async (includeInactive = false) => {
const res = await api.get<ApiResponse<SalesPlatform[]>>('/platforms', { params: { includeInactive } });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<SalesPlatform>>(`/platforms/${id}`);
return res.data;
},
create: async (data: Partial<SalesPlatform>) => {
const res = await api.post<ApiResponse<SalesPlatform>>('/platforms', data);
return res.data;
},
update: async (id: number, data: Partial<SalesPlatform>) => {
const res = await api.put<ApiResponse<SalesPlatform>>(`/platforms/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/platforms/${id}`);
return res.data;
},
};
// Cancellation Periods
export const cancellationPeriodApi = {
getAll: async (includeInactive = false) => {
const res = await api.get<ApiResponse<CancellationPeriod[]>>('/cancellation-periods', { params: { includeInactive } });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<CancellationPeriod>>(`/cancellation-periods/${id}`);
return res.data;
},
create: async (data: Partial<CancellationPeriod>) => {
const res = await api.post<ApiResponse<CancellationPeriod>>('/cancellation-periods', data);
return res.data;
},
update: async (id: number, data: Partial<CancellationPeriod>) => {
const res = await api.put<ApiResponse<CancellationPeriod>>(`/cancellation-periods/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/cancellation-periods/${id}`);
return res.data;
},
};
// Contract Durations
export const contractDurationApi = {
getAll: async (includeInactive = false) => {
const res = await api.get<ApiResponse<ContractDuration[]>>('/contract-durations', { params: { includeInactive } });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<ContractDuration>>(`/contract-durations/${id}`);
return res.data;
},
create: async (data: Partial<ContractDuration>) => {
const res = await api.post<ApiResponse<ContractDuration>>('/contract-durations', data);
return res.data;
},
update: async (id: number, data: Partial<ContractDuration>) => {
const res = await api.put<ApiResponse<ContractDuration>>(`/contract-durations/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/contract-durations/${id}`);
return res.data;
},
};
// Contract Categories (Vertragstypen)
export const contractCategoryApi = {
getAll: async (includeInactive = false) => {
const res = await api.get<ApiResponse<ContractCategory[]>>('/contract-categories', { params: { includeInactive } });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<ContractCategory>>(`/contract-categories/${id}`);
return res.data;
},
create: async (data: Partial<ContractCategory>) => {
const res = await api.post<ApiResponse<ContractCategory>>('/contract-categories', data);
return res.data;
},
update: async (id: number, data: Partial<ContractCategory>) => {
const res = await api.put<ApiResponse<ContractCategory>>(`/contract-categories/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/contract-categories/${id}`);
return res.data;
},
};
// Providers (Anbieter)
export const providerApi = {
getAll: async (includeInactive = false) => {
const res = await api.get<ApiResponse<Provider[]>>('/providers', { params: { includeInactive } });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<Provider>>(`/providers/${id}`);
return res.data;
},
create: async (data: Partial<Provider>) => {
const res = await api.post<ApiResponse<Provider>>('/providers', data);
return res.data;
},
update: async (id: number, data: Partial<Provider>) => {
const res = await api.put<ApiResponse<Provider>>(`/providers/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/providers/${id}`);
return res.data;
},
getTariffs: async (providerId: number, includeInactive = false) => {
const res = await api.get<ApiResponse<Tariff[]>>(`/providers/${providerId}/tariffs`, { params: { includeInactive } });
return res.data;
},
createTariff: async (providerId: number, data: Partial<Tariff>) => {
const res = await api.post<ApiResponse<Tariff>>(`/providers/${providerId}/tariffs`, data);
return res.data;
},
};
// Tariffs (Tarife)
export const tariffApi = {
getById: async (id: number) => {
const res = await api.get<ApiResponse<Tariff>>(`/tariffs/${id}`);
return res.data;
},
update: async (id: number, data: Partial<Tariff>) => {
const res = await api.put<ApiResponse<Tariff>>(`/tariffs/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/tariffs/${id}`);
return res.data;
},
};
// Upload
export const uploadApi = {
uploadBankCardDocument: async (bankCardId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/bank-cards/${bankCardId}`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
uploadIdentityDocument: async (documentId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/documents/${documentId}`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteBankCardDocument: async (bankCardId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/bank-cards/${bankCardId}`);
return res.data;
},
deleteIdentityDocument: async (documentId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/documents/${documentId}`);
return res.data;
},
uploadBusinessRegistration: async (customerId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/customers/${customerId}/business-registration`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteBusinessRegistration: async (customerId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/customers/${customerId}/business-registration`);
return res.data;
},
uploadCommercialRegister: async (customerId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/customers/${customerId}/commercial-register`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteCommercialRegister: async (customerId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/customers/${customerId}/commercial-register`);
return res.data;
},
uploadPrivacyPolicy: async (customerId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/customers/${customerId}/privacy-policy`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deletePrivacyPolicy: async (customerId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/customers/${customerId}/privacy-policy`);
return res.data;
},
// Contract Documents
uploadCancellationLetter: async (contractId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/contracts/${contractId}/cancellation-letter`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteCancellationLetter: async (contractId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/contracts/${contractId}/cancellation-letter`);
return res.data;
},
uploadCancellationConfirmation: async (contractId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/contracts/${contractId}/cancellation-confirmation`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteCancellationConfirmation: async (contractId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/contracts/${contractId}/cancellation-confirmation`);
return res.data;
},
uploadCancellationLetterOptions: async (contractId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/contracts/${contractId}/cancellation-letter-options`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteCancellationLetterOptions: async (contractId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/contracts/${contractId}/cancellation-letter-options`);
return res.data;
},
uploadCancellationConfirmationOptions: async (contractId: number, file: File) => {
const formData = new FormData();
formData.append('document', file);
const res = await api.post<ApiResponse<{ path: string; filename: string }>>(`/upload/contracts/${contractId}/cancellation-confirmation-options`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return res.data;
},
deleteCancellationConfirmationOptions: async (contractId: number) => {
const res = await api.delete<ApiResponse<void>>(`/upload/contracts/${contractId}/cancellation-confirmation-options`);
return res.data;
},
};
// Users
export const userApi = {
getAll: async (params?: { search?: string; isActive?: boolean; roleId?: number; page?: number; limit?: number }) => {
const res = await api.get<ApiResponse<User[]>>('/users', { params });
return res.data;
},
getById: async (id: number) => {
const res = await api.get<ApiResponse<User>>(`/users/${id}`);
return res.data;
},
create: async (data: { email: string; password: string; firstName: string; lastName: string; roleIds: number[]; customerId?: number; hasDeveloperAccess?: boolean }) => {
const res = await api.post<ApiResponse<User>>('/users', data);
return res.data;
},
update: async (id: number, data: Partial<User> & { password?: string; roleIds?: number[] }) => {
const res = await api.put<ApiResponse<User>>(`/users/${id}`, data);
return res.data;
},
delete: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/users/${id}`);
return res.data;
},
getRoles: async () => {
const res = await api.get<ApiResponse<Role[]>>('/users/roles/list');
return res.data;
},
};
// Developer
export const developerApi = {
getSchema: async () => {
const res = await api.get<ApiResponse<any[]>>('/developer/schema');
return res.data;
},
getTableData: async (tableName: string, page = 1, limit = 50) => {
const res = await api.get<ApiResponse<any[]>>(`/developer/table/${tableName}`, { params: { page, limit } });
return res.data;
},
updateRow: async (tableName: string, id: string, data: Record<string, any>) => {
const res = await api.put<ApiResponse<any>>(`/developer/table/${tableName}/${id}`, data);
return res.data;
},
deleteRow: async (tableName: string, id: string) => {
const res = await api.delete<ApiResponse<void>>(`/developer/table/${tableName}/${id}`);
return res.data;
},
getReference: async (tableName: string) => {
const res = await api.get<ApiResponse<any[]>>(`/developer/reference/${tableName}`);
return res.data;
},
};
// Email Provider (für Stressfrei-Wechseln Provisionierung)
export interface EmailProviderConfig {
id: number;
name: string;
type: 'PLESK' | 'CPANEL' | 'DIRECTADMIN';
apiUrl: string;
apiKey?: string;
username?: string;
passwordEncrypted?: string;
domain: string;
defaultForwardEmail?: string;
// IMAP/SMTP-Server (für E-Mail-Client)
imapServer?: string;
imapPort?: number;
smtpServer?: string;
smtpPort?: number;
// Verschlüsselungs-Einstellungen
imapEncryption?: 'SSL' | 'STARTTLS' | 'NONE';
smtpEncryption?: 'SSL' | 'STARTTLS' | 'NONE';
allowSelfSignedCerts?: boolean; // Selbstsignierte Zertifikate erlauben
isActive: boolean;
isDefault: boolean;
createdAt: string;
updatedAt: string;
}
export interface EmailOperationResult {
success: boolean;
message?: string;
error?: string;
}
export const emailProviderApi = {
// Config CRUD
getConfigs: async () => {
const res = await api.get<ApiResponse<EmailProviderConfig[]>>('/email-providers/configs');
return res.data;
},
getConfig: async (id: number) => {
const res = await api.get<ApiResponse<EmailProviderConfig>>(`/email-providers/configs/${id}`);
return res.data;
},
createConfig: async (data: Partial<EmailProviderConfig> & { password?: string }) => {
const res = await api.post<ApiResponse<EmailProviderConfig>>('/email-providers/configs', data);
return res.data;
},
updateConfig: async (id: number, data: Partial<EmailProviderConfig> & { password?: string }) => {
const res = await api.put<ApiResponse<EmailProviderConfig>>(`/email-providers/configs/${id}`, data);
return res.data;
},
deleteConfig: async (id: number) => {
const res = await api.delete<ApiResponse<void>>(`/email-providers/configs/${id}`);
return res.data;
},
// Email Operations
testConnection: async (options?: {
id?: number;
testData?: {
type: 'PLESK' | 'CPANEL' | 'DIRECTADMIN';
apiUrl: string;
apiKey?: string;
username?: string;
password?: string;
domain: string;
};
}) => {
const body = options?.testData
? { ...options.testData }
: options?.id
? { id: options.id }
: {};
const res = await api.post<ApiResponse<EmailOperationResult>>('/email-providers/test-connection', body);
return res.data;
},
getDomain: async () => {
const res = await api.get<ApiResponse<{ domain: string | null }>>('/email-providers/domain');
return res.data;
},
checkEmailExists: async (localPart: string) => {
const res = await api.get<ApiResponse<{ exists: boolean; email?: string }>>(`/email-providers/check/${localPart}`);
return res.data;
},
provisionEmail: async (localPart: string, customerEmail: string) => {
const res = await api.post<ApiResponse<EmailOperationResult>>('/email-providers/provision', { localPart, customerEmail });
return res.data;
},
deprovisionEmail: async (localPart: string) => {
const res = await api.delete<ApiResponse<EmailOperationResult>>(`/email-providers/deprovision/${localPart}`);
return res.data;
},
};
export default api;