Phase 1C: Frontend unified contact UI
This commit is contained in:
@@ -7,7 +7,7 @@ import { GlobalSearchResultsPage } from '@/pages/GlobalSearchResults';
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
useGlobalSearch: () => ({
|
||||
data: [
|
||||
{ id: '1', type: 'company', name: 'TestCorp GmbH', description: 'IT company', url: '/companies/1' },
|
||||
{ id: '1', type: 'contact', name: 'TestCorp GmbH', description: 'IT company', url: '/contacts/1' },
|
||||
{ id: '2', type: 'contact', name: 'Max Mustermann', description: 'CEO', url: '/contacts/2' },
|
||||
],
|
||||
isLoading: false,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
useGlobalSearch: () => ({
|
||||
data: [
|
||||
{ id: '1', type: 'company', name: 'TestCorp GmbH', description: 'IT company', url: '/companies/1' },
|
||||
{ id: '1', type: 'contact', name: 'TestCorp GmbH', description: 'IT company', url: '/contacts/1' },
|
||||
{ id: '2', type: 'contact', name: 'Max Mustermann', description: 'CEO', url: '/contacts/2' },
|
||||
{ id: '3', type: 'mail', name: 'Test Email', description: 'Subject: Hello', url: '/mail/3' },
|
||||
{ id: '4', type: 'file', name: 'Document.pdf', description: 'PDF file', url: '/dms/4' },
|
||||
|
||||
@@ -27,17 +27,17 @@ beforeEach(() => {
|
||||
|
||||
describe('BulkTagDialog', () => {
|
||||
it('renders dialog when open', async () => {
|
||||
render(<BulkTagDialog open={true} entityType="company" entityIds={['c1', 'c2']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
render(<BulkTagDialog open={true} entityType="contact" entityIds={['c1', 'c2']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows selected entity count', async () => {
|
||||
render(<BulkTagDialog open={true} entityType="company" entityIds={['c1', 'c2', 'c3']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
render(<BulkTagDialog open={true} entityType="contact" entityIds={['c1', 'c2', 'c3']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
expect(screen.getByText('3 ausgewaehlte Eintraege')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('loads and displays available tags', async () => {
|
||||
render(<BulkTagDialog open={true} entityType="company" entityIds={['c1']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
render(<BulkTagDialog open={true} entityType="contact" entityIds={['c1']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
await waitFor(() => {
|
||||
expect(fetchTags).toHaveBeenCalled();
|
||||
});
|
||||
@@ -48,7 +48,7 @@ describe('BulkTagDialog', () => {
|
||||
});
|
||||
|
||||
it('toggles tag selection on click', async () => {
|
||||
render(<BulkTagDialog open={true} entityType="company" entityIds={['c1']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
render(<BulkTagDialog open={true} entityType="contact" entityIds={['c1']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
await waitFor(() => {
|
||||
expect(fetchTags).toHaveBeenCalled();
|
||||
});
|
||||
@@ -62,7 +62,7 @@ describe('BulkTagDialog', () => {
|
||||
it('calls bulkAssignTags on assign button click', async () => {
|
||||
const onAssigned = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
render(<BulkTagDialog open={true} entityType="company" entityIds={['c1', 'c2']} onClose={onClose} onAssigned={onAssigned} />);
|
||||
render(<BulkTagDialog open={true} entityType="contact" entityIds={['c1', 'c2']} onClose={onClose} onAssigned={onAssigned} />);
|
||||
await waitFor(() => {
|
||||
expect(fetchTags).toHaveBeenCalled();
|
||||
});
|
||||
@@ -73,14 +73,14 @@ describe('BulkTagDialog', () => {
|
||||
await waitFor(() => {
|
||||
expect(bulkAssignTags).toHaveBeenCalledWith({
|
||||
tag_ids: ['t1'],
|
||||
entity_type: 'company',
|
||||
entity_type: 'contact',
|
||||
entity_ids: ['c1', 'c2'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render when closed', () => {
|
||||
render(<BulkTagDialog open={false} entityType="company" entityIds={['c1']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
render(<BulkTagDialog open={false} entityType="contact" entityIds={['c1']} onClose={vi.fn()} onAssigned={vi.fn()} />);
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { TagPicker } from '@/components/tags/TagPicker';
|
||||
|
||||
const mockTags = [
|
||||
{ id: 't1', name: 'VIP-Kunde', color: '#EF4444', created_by: 'u1', usage_count: 5 },
|
||||
{ id: 't2', name: 'Lead', color: '#3B82F6', created_by: 'u1', usage_count: 12 },
|
||||
{ id: 't3', name: 'Aktiv', color: '#10B981', created_by: 'u1', usage_count: 3 },
|
||||
];
|
||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
|
||||
const mockFetchTags = vi.fn();
|
||||
const mockAssignTag = vi.fn();
|
||||
@@ -15,44 +8,48 @@ const mockUnassignTag = vi.fn();
|
||||
const mockCreateTag = vi.fn();
|
||||
|
||||
vi.mock('@/api/tags', () => ({
|
||||
fetchTags: (...args: unknown[]) => mockFetchTags(...args),
|
||||
assignTag: (...args: unknown[]) => mockAssignTag(...args),
|
||||
unassignTag: (...args: unknown[]) => mockUnassignTag(...args),
|
||||
createTag: (...args: unknown[]) => mockCreateTag(...args),
|
||||
fetchTags: (...args: any[]) => mockFetchTags(...args),
|
||||
assignTag: (...args: any[]) => mockAssignTag(...args),
|
||||
unassignTag: (...args: any[]) => mockUnassignTag(...args),
|
||||
createTag: (...args: any[]) => mockCreateTag(...args),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/Toast', () => ({
|
||||
useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() }),
|
||||
}));
|
||||
import { TagPicker } from '@/components/tags/TagPicker';
|
||||
|
||||
const mockTags = [
|
||||
{ id: 't1', name: 'VIP-Kunde', color: '#10B981', created_by: 'u1' },
|
||||
{ id: 't2', name: 'Lead', color: '#3B82F6', created_by: 'u1' },
|
||||
{ id: 't3', name: 'Archiv', color: '#6B7280', created_by: 'u1' },
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockFetchTags.mockResolvedValue(mockTags);
|
||||
mockAssignTag.mockResolvedValue({ id: 'a1', tag_id: 't1', entity_type: 'company', entity_id: 'c1' });
|
||||
mockAssignTag.mockResolvedValue({ id: 'a1', tag_id: 't1', entity_type: 'contact', entity_id: 'c1' });
|
||||
mockUnassignTag.mockResolvedValue(undefined);
|
||||
mockCreateTag.mockResolvedValue({ id: 't4', name: 'Neu', color: '#F59E0B', created_by: 'u1' });
|
||||
});
|
||||
|
||||
describe('TagPicker', () => {
|
||||
it('renders the tag picker container', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
expect(screen.getByTestId('tag-picker')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders assigned tags section', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
expect(screen.getByText('Zugewiesene Tags')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows no tags assigned message when empty', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Tags zugewiesen')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('loads and displays available tags', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
await waitFor(() => {
|
||||
expect(mockFetchTags).toHaveBeenCalled();
|
||||
});
|
||||
@@ -64,18 +61,18 @@ describe('TagPicker', () => {
|
||||
});
|
||||
|
||||
it('assigns a tag when clicking available tag', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('VIP-Kunde')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText('VIP-Kunde'));
|
||||
await waitFor(() => {
|
||||
expect(mockAssignTag).toHaveBeenCalledWith({ tag_id: 't1', entity_type: 'company', entity_id: 'c1' });
|
||||
expect(mockAssignTag).toHaveBeenCalledWith({ tag_id: 't1', entity_type: 'contact', entity_id: 'c1' });
|
||||
});
|
||||
});
|
||||
|
||||
it('shows create tag form when clicking create button', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Neues Tag')).toBeInTheDocument();
|
||||
});
|
||||
@@ -86,7 +83,7 @@ describe('TagPicker', () => {
|
||||
});
|
||||
|
||||
it('renders search input for tags', async () => {
|
||||
render(<TagPicker entityType="company" entityId="c1" />);
|
||||
render(<TagPicker entityType="contact" entityId="c1" />);
|
||||
expect(screen.getByLabelText('Tag suchen')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user