Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
410cd7c123 Merge pull request 'feat(sprint-73): sticky chat-invoerveld + reverse-chronologische idee-timeline' (#1) from feat/sprint-tzw3dt4h into main
Some checks are pending
CI / Lint, Typecheck, Test & Build (push) Waiting to run
CI / Detect deploy-relevant changes (push) Blocked by required conditions
CI / Deploy Preview (PR) (push) Blocked by required conditions
CI / Deploy Production (main) (push) Blocked by required conditions
CI / Deploy Manual (workflow_dispatch) (push) Waiting to run
Reviewed-on: #1
2026-05-15 21:40:59 +02:00
Scrum4Me Agent
c9d4122b3a feat(timeline): reverse-chronologische volgorde + unit-test merge/sort
Some checks are pending
CI / Lint, Typecheck, Test & Build (pull_request) Waiting to run
CI / Detect deploy-relevant changes (pull_request) Blocked by required conditions
CI / Deploy Preview (PR) (pull_request) Blocked by required conditions
CI / Deploy Production (main) (pull_request) Blocked by required conditions
CI / Deploy Manual (workflow_dispatch) (pull_request) Waiting to run
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 <noreply@anthropic.com>
2026-05-15 20:08:42 +02:00
Scrum4Me Agent
22781365e6 feat(timeline): sticky chat-input bovenaan timeline-sectie
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 <noreply@anthropic.com>
2026-05-15 19:57:01 +02:00
Scrum4Me Agent
a3303a605b 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 <noreply@anthropic.com>
2026-05-15 19:49:15 +02:00
3 changed files with 71 additions and 21 deletions

View file

@ -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([])
})
})

View file

@ -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 },
})

View file

@ -99,6 +99,23 @@ const USER_QUESTION_STATUS_LABEL: Record<TimelineUserQuestion['status'], string>
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,28 +124,18 @@ 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
return (
<div className="space-y-4" {...debugProps('idea-timeline', 'IdeaTimeline', 'components/ideas/idea-timeline.tsx')}>
<div {...debugProps('idea-timeline', 'IdeaTimeline', 'components/ideas/idea-timeline.tsx')}>
{showChatInput && (
<div className="sticky top-0 z-10 bg-background border-b border-border pb-3 mb-4" data-debug-id="idea-timeline__chat-input">
<UserChatInput ideaId={ideaId} isDemo={isDemo} />
</div>
)}
<div className="space-y-4">
{merged.length === 0 ? (
<p className="text-sm text-muted-foreground py-8 text-center italic">
Nog geen activiteit op dit idee.
@ -252,8 +259,7 @@ export function IdeaTimeline({
})}
</ol>
)}
{showChatInput && <UserChatInput ideaId={ideaId} isDemo={isDemo} />}
</div>
</div>
)
}