Link tenant packages to events and show usage in billing
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-06 12:54:33 +01:00
parent fa114ac0dc
commit 0291d537fb
11 changed files with 572 additions and 51 deletions

View File

@@ -35,6 +35,8 @@ vi.mock('react-i18next', () => ({
}));
vi.mock('lucide-react', () => ({
ChevronDown: () => <span />,
ChevronUp: () => <span />,
Package: () => <span />,
Receipt: () => <span />,
RefreshCcw: () => <span />,
@@ -113,7 +115,7 @@ vi.mock('../../lib/apiError', () => ({
}));
vi.mock('../../constants', () => ({
ADMIN_EVENT_VIEW_PATH: '/mobile/events',
ADMIN_EVENT_VIEW_PATH: (slug: string) => `/mobile/events/${slug}`,
adminPath: (path: string) => path,
}));
@@ -165,8 +167,155 @@ vi.mock('../../api', () => ({
}));
import MobileBillingPage from '../BillingPage';
import * as api from '../../api';
describe('MobileBillingPage', () => {
it('shows linked event information for a package', async () => {
vi.mocked(api.getTenantPackagesOverview).mockResolvedValueOnce({
activePackage: {
id: 11,
package_id: 501,
package_name: 'Pro Paket',
package_type: 'reseller',
included_package_slug: 'pro',
active: true,
used_events: 2,
remaining_events: 1,
price: 49,
currency: 'EUR',
purchased_at: '2024-01-01T00:00:00Z',
expires_at: null,
package_limits: {},
linked_events_count: 2,
current_event: {
id: 77,
slug: 'sommerfest',
name: { de: 'Sommerfest' },
status: 'published',
event_date: '2024-08-15T00:00:00Z',
linked_at: '2024-08-01T00:00:00Z',
},
},
packages: [],
});
render(<MobileBillingPage />);
await screen.findByText('Aktuell genutzt für');
expect(screen.getByText('Sommerfest')).toBeInTheDocument();
expect(screen.getByText('2 verknüpfte Events')).toBeInTheDocument();
});
it('shows only recent package history and can expand the rest', async () => {
vi.mocked(api.getTenantPackagesOverview).mockResolvedValueOnce({
activePackage: {
id: 1,
package_id: 100,
package_name: 'Aktives Paket',
package_type: 'reseller',
included_package_slug: 'standard',
active: true,
used_events: 0,
remaining_events: 1,
price: 29,
currency: 'EUR',
purchased_at: '2024-01-01T00:00:00Z',
expires_at: null,
package_limits: {},
},
packages: [
{
id: 1,
package_id: 100,
package_name: 'Aktives Paket',
package_type: 'reseller',
included_package_slug: 'standard',
active: true,
used_events: 0,
remaining_events: 1,
price: 29,
currency: 'EUR',
purchased_at: '2024-01-01T00:00:00Z',
expires_at: null,
package_limits: {},
},
{
id: 2,
package_id: 101,
package_name: 'Historie 1',
package_type: 'reseller',
included_package_slug: 'standard',
active: false,
used_events: 1,
remaining_events: 0,
price: 29,
currency: 'EUR',
purchased_at: '2023-12-01T00:00:00Z',
expires_at: null,
package_limits: {},
},
{
id: 3,
package_id: 102,
package_name: 'Historie 2',
package_type: 'reseller',
included_package_slug: 'standard',
active: false,
used_events: 1,
remaining_events: 0,
price: 29,
currency: 'EUR',
purchased_at: '2023-11-01T00:00:00Z',
expires_at: null,
package_limits: {},
},
{
id: 4,
package_id: 103,
package_name: 'Historie 3',
package_type: 'reseller',
included_package_slug: 'standard',
active: false,
used_events: 1,
remaining_events: 0,
price: 29,
currency: 'EUR',
purchased_at: '2023-10-01T00:00:00Z',
expires_at: null,
package_limits: {},
},
{
id: 5,
package_id: 104,
package_name: 'Historie 4',
package_type: 'reseller',
included_package_slug: 'standard',
active: false,
used_events: 1,
remaining_events: 0,
price: 29,
currency: 'EUR',
purchased_at: '2023-09-01T00:00:00Z',
expires_at: null,
package_limits: {},
},
],
});
render(<MobileBillingPage />);
await screen.findByText('Aktives Paket');
expect(screen.getByText('Historie 1')).toBeInTheDocument();
expect(screen.getByText('Historie 2')).toBeInTheDocument();
expect(screen.getByText('Historie 3')).toBeInTheDocument();
expect(screen.queryByText('Historie 4')).not.toBeInTheDocument();
fireEvent.click(screen.getByText('Verlauf anzeigen (1)'));
expect(await screen.findByText('Historie 4')).toBeInTheDocument();
expect(screen.getByText('Verlauf ausblenden')).toBeInTheDocument();
});
it('downloads receipts via the API helper', async () => {
render(<MobileBillingPage />);