QR-Codes-UI zu Einladungen umgebaut mit PDF-Export und Druckanzeige + Customizer

This commit is contained in:
Codex Agent
2025-10-30 07:12:27 +01:00
parent d781448914
commit 06df61f706
20 changed files with 1724 additions and 537 deletions

View File

@@ -13,7 +13,15 @@ import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { AdminLayout } from '../components/AdminLayout';
import { createEvent, getEvent, getTenantPackagesOverview, updateEvent, getPackages, getEventTypes } from '../api';
import {
createEvent,
getEvent,
getTenantPackagesOverview,
updateEvent,
getPackages,
getEventTypes,
TenantEvent,
} from '../api';
import { isAuthError } from '../auth/tokens';
import { ADMIN_BILLING_PATH, ADMIN_EVENT_VIEW_PATH, ADMIN_EVENTS_PATH } from '../constants';
@@ -65,7 +73,6 @@ export default function EventFormPage() {
});
const [autoSlug, setAutoSlug] = React.useState(true);
const [originalSlug, setOriginalSlug] = React.useState<string | null>(null);
const [loading, setLoading] = React.useState(isEdit);
const [saving, setSaving] = React.useState(false);
const [error, setError] = React.useState<string | null>(null);
const [readOnlyPackageName, setReadOnlyPackageName] = React.useState<string | null>(null);
@@ -107,6 +114,17 @@ export default function EventFormPage() {
setReadOnlyPackageName((prev) => prev ?? activePackage.package_name);
}, [isEdit, activePackage]);
const {
data: loadedEvent,
isLoading: eventLoading,
error: eventLoadError,
} = useQuery<TenantEvent>({
queryKey: ['tenant', 'events', slugParam],
queryFn: () => getEvent(slugParam!),
enabled: Boolean(isEdit && slugParam),
staleTime: 60_000,
});
React.useEffect(() => {
if (isEdit) {
return;
@@ -128,54 +146,45 @@ export default function EventFormPage() {
}, [eventTypes, isEdit]);
React.useEffect(() => {
let cancelled = false;
if (!isEdit || !slugParam) {
setLoading(false);
return () => {
cancelled = true;
};
if (!isEdit || !loadedEvent) {
return;
}
(async () => {
try {
const event = await getEvent(slugParam);
if (cancelled) return;
const name = normalizeName(event.name);
setForm((prev) => ({
...prev,
name,
slug: event.slug,
date: event.event_date ? event.event_date.slice(0, 10) : '',
eventTypeId: event.event_type_id ?? prev.eventTypeId,
isPublished: event.status === 'published',
package_id: event.package?.id ? Number(event.package.id) : prev.package_id,
}));
setOriginalSlug(event.slug);
setReadOnlyPackageName(event.package?.name ?? null);
setEventPackageMeta(event.package
? {
id: Number(event.package.id),
name: event.package.name ?? (typeof event.package === 'string' ? event.package : ''),
purchasedAt: event.package.purchased_at ?? null,
expiresAt: event.package.expires_at ?? null,
}
: null);
setAutoSlug(false);
} catch (err) {
if (!isAuthError(err)) {
setError('Event konnte nicht geladen werden.');
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
})();
const name = normalizeName(loadedEvent.name);
return () => {
cancelled = true;
};
}, [isEdit, slugParam]);
setForm((prev) => ({
...prev,
name,
slug: loadedEvent.slug,
date: loadedEvent.event_date ? loadedEvent.event_date.slice(0, 10) : '',
eventTypeId: loadedEvent.event_type_id ?? prev.eventTypeId,
isPublished: loadedEvent.status === 'published',
package_id: loadedEvent.package?.id ? Number(loadedEvent.package.id) : prev.package_id,
}));
setOriginalSlug(loadedEvent.slug);
setReadOnlyPackageName(loadedEvent.package?.name ?? null);
setEventPackageMeta(loadedEvent.package
? {
id: Number(loadedEvent.package.id),
name: loadedEvent.package.name ?? (typeof loadedEvent.package === 'string' ? loadedEvent.package : ''),
purchasedAt: loadedEvent.package.purchased_at ?? null,
expiresAt: loadedEvent.package.expires_at ?? null,
}
: null);
setAutoSlug(false);
}, [isEdit, loadedEvent]);
React.useEffect(() => {
if (!isEdit || !eventLoadError) {
return;
}
if (!isAuthError(eventLoadError)) {
setError('Event konnte nicht geladen werden.');
}
}, [isEdit, eventLoadError]);
const loading = isEdit ? eventLoading : false;
function handleNameChange(value: string) {
setForm((prev) => ({ ...prev, name: value }));