feat: standardize correspondent and tag types with usage count

This commit is contained in:
2025-12-04 13:11:52 +01:00
parent 225f810513
commit e8f578eb48
4 changed files with 37 additions and 36 deletions
@@ -1,22 +1,11 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import type { FormEvent, KeyboardEvent } from 'react'; import type { FormEvent, KeyboardEvent } from 'react';
import type { Correspondent } from '../types/documents';
interface CorrespondentUsage {
total?: number;
[key: string]: unknown;
}
interface CorrespondentEntry {
id?: string;
name?: string;
usage?: CorrespondentUsage;
[key: string]: unknown;
}
export interface CorrespondentsPanelProps { export interface CorrespondentsPanelProps {
correspondents?: CorrespondentEntry[]; correspondents?: Correspondent[];
onRefresh?: () => void | Promise<void>; onRefresh?: () => void | Promise<void>;
onCreate: (payload: { name: string }) => Promise<CorrespondentEntry | void>; onCreate: (payload: { name: string }) => Promise<Correspondent | void>;
onUpdate: (id: string, payload: { name: string }) => Promise<void>; onUpdate: (id: string, payload: { name: string }) => Promise<void>;
onDelete: (id: string) => Promise<void>; onDelete: (id: string) => Promise<void>;
onNotify?: (message: string, variant?: string) => void; onNotify?: (message: string, variant?: string) => void;
@@ -37,7 +26,7 @@ function CorrespondentsPanel({
const [creating, setCreating] = useState(false); const [creating, setCreating] = useState(false);
const [deletingId, setDeletingId] = useState<string | null>(null); const [deletingId, setDeletingId] = useState<string | null>(null);
const startEdit = useCallback((correspondent: CorrespondentEntry) => { const startEdit = useCallback((correspondent: Correspondent) => {
setEditingId(correspondent.id); setEditingId(correspondent.id);
setDraftName(correspondent.name); setDraftName(correspondent.name);
}, []); }, []);
@@ -68,7 +57,7 @@ function CorrespondentsPanel({
}, [editingId, draftName, onUpdate, cancelEdit, onNotify]); }, [editingId, draftName, onUpdate, cancelEdit, onNotify]);
const handleDelete = useCallback( const handleDelete = useCallback(
async (correspondent: CorrespondentEntry) => { async (correspondent: Correspondent) => {
if (!correspondent?.id) return; if (!correspondent?.id) return;
setDeletingId(correspondent.id); setDeletingId(correspondent.id);
try { try {
@@ -121,12 +110,12 @@ function CorrespondentsPanel({
[handleSave, cancelEdit], [handleSave, cancelEdit],
); );
const renderUsage = useCallback((usage?: CorrespondentUsage) => { const renderUsage = useCallback((correspondent: Correspondent) => {
if (!usage) { const count = correspondent.usage_count;
if (count == null || !Number.isFinite(count)) {
return '0'; return '0';
} }
const total = Number.isFinite(usage.total) ? Number(usage.total) : 0; return count.toString();
return total.toString();
}, []); }, []);
return ( return (
@@ -195,7 +184,7 @@ function CorrespondentsPanel({
<span>{correspondent.name}</span> <span>{correspondent.name}</span>
)} )}
</td> </td>
<td className="numeric">{renderUsage(correspondent.usage)}</td> <td className="numeric">{renderUsage(correspondent)}</td>
<td className="actions"> <td className="actions">
{isEditing ? ( {isEditing ? (
<div className="tags-table__edit-controls"> <div className="tags-table__edit-controls">
@@ -1,4 +1,5 @@
import { MutableRefObject, useCallback, useState } from 'react'; import { MutableRefObject, useCallback, useState } from 'react';
import type { Correspondent } from '../../types/documents';
type ApiClient = { type ApiClient = {
get: (path: string) => Promise<{ data: unknown }>; get: (path: string) => Promise<{ data: unknown }>;
@@ -7,12 +8,6 @@ type ApiClient = {
delete: (path: string) => Promise<{ data: unknown }>; delete: (path: string) => Promise<{ data: unknown }>;
}; };
interface CorrespondentEntry {
id?: string;
name?: string;
[key: string]: unknown;
}
interface UseCorrespondentsOptions { interface UseCorrespondentsOptions {
apiClient: ApiClient; apiClient: ApiClient;
notifyApiError: (error: unknown, fallback: string) => void; notifyApiError: (error: unknown, fallback: string) => void;
@@ -28,7 +23,7 @@ const useCorrespondents = ({
tenantIdRef, tenantIdRef,
mapDocumentCaches, mapDocumentCaches,
}: UseCorrespondentsOptions) => { }: UseCorrespondentsOptions) => {
const [correspondents, setCorrespondents] = useState<CorrespondentEntry[]>([]); const [correspondents, setCorrespondents] = useState<Correspondent[]>([]);
const refreshCorrespondents = useCallback(async () => { const refreshCorrespondents = useCallback(async () => {
const requestTenantId = tenantIdRef.current; const requestTenantId = tenantIdRef.current;
+2 -8
View File
@@ -1,5 +1,6 @@
import { MutableRefObject, useCallback, useState } from 'react'; import { MutableRefObject, useCallback, useState } from 'react';
import type { TagId, TenantId } from '../../types/identifiers'; import type { TagId, TenantId } from '../../types/identifiers';
import type { Tag } from '../../types/documents';
type ApiClient = { type ApiClient = {
get: (path: string) => Promise<{ data: unknown }> get: (path: string) => Promise<{ data: unknown }>
@@ -12,13 +13,6 @@ interface TagManagerInterface {
buildPayload: (input: { label?: string; color?: string | null }) => { label: string; color: string | null }; buildPayload: (input: { label?: string; color?: string | null }) => { label: string; color: string | null };
} }
interface TagEntry {
id?: TagId;
label?: string;
color?: string | null;
[key: string]: unknown;
}
interface UseTagsOptions { interface UseTagsOptions {
apiClient: ApiClient; apiClient: ApiClient;
notifyApiError: (error: unknown, fallback: string) => void; notifyApiError: (error: unknown, fallback: string) => void;
@@ -38,7 +32,7 @@ const useTags = ({
setActiveTagFilters, setActiveTagFilters,
mapDocumentCaches, mapDocumentCaches,
}: UseTagsOptions) => { }: UseTagsOptions) => {
const [tags, setTags] = useState<TagEntry[]>([]); const [tags, setTags] = useState<Tag[]>([]);
const refreshTags = useCallback(async () => { const refreshTags = useCallback(async () => {
const requestTenantId = tenantIdRef.current; const requestTenantId = tenantIdRef.current;
+23
View File
@@ -14,6 +14,29 @@ export interface DocumentCorrespondent {
count?: number; count?: number;
} }
/**
* A tag entity as returned by the API (includes usage_count).
* Use DocumentTag for the embedded version on documents.
*/
export interface Tag {
id?: Identifier;
label?: string;
color?: string | null;
usage_count?: number;
[key: string]: unknown;
}
/**
* A correspondent entity as returned by the API.
* Use DocumentCorrespondent for the embedded version on documents.
*/
export interface Correspondent {
id?: Identifier;
name?: string;
usage_count?: number;
[key: string]: unknown;
}
export interface DocumentVersion { export interface DocumentVersion {
assets?: Record<string, Asset> | Asset[] | null; assets?: Record<string, Asset> | Asset[] | null;
metadata?: Record<string, unknown> & { page_count?: number } | null; metadata?: Record<string, unknown> & { page_count?: number } | null;