feat(tickets): Erst-Text nach Antwort sperren, nur neueste Antwort editierbar

Sobald ein Ticket eine Antwort hat, ist der ursprüngliche Ticket-Text nicht
mehr bearbeitbar. Editierbar ist dann nur noch die neueste Antwort — und auch
die nur, bis die KI darauf reagiert hat (Status verlässt „Answered"). Ältere
Antworten im Verlauf (thread) bleiben wie bisher schreibgeschützt.

Regressionstest + Answered-Mock-Ticket ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 10:32:04 +02:00
parent 6b264a9b71
commit a0fa33da42
3 changed files with 44 additions and 4 deletions

View File

@@ -480,6 +480,29 @@ export function seedDb(): MockDb {
{ role: 'breeder', text: 'Im Juni.', at: '2026-06-13T18:00:00Z' },
],
},
{
// Beantwortetes Ticket (Answered): die KI hat noch NICHT reagiert. Der Erst-Text ist
// jetzt gesperrt (kein „Bearbeiten"), nur die neueste Antwort ist noch änderbar.
id: 'feedback-answered',
message: 'Bei Karlsson stimmt der Farbschlag nicht.',
context: 'gerbil-detail',
gerbilId: 'karlsson',
litterId: null,
contactId: null,
entityName: 'Karlsson',
url: 'http://localhost:5173/rennmaeuse/karlsson',
clientTimestamp: '2026-06-16T09:00:00Z',
userAgent: null,
createdAt: '2026-06-16T09:00:00Z',
status: 'Answered',
resolvedAt: null,
question: 'Welcher Farbschlag ist korrekt?',
answer: 'Karlsson ist Blau, nicht Schwarz.',
answeredAt: '2026-06-16T18:00:00Z',
fixNote: null,
agentContext: null,
thread: [],
},
{
id: 'feedback-open',
message: 'Das Gewicht wird auf der Verlaufskurve falsch gerundet.',

View File

@@ -38,7 +38,8 @@ test.describe('Meine Tickets', () => {
const open = page.locator('.tickets-filter').filter({ hasText: tt.filters.open })
const closed = page.locator('.tickets-filter').filter({ hasText: tt.filters.closed })
await expect(dialog.locator('.tickets-filter__count')).toHaveText('1')
await expect(open.locator('.tickets-filter__count')).toHaveText('1')
// „Offen" = Open + Answered → das offene + das beantwortete Ticket = 2.
await expect(open.locator('.tickets-filter__count')).toHaveText('2')
await expect(closed.locator('.tickets-filter__count')).toHaveText('1')
// „Offen": nur das offene Ticket.
@@ -131,6 +132,20 @@ test.describe('Meine Tickets', () => {
await expect(page.getByText('Korrigierte Beschreibung des Fehlers.')).toBeVisible()
})
test('beantwortetes Ticket: Erst-Text gesperrt, nur die neueste Antwort ist änderbar', async ({
page,
}) => {
await page.goto('/hilfe/tickets')
// „Offen"-Ansicht (Default) zeigt auch beantwortete Tickets (Answered).
const card = page.locator('.ticket-card').filter({ hasText: 'Karlsson ist Blau' })
await expect(card).toBeVisible()
// Erst-Text ist gesperrt: kein „Bearbeiten"-Button mehr, sobald es eine Antwort gibt.
await expect(card.getByRole('button', { name: tt.edit })).toHaveCount(0)
// Die neueste Antwort ist dagegen noch änderbar (KI hat noch nicht reagiert → Answered).
await expect(card.getByRole('button', { name: tt.amendAnswer })).toBeVisible()
})
test('Rückfrage beantworten: Züchterin sieht Frage, antwortet, Badge wird „Beantwortet"', async ({
page,
}) => {