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:
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user