typescript-typenfehler behoben.. npm run lint läuft nun fehlerfrei durch.

This commit is contained in:
Codex Agent
2025-11-22 11:49:47 +01:00
parent 6c78d7e281
commit eb41cb6194
74 changed files with 469 additions and 396 deletions

View File

@@ -3,7 +3,7 @@ import { ApiError, emitApiErrorEvent } from './lib/apiError';
import type { EventLimitSummary } from './lib/limitWarnings';
import i18n from './i18n';
type JsonValue = Record<string, any>;
type JsonValue = Record<string, unknown>;
export type TenantAccountProfile = {
id: number;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import { cn } from '@/lib/utils';
interface StatItem {
key: string;

View File

@@ -44,7 +44,7 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
initialData: [],
});
const events = authReady ? fetchedEvents : [];
const events = React.useMemo(() => (authReady ? fetchedEvents : []), [authReady, fetchedEvents]);
const isLoading = authReady ? queryLoading : status === 'loading';
React.useEffect(() => {

View File

@@ -148,9 +148,12 @@ export default function WelcomeOrderSummaryPage() {
const packagesState = useTenantPackages();
const { t, i18n } = useTranslation("onboarding");
const locale = i18n.language?.startsWith("en") ? "en-GB" : "de-DE";
const { currencyFormatter, dateFormatter } = useLocaleFormats(locale);
const { currencyFormatter } = useLocaleFormats(locale);
const packageIdFromState = typeof location.state === "object" ? (location.state as any)?.packageId : undefined;
const packageIdFromState =
typeof location.state === "object" && location.state !== null && "packageId" in location.state
? (location.state as { packageId?: number | string | null }).packageId
: undefined;
const selectedPackageId = progress.selectedPackage?.id ?? packageIdFromState ?? null;
React.useEffect(() => {

View File

@@ -6,10 +6,8 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button';
import { AdminLayout } from '../components/AdminLayout';
import { CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
TenantHeroCard,
TenantOnboardingChecklistCard,
FrostedSurface,
tenantHeroPrimaryButtonClass,
tenantHeroSecondaryButtonClass,

View File

@@ -327,7 +327,7 @@ const shownWarningToasts = React.useRef<Set<string>>(new Set());
scope={warning.scope as 'photos' | 'guests' | 'gallery'}
onCheckout={(key) => { void handleAddonPurchase(warning.scope as 'photos' | 'guests' | 'gallery', key); }}
busy={addonBusyId === warning.scope}
t={(key, fallback) => t(key as any, fallback)}
t={(key, fallback) => t(key, fallback)}
/>
) : null}
</div>

View File

@@ -452,8 +452,8 @@ export default function EventInvitesPage(): React.ReactElement {
const exportLogo = effectiveCustomization?.logo_data_url ?? effectiveCustomization?.logo_url ?? null;
const exportQr = selectedInvite?.qr_code_data_url ?? null;
const handlePreviewSelect = React.useCallback((_id: string | null) => undefined, []);
const handlePreviewChange = React.useCallback((_id: string, _patch: Partial<LayoutElement>) => undefined, []);
const handlePreviewSelect = React.useCallback(() => undefined, []);
const handlePreviewChange = React.useCallback(() => undefined, []);
const handleCustomizerDraftChange = React.useCallback((draft: QrLayoutCustomization | null) => {
setCustomizerDraft((previous) => {
@@ -865,7 +865,7 @@ export default function EventInvitesPage(): React.ReactElement {
scope="guests"
onCheckout={(key) => { void handleAddonPurchase(key); }}
busy={addonBusy === 'guests'}
t={(key, fallback) => t(key as any, fallback)}
t={(key, fallback) => t(key, fallback)}
/>
</div>
) : null}
@@ -882,7 +882,7 @@ export default function EventInvitesPage(): React.ReactElement {
<CardDescription>{t('events.sections.addons.description', 'Zusätzliche Kontingente für dieses Event.')}</CardDescription>
</CardHeader>
<CardContent>
<AddonSummaryList addons={state.event.addons} t={(key, fallback) => t(key as any, fallback)} />
<AddonSummaryList addons={state.event.addons} t={(key, fallback) => t(key, fallback)} />
</CardContent>
</Card>
) : null}

View File

@@ -13,14 +13,14 @@ import { getAddonCatalog, getEvent, type EventAddonCatalogItem, type EventAddonS
import { AdminLayout } from '../components/AdminLayout';
import { createEventAddonCheckout, deletePhoto, featurePhoto, getEventPhotos, TenantPhoto, unfeaturePhoto } from '../api';
import { isAuthError } from '../auth/tokens';
import { getApiErrorMessage, isApiError } from '../lib/apiError';
import { getApiErrorMessage } from '../lib/apiError';
import { buildLimitWarnings, type EventLimitSummary } from '../lib/limitWarnings';
import { useTranslation } from 'react-i18next';
import { ADMIN_EVENTS_PATH, ADMIN_EVENT_VIEW_PATH } from '../constants';
export default function EventPhotosPage() {
const params = useParams<{ slug?: string }>();
const [searchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();
const slug = params.slug ?? searchParams.get('slug') ?? null;
const navigate = useNavigate();
const { t } = useTranslation('management');
@@ -36,8 +36,6 @@ export default function EventPhotosPage() {
const [busyId, setBusyId] = React.useState<number | null>(null);
const [limits, setLimits] = React.useState<EventLimitSummary | null>(null);
const [addons, setAddons] = React.useState<EventAddonCatalogItem[]>([]);
const [catalogError, setCatalogError] = React.useState<string | undefined>(undefined);
//const [searchParams, setSearchParams] = React.useState(() => new URLSearchParams(window.location.search));
const [eventAddons, setEventAddons] = React.useState<EventAddonSummary[]>([]);
const load = React.useCallback(async () => {
@@ -57,7 +55,6 @@ export default function EventPhotosPage() {
setLimits(photoResult.limits ?? null);
setEventAddons(eventData.addons ?? []);
setAddons(catalog);
setCatalogError(undefined);
} catch (err) {
if (!isAuthError(err)) {
setError(getApiErrorMessage(err, 'Fotos konnten nicht geladen werden.'));
@@ -81,7 +78,7 @@ export default function EventPhotosPage() {
setSearchParams(params);
navigate(window.location.pathname, { replace: true });
}
}, [searchParams, slug, load, navigate, translateLimits]);
}, [searchParams, slug, load, navigate, translateLimits, setSearchParams]);
async function handleToggleFeature(photo: TenantPhoto) {
if (!slug) return;
@@ -162,7 +159,7 @@ export default function EventPhotosPage() {
<CardDescription>{t('events.sections.addons.description', 'Zusätzliche Kontingente für dieses Event.')}</CardDescription>
</CardHeader>
<CardContent>
<AddonSummaryList addons={eventAddons} t={(key, fallback) => t(key as any, fallback)} />
<AddonSummaryList addons={eventAddons} t={(key, fallback) => t(key, fallback)} />
</CardContent>
</Card>
)}

View File

@@ -33,7 +33,7 @@ import {
ADMIN_EVENT_INVITES_PATH,
ADMIN_EVENT_TOOLKIT_PATH,
} from '../constants';
import { buildLimitWarnings, type EventLimitSummary } from '../lib/limitWarnings';
import { buildLimitWarnings } from '../lib/limitWarnings';
import { useTranslation } from 'react-i18next';
export default function EventsPage() {
@@ -57,7 +57,7 @@ export default function EventsPage() {
setLoading(false);
}
})();
}, []);
}, [t]);
const translateManagement = React.useCallback(
(key: string, fallback?: string, options?: Record<string, unknown>) =>

View File

@@ -239,7 +239,7 @@ function formatDate(value: string, language: string | undefined): string {
month: 'short',
year: 'numeric',
});
} catch (error) {
} catch {
return value;
}
}

View File

@@ -10,7 +10,7 @@ import { Label } from '@/components/ui/label';
import { useAuth } from '../auth/context';
import { ADMIN_DEFAULT_AFTER_LOGIN_PATH, ADMIN_EVENTS_PATH } from '../constants';
import { encodeReturnTo, resolveReturnTarget } from '../lib/returnTo';
import { resolveReturnTarget } from '../lib/returnTo';
import { useMutation } from '@tanstack/react-query';
type LoginResponse = {

View File

@@ -352,7 +352,6 @@ export function TasksSection({ embedded = false, onNavigateToCollections }: Task
export default function TasksPage() {
const navigate = useNavigate();
const { t } = useTranslation('management');
const { t: tc } = useTranslation('common');
return (
<AdminLayout

View File

@@ -10,7 +10,6 @@ import {
ShieldCheck,
Sparkles,
SunMedium,
Users,
Wand2,
} from 'lucide-react';
import { Button } from '@/components/ui/button';

View File

@@ -1561,37 +1561,34 @@ export function InviteLayoutCustomizerPanel({
const highlightedElementId = activeElementId ?? inspectorElementId;
const renderResponsiveSection = React.useCallback(
(id: string, title: string, description: string, content: React.ReactNode) => {
const body = <div className="space-y-4">{content}</div>;
if (!isCompact) {
return (
<section key={id} className="space-y-4 rounded-2xl border border-border bg-[var(--tenant-surface)] p-5 shadow-sm transition-colors">
<header className="space-y-1">
<h3 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{title}</h3>
{description ? <p className="text-xs text-muted-foreground">{description}</p> : null}
</header>
{body}
</section>
);
}
const renderResponsiveSection = (id: string, title: string, description: string, content: React.ReactNode) => {
const body = <div className="space-y-4">{content}</div>;
if (!isCompact) {
return (
<Collapsible key={id} defaultOpen className="rounded-2xl border border-border bg-[var(--tenant-surface)] p-3 shadow-sm transition-colors">
<CollapsibleTrigger type="button" className="flex w-full items-center justify-between gap-3 text-left">
<div className="space-y-1">
<h3 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{title}</h3>
{description ? <p className="text-xs text-muted-foreground">{description}</p> : null}
</div>
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform data-[state=open]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="pt-4">{body}</CollapsibleContent>
</Collapsible>
<section key={id} className="space-y-4 rounded-2xl border border-border bg-[var(--tenant-surface)] p-5 shadow-sm transition-colors">
<header className="space-y-1">
<h3 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{title}</h3>
{description ? <p className="text-xs text-muted-foreground">{description}</p> : null}
</header>
{body}
</section>
);
},
[isCompact]
);
}
return (
<Collapsible key={id} defaultOpen className="rounded-2xl border border-border bg-[var(--tenant-surface)] p-3 shadow-sm transition-colors">
<CollapsibleTrigger type="button" className="flex w-full items-center justify-between gap-3 text-left">
<div className="space-y-1">
<h3 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{title}</h3>
{description ? <p className="text-xs text-muted-foreground">{description}</p> : null}
</div>
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform data-[state=open]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="pt-4">{body}</CollapsibleContent>
</Collapsible>
);
};
return (
<div className="space-y-4">

View File

@@ -6,7 +6,6 @@ import {
CANVAS_WIDTH,
LayoutElement,
clamp,
LayoutElementType,
} from './schema';
type DesignerCanvasProps = {
@@ -23,7 +22,6 @@ type DesignerCanvasProps = {
qrCodeDataUrl: string | null;
logoDataUrl: string | null;
scale?: number;
layoutKey?: string;
readOnly?: boolean;
};
@@ -43,7 +41,6 @@ export function DesignerCanvas({
qrCodeDataUrl,
logoDataUrl,
scale = 1,
layoutKey,
readOnly = false,
}: DesignerCanvasProps): React.JSX.Element {
const canvasElementRef = React.useRef<HTMLCanvasElement | null>(null);
@@ -170,10 +167,6 @@ export function DesignerCanvas({
return () => {
const timeoutId = window.setTimeout(() => {
if (disposeTokenRef.current !== disposeToken) {
return;
}
destroyCanvas(canvas);
pendingTimeoutRef.current = null;
pendingDisposeRef.current = null;
@@ -216,8 +209,8 @@ export function DesignerCanvas({
onSelect(active.elementId);
};
const handleSelectionCleared = (event?: unknown) => {
const pointerEvent = event as { e?: MouseEvent } | undefined;
const handleSelectionCleared = (event?: fabric.IEvent<MouseEvent>) => {
const pointerEvent = event?.e;
if (readOnly) {
return;
}
@@ -229,11 +222,11 @@ export function DesignerCanvas({
onSelect(null);
};
const handleObjectModified = (e: any) => {
const handleObjectModified = (event: fabric.IEvent<MouseEvent>) => {
if (readOnly) {
return;
}
const target = e.target as FabricObjectWithId | undefined;
const target = event.target as FabricObjectWithId | undefined;
if (!target || typeof target.elementId !== 'string') {
return;
}
@@ -312,7 +305,7 @@ export function DesignerCanvas({
canvas.on('selection:cleared', handleSelectionCleared);
canvas.on('object:modified', handleObjectModified);
const handleEditingExited = (event: { target?: FabricObjectWithId & { text?: string } }) => {
const handleEditingExited = (event: fabric.IEvent<MouseEvent> & { target?: FabricObjectWithId & { text?: string } }) => {
if (readOnly) {
return;
}
@@ -327,14 +320,14 @@ export function DesignerCanvas({
canvas.requestRenderAll();
};
(canvas as any).on('editing:exited', handleEditingExited);
canvas.on('editing:exited', handleEditingExited);
return () => {
canvas.off('selection:created', handleSelection);
canvas.off('selection:updated', handleSelection);
canvas.off('selection:cleared', handleSelectionCleared);
canvas.off('object:modified', handleObjectModified);
(canvas as any).off('editing:exited', handleEditingExited);
canvas.off('editing:exited', handleEditingExited);
};
}, [onChange, onSelect, readOnly]);
@@ -702,7 +695,9 @@ export async function createFabricObject({
padding: 0, // No padding to fix large frame
});
if (qrImage) {
(qrImage as any).uniformScaling = true; // Lock aspect ratio
if (qrImage instanceof fabric.Image) {
qrImage.uniformScaling = true; // Lock aspect ratio
}
qrImage.lockScalingFlip = true;
qrImage.padding = 0;
qrImage.cornerColor = 'transparent';
@@ -801,7 +796,7 @@ export async function loadImageObject(
options?: { objectFit?: 'contain' | 'cover'; shadow?: string; padding?: number },
abortSignal?: AbortSignal,
): Promise<fabric.Object | null> {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
let resolved = false;
const resolveSafely = (value: fabric.Object | null) => {
if (resolved) {

View File

@@ -1,5 +1,18 @@
// import type { EventQrInviteLayout } from '../../api'; // Temporär deaktiviert wegen Modul-Fehler; definiere lokal falls nötig
type EventQrInviteLayout = any; // Placeholder für Typ, bis Pfad gefixt
// import type { EventQrInviteLayout } from '../../api'; // Temporär deaktiviert wegen Modul-Fehler; definiere lokal falls nötig
type EventQrInviteLayout = {
id: string;
name?: string;
description?: string | null;
subtitle?: string | null;
preview?: {
background?: string | null;
background_gradient?: { angle?: number; stops?: string[] } | null;
accent?: string | null;
text?: string | null;
qr_size_px?: number | null;
} | null;
formats?: string[];
};
export const CANVAS_WIDTH = 1240;
export const CANVAS_HEIGHT = 1754;

View File

@@ -5,8 +5,6 @@ import {
ADMIN_BASE_PATH,
ADMIN_DEFAULT_AFTER_LOGIN_PATH,
ADMIN_EVENTS_PATH,
ADMIN_HOME_PATH,
ADMIN_LOGIN_PATH,
ADMIN_LOGIN_START_PATH,
ADMIN_PUBLIC_LANDING_PATH,
} from './constants';