297 lines
9.5 KiB
TypeScript
297 lines
9.5 KiB
TypeScript
|
|
/**
|
||
|
|
* Documents Generator API client (Phase L1-L3).
|
||
|
|
*
|
||
|
|
* Letterheads (Briefpapier), print templates (block compositions),
|
||
|
|
* block registry and placeholders — all under /reports/... (documents
|
||
|
|
* router of the report_generator plugin).
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { apiClient, apiDelete, apiGet, apiPost, apiPut } from './client';
|
||
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||
|
|
|
||
|
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export interface DocBlock {
|
||
|
|
id: string;
|
||
|
|
type: string;
|
||
|
|
config: Record<string, unknown>;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LetterheadPageConfig {
|
||
|
|
size?: string;
|
||
|
|
orientation?: string;
|
||
|
|
margins?: { top?: number; right?: number; bottom?: number; left?: number };
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LetterheadConfig {
|
||
|
|
page?: LetterheadPageConfig;
|
||
|
|
header?: { enabled: boolean; blocks: DocBlock[] };
|
||
|
|
footer?: { enabled: boolean; blocks: DocBlock[] };
|
||
|
|
watermark?: { enabled: boolean; text?: string };
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface Letterhead {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
description: string;
|
||
|
|
config: LetterheadConfig;
|
||
|
|
is_default: boolean;
|
||
|
|
created_by: string;
|
||
|
|
created_at?: string | null;
|
||
|
|
updated_at?: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface PrintTemplate {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
description: string;
|
||
|
|
letterhead_id: string | null;
|
||
|
|
entity_type: string;
|
||
|
|
blocks: DocBlock[];
|
||
|
|
output_format: string;
|
||
|
|
created_by: string;
|
||
|
|
created_at?: string | null;
|
||
|
|
updated_at?: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface DocumentBlockType {
|
||
|
|
type: string;
|
||
|
|
label: string;
|
||
|
|
category: string;
|
||
|
|
description: string;
|
||
|
|
fields: Record<string, string>;
|
||
|
|
builtin: boolean;
|
||
|
|
contributed_by?: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface DocumentPlaceholder {
|
||
|
|
key: string;
|
||
|
|
label: string;
|
||
|
|
example: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface DocumentAsset {
|
||
|
|
id: string;
|
||
|
|
letterhead_id: string | null;
|
||
|
|
filename: string;
|
||
|
|
mime_type: string;
|
||
|
|
size_bytes: number;
|
||
|
|
data_url: string | null;
|
||
|
|
created_at?: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LetterheadInput {
|
||
|
|
name: string;
|
||
|
|
description?: string;
|
||
|
|
config?: LetterheadConfig;
|
||
|
|
is_default?: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface PrintTemplateInput {
|
||
|
|
name: string;
|
||
|
|
description?: string;
|
||
|
|
letterhead_id?: string | null;
|
||
|
|
entity_type?: string;
|
||
|
|
blocks?: DocBlock[];
|
||
|
|
output_format?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Query Keys ─────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export const DOC_QUERY_KEYS = {
|
||
|
|
letterheads: ['documents', 'letterheads'] as const,
|
||
|
|
letterhead: (id: string) => ['documents', 'letterheads', id] as const,
|
||
|
|
templates: ['documents', 'print-templates'] as const,
|
||
|
|
template: (id: string) => ['documents', 'print-templates', id] as const,
|
||
|
|
blocks: ['documents', 'block-types'] as const,
|
||
|
|
placeholders: (entityType?: string) => ['documents', 'placeholders', entityType ?? 'all'] as const,
|
||
|
|
assets: (letterheadId: string) => ['documents', 'assets', letterheadId] as const,
|
||
|
|
};
|
||
|
|
|
||
|
|
// ─── Fetch helpers ─────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
async function fetchLetterheads(): Promise<Letterhead[]> {
|
||
|
|
const res = await apiGet<{ items: Letterhead[]; total: number }>('/reports/letterheads');
|
||
|
|
return res.items ?? [];
|
||
|
|
}
|
||
|
|
|
||
|
|
async function fetchTemplates(): Promise<PrintTemplate[]> {
|
||
|
|
const res = await apiGet<{ items: PrintTemplate[]; total: number }>('/reports/print-templates');
|
||
|
|
return res.items ?? [];
|
||
|
|
}
|
||
|
|
|
||
|
|
async function fetchBlockTypes(): Promise<DocumentBlockType[]> {
|
||
|
|
return apiGet<DocumentBlockType[]>('/reports/document-blocks');
|
||
|
|
}
|
||
|
|
|
||
|
|
async function fetchPlaceholders(entityType?: string): Promise<Record<string, DocumentPlaceholder[]>> {
|
||
|
|
const url = entityType
|
||
|
|
? `/reports/document-placeholders?entity_type=${encodeURIComponent(entityType)}`
|
||
|
|
: '/reports/document-placeholders';
|
||
|
|
return apiGet<Record<string, DocumentPlaceholder[]>>(url);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function fetchAssets(letterheadId: string): Promise<DocumentAsset[]> {
|
||
|
|
return apiGet<DocumentAsset[]>(`/reports/letterheads/${letterheadId}/assets`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Hooks: Letterheads ─────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export function useLetterheads() {
|
||
|
|
return useQuery<Letterhead[]>({
|
||
|
|
queryKey: DOC_QUERY_KEYS.letterheads,
|
||
|
|
queryFn: fetchLetterheads,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useCreateLetterhead() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (input: LetterheadInput) =>
|
||
|
|
apiPost<Letterhead>('/reports/letterheads', input),
|
||
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.letterheads }),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useUpdateLetterhead() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: ({ id, input }: { id: string; input: Partial<LetterheadInput> }) =>
|
||
|
|
apiPut<Letterhead>(`/reports/letterheads/${id}`, input),
|
||
|
|
onSuccess: (_data, vars) => {
|
||
|
|
qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.letterheads });
|
||
|
|
qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.letterhead(vars.id) });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useDeleteLetterhead() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (id: string) => apiDelete(`/reports/letterheads/${id}`),
|
||
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.letterheads }),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Hooks: Print Templates ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export function usePrintTemplates() {
|
||
|
|
return useQuery<PrintTemplate[]>({
|
||
|
|
queryKey: DOC_QUERY_KEYS.templates,
|
||
|
|
queryFn: fetchTemplates,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useCreatePrintTemplate() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (input: PrintTemplateInput) =>
|
||
|
|
apiPost<PrintTemplate>('/reports/print-templates', input),
|
||
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.templates }),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useUpdatePrintTemplate() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: ({ id, input }: { id: string; input: Partial<PrintTemplateInput> }) =>
|
||
|
|
apiPut<PrintTemplate>(`/reports/print-templates/${id}`, input),
|
||
|
|
onSuccess: (_data, vars) => {
|
||
|
|
qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.templates });
|
||
|
|
qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.template(vars.id) });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useDeletePrintTemplate() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (id: string) => apiDelete(`/reports/print-templates/${id}`),
|
||
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.templates }),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Hooks: Registry ────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export function useDocumentBlockTypes() {
|
||
|
|
return useQuery<DocumentBlockType[]>({
|
||
|
|
queryKey: DOC_QUERY_KEYS.blocks,
|
||
|
|
queryFn: fetchBlockTypes,
|
||
|
|
staleTime: 5 * 60 * 1000,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useDocumentPlaceholders(entityType?: string, enabled = true) {
|
||
|
|
return useQuery<Record<string, DocumentPlaceholder[]>>({
|
||
|
|
queryKey: DOC_QUERY_KEYS.placeholders(entityType),
|
||
|
|
queryFn: () => fetchPlaceholders(entityType),
|
||
|
|
enabled,
|
||
|
|
staleTime: 5 * 60 * 1000,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useLetterheadAssets(letterheadId: string | null | undefined) {
|
||
|
|
return useQuery<DocumentAsset[]>({
|
||
|
|
queryKey: DOC_QUERY_KEYS.assets(letterheadId ?? 'none'),
|
||
|
|
queryFn: () => fetchAssets(letterheadId as string),
|
||
|
|
enabled: !!letterheadId,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useUploadLetterheadAsset() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: ({ letterheadId, file }: { letterheadId: string; file: File }) => {
|
||
|
|
const form = new FormData();
|
||
|
|
form.append('file', file);
|
||
|
|
return apiClient
|
||
|
|
.post(`/reports/letterheads/${letterheadId}/assets`, form, {
|
||
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||
|
|
})
|
||
|
|
.then((r) => r.data as DocumentAsset);
|
||
|
|
},
|
||
|
|
onSuccess: (_data, vars) =>
|
||
|
|
qc.invalidateQueries({ queryKey: DOC_QUERY_KEYS.assets(vars.letterheadId) }),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Preview & Render ──────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export interface PreviewInput {
|
||
|
|
blocks: DocBlock[];
|
||
|
|
letterhead_config: LetterheadConfig | null;
|
||
|
|
entity_type?: string | null;
|
||
|
|
data?: Record<string, unknown> | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function usePreviewDocument() {
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (input: PreviewInput) =>
|
||
|
|
apiPost<{ html: string }>('/reports/documents/preview', input),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface RenderInput {
|
||
|
|
templateId: string;
|
||
|
|
entityType: string;
|
||
|
|
entityId: string;
|
||
|
|
outputFormat?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function renderPrintTemplate(input: RenderInput): Promise<Blob> {
|
||
|
|
const response = await apiClient.post(
|
||
|
|
`/reports/print-templates/${input.templateId}/render`,
|
||
|
|
{
|
||
|
|
entity_type: input.entityType,
|
||
|
|
entity_id: input.entityId,
|
||
|
|
output_format: input.outputFormat ?? 'pdf',
|
||
|
|
},
|
||
|
|
{ responseType: 'blob' },
|
||
|
|
);
|
||
|
|
return response.data as Blob;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useRenderPrintTemplate() {
|
||
|
|
return useMutation({ mutationFn: renderPrintTemplate });
|
||
|
|
}
|