From a3303a605b51ce44a412446675f7131c0219d95f Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Fri, 15 May 2026 19:49:15 +0200 Subject: [PATCH 1/3] fix(ideas): consistent desc orderBy for userQuestions in timeline page userQuestions waren als enige `asc` opgehaald terwijl logs en questions `desc` gebruiken; het timeline-component sorteert toch client-side `desc`, maar consistentie voorkomt verwarring bij toekomstige wijzigingen. Co-Authored-By: Claude Sonnet 4.6 --- app/(app)/ideas/[id]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(app)/ideas/[id]/page.tsx b/app/(app)/ideas/[id]/page.tsx index 80d946c..dcf7543 100644 --- a/app/(app)/ideas/[id]/page.tsx +++ b/app/(app)/ideas/[id]/page.tsx @@ -89,7 +89,7 @@ export default async function IdeaDetailPage({ params, searchParams }: PageProps const userQuestionsRaw = await prisma.userQuestion.findMany({ where: { idea_id: id }, - orderBy: { created_at: 'asc' }, + orderBy: { created_at: 'desc' }, take: 100, select: { id: true, question: true, answer: true, status: true, created_at: true }, }) From 22781365e6ff7b652c6a81a5d804621b0e4ce6af Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Fri, 15 May 2026 19:57:01 +0200 Subject: [PATCH 2/3] feat(timeline): sticky chat-input bovenaan timeline-sectie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verplaats UserChatInput naar boven in IdeaTimeline en geef de wrapper sticky top-0 z-10 bg-background border-b border-border — input blijft zichtbaar terwijl de timeline-items eronder doorscrollt. Co-Authored-By: Claude Sonnet 4.6 --- components/ideas/idea-timeline.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/components/ideas/idea-timeline.tsx b/components/ideas/idea-timeline.tsx index b81eb42..5e17b94 100644 --- a/components/ideas/idea-timeline.tsx +++ b/components/ideas/idea-timeline.tsx @@ -128,7 +128,13 @@ export function IdeaTimeline({ const showChatInput = planMd !== null return ( -
+
+ {showChatInput && ( +
+ +
+ )} +
{merged.length === 0 ? (

Nog geen activiteit op dit idee. @@ -252,8 +258,7 @@ export function IdeaTimeline({ })} )} - - {showChatInput && } +

) } From c9d4122b3a5b635baf2171ba1168ed6448afc9a5 Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Fri, 15 May 2026 20:08:42 +0200 Subject: [PATCH 3/3] feat(timeline): reverse-chronologische volgorde + unit-test merge/sort Extraheert mergeTimelineItems uit IdeaTimeline als exporteerbare functie en voegt een vitest-test toe die de nieuwste-boven-sortering verifieert. revalidatePath in user-questions action was al aanwezig. Co-Authored-By: Claude Sonnet 4.6 --- .../components/idea-timeline-merge.test.ts | 44 +++++++++++++++++++ components/ideas/idea-timeline.tsx | 35 ++++++++------- 2 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 __tests__/components/idea-timeline-merge.test.ts diff --git a/__tests__/components/idea-timeline-merge.test.ts b/__tests__/components/idea-timeline-merge.test.ts new file mode 100644 index 0000000..4af127d --- /dev/null +++ b/__tests__/components/idea-timeline-merge.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from 'vitest' +import { mergeTimelineItems } from '@/components/ideas/idea-timeline' + +describe('mergeTimelineItems', () => { + it('sorteert reverse-chronologisch: nieuwste entry staat eerst', () => { + const logs = [ + { id: 'l1', type: 'NOTE', content: 'oud', metadata: null, created_at: '2024-01-01T10:00:00.000Z' }, + ] + const questions = [ + { + id: 'q1', + question: 'Vraag?', + options: null, + status: 'open' as const, + answer: null, + created_at: '2024-01-03T12:00:00.000Z', + expires_at: '2024-01-10T12:00:00.000Z', + }, + ] + const userQuestions = [ + { + id: 'uq1', + question: 'Mijn vraag', + answer: null, + status: 'pending' as const, + created_at: '2024-01-02T08:00:00.000Z', + }, + ] + + const result = mergeTimelineItems(logs, questions, userQuestions) + + expect(result).toHaveLength(3) + expect(result[0].created_at).toBe('2024-01-03T12:00:00.000Z') + expect(result[0].kind).toBe('question') + expect(result[1].created_at).toBe('2024-01-02T08:00:00.000Z') + expect(result[1].kind).toBe('user_question') + expect(result[2].created_at).toBe('2024-01-01T10:00:00.000Z') + expect(result[2].kind).toBe('log') + }) + + it('geeft lege lijst terug bij geen input', () => { + expect(mergeTimelineItems([], [], [])).toEqual([]) + }) +}) diff --git a/components/ideas/idea-timeline.tsx b/components/ideas/idea-timeline.tsx index 5e17b94..053289c 100644 --- a/components/ideas/idea-timeline.tsx +++ b/components/ideas/idea-timeline.tsx @@ -99,6 +99,23 @@ const USER_QUESTION_STATUS_LABEL: Record answered: 'Beantwoord', } +export type TimelineEntry = + | { kind: 'log'; created_at: string; data: TimelineLog } + | { kind: 'question'; created_at: string; data: TimelineQuestion } + | { kind: 'user_question'; created_at: string; data: TimelineUserQuestion } + +export function mergeTimelineItems( + logs: TimelineLog[], + questions: TimelineQuestion[], + userQuestions: TimelineUserQuestion[], +): TimelineEntry[] { + return [ + ...logs.map((l) => ({ kind: 'log' as const, created_at: l.created_at, data: l })), + ...questions.map((q) => ({ kind: 'question' as const, created_at: q.created_at, data: q })), + ...userQuestions.map((uq) => ({ kind: 'user_question' as const, created_at: uq.created_at, data: uq })), + ].sort((a, b) => (a.created_at < b.created_at ? 1 : -1)) +} + export function IdeaTimeline({ logs, questions, @@ -107,23 +124,7 @@ export function IdeaTimeline({ ideaId, isDemo = false, }: Props) { - const merged = [ - ...logs.map((l) => ({ - kind: 'log' as const, - created_at: l.created_at, - data: l, - })), - ...questions.map((q) => ({ - kind: 'question' as const, - created_at: q.created_at, - data: q, - })), - ...userQuestions.map((uq) => ({ - kind: 'user_question' as const, - created_at: uq.created_at, - data: uq, - })), - ].sort((a, b) => (a.created_at < b.created_at ? 1 : -1)) + const merged = mergeTimelineItems(logs, questions, userQuestions) const showChatInput = planMd !== null