fix: resolve all 12 TypeScript errors (tsc --noEmit clean)

- mail.ts: add explicit types to decodeMimeHeader callback params
- SessionList.tsx: use React.MouseEvent instead of nativeEvent
- ComposeModal.tsx: construct name from first_name + last_name
- MailDetail.tsx: destructure onReply/onForward from props
- RichTextEditor.tsx: use SetContentOptions object instead of boolean
- MailSearchBar.tsx: add optional value prop to interface
- EmptyState.tsx: add optional children prop
- Badge.tsx: add secondary variant to BadgeVariant, variantClasses, dotColors
- ConfirmDialog.tsx: add optional children prop
- hooks.ts: extend SearchResult type to include mail/file/event
This commit is contained in:
Agent Zero
2026-07-19 17:41:39 +02:00
parent 27a8ad8b30
commit 6f1655785e
10 changed files with 14 additions and 7 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ export interface AuditLogEntry {
}
export interface SearchResult {
type: 'company' | 'contact';
type: 'company' | 'contact' | 'mail' | 'file' | 'event';
id: string;
name: string;
description?: string;
+1 -1
View File
@@ -417,7 +417,7 @@ export function decodeMimeHeader(value: string | undefined | null): string {
return decodeURIComponent(escape(decoded));
} else {
// Q encoding: replace _ with space, then decode =XX hex sequences
const qDecoded = encoded.replace(/_/g, ' ').replace(/=([0-9A-F]{2})/gi, (_m, hex) => {
const qDecoded = encoded.replace(/_/g, ' ').replace(/=([0-9A-F]{2})/gi, (_m: string, hex: string) => {
return String.fromCharCode(parseInt(hex, 16));
});
return decodeURIComponent(escape(qDecoded));
+2 -2
View File
@@ -95,7 +95,7 @@ function TreeItem({
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
</svg>
<span className="flex-1 truncate">{node.folder!.name}</span>
<button onClick={(e) => { e.stopPropagation(); onContextMenu(e.nativeEvent, 'folder', node.folder!.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-primary-600 p-0.5" title="Umbenennen">
<button onClick={(e) => { e.stopPropagation(); onContextMenu(e, 'folder', node.folder!.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-primary-600 p-0.5" title="Umbenennen">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /></svg>
</button>
<button onClick={(e) => { e.stopPropagation(); onDelete(node.folder!.id, 'folder'); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-red-600 p-0.5" title="Löschen">
@@ -131,7 +131,7 @@ function TreeItem({
<span className={clsx('flex-1 truncate', activeSessionId === session.id && 'font-bold text-primary-700')}>
{session.title}
</span>
<button onClick={(e) => { e.stopPropagation(); onContextMenu(e.nativeEvent, 'session', session.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-primary-600 p-0.5" title="Umbenennen">
<button onClick={(e) => { e.stopPropagation(); onContextMenu(e, 'session', session.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-primary-600 p-0.5" title="Umbenennen">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /></svg>
</button>
<button onClick={(e) => { e.stopPropagation(); onDelete(session.id, 'session'); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-red-600 p-0.5" title="Löschen">
@@ -92,7 +92,7 @@ export function ComposeModal({
if (sig) {
const processedHtml = replaceSignatureVariables(
sig.body_html,
{ name: authUser?.name, email: authUser?.email, role: authUser?.role, first_name: authUser?.first_name, last_name: authUser?.last_name },
{ name: authUser ? `${authUser.first_name} ${authUser.last_name}`.trim() : undefined, email: authUser?.email, role: authUser?.role, first_name: authUser?.first_name, last_name: authUser?.last_name },
{ name: authTenant?.name },
);
setBody((prev) => `${prev}<br/><br/>---<br/>${processedHtml}`);
@@ -44,6 +44,8 @@ function formatBytes(bytes: number): string {
export function MailDetail({
mail,
loading,
onReply,
onForward,
onDownloadAttachment,
downloadingAttachmentId,
}: MailDetailProps) {
@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/Input';
export interface MailSearchBarProps {
onSearch: (query: string) => void;
value?: string;
}
export function MailSearchBar({ onSearch }: MailSearchBarProps) {
@@ -88,7 +88,7 @@ export function RichTextEditor({ content, onChange, placeholder, editable = true
// Sync external content changes (e.g. template/signature insert, reply/forward/draft load)
React.useEffect(() => {
if (editor && content !== editor.getHTML()) {
editor.commands.setContent(content, false);
editor.commands.setContent(content, { emitUpdate: false });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [content, editor]);
+3 -1
View File
@@ -1,7 +1,7 @@
import React from 'react';
import clsx from 'clsx';
export type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
export type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'secondary';
export interface BadgeProps {
variant?: BadgeVariant;
@@ -17,6 +17,7 @@ const variantClasses: Record<BadgeVariant, string> = {
warning: 'bg-warning-100 text-warning-700',
danger: 'bg-danger-100 text-danger-700',
info: 'bg-accent-100 text-accent-700',
secondary: 'bg-secondary-200 text-secondary-800',
};
const dotColors: Record<BadgeVariant, string> = {
@@ -26,6 +27,7 @@ const dotColors: Record<BadgeVariant, string> = {
warning: 'bg-warning-500',
danger: 'bg-danger-500',
info: 'bg-accent-500',
secondary: 'bg-secondary-500',
};
export function Badge({ variant = 'default', children, className, dot = false }: BadgeProps) {
@@ -12,6 +12,7 @@ export interface ConfirmDialogProps {
variant?: 'primary' | 'danger';
onConfirm: () => void;
onCancel: () => void;
children?: React.ReactNode;
}
export function ConfirmDialog({
@@ -8,6 +8,7 @@ export interface EmptyStateProps {
icon?: React.ReactNode;
action?: React.ReactNode;
className?: string;
children?: React.ReactNode;
}
export function EmptyState({