Patterns
Change notes (threaded)
Notes with tags, the pinned decision on top, replies nested and folded after one level. Reply works.
- KAKarim AhmedOwnerDecisionWe publish v7 today at ৳70. Monthly parkers keep their price until their next bill on the 25th.
- NJNusrat JahanManagerNoted. I'll brief the attendants at the 2 pm shift change.
- TSTania SultanaAccountantQuestionDoes the daily cap change for motorcycles too, or only cars?
- KAKarim AhmedOwnerOnly cars. Bikes stay at ৳150.
- SIShahidul IslamSupervisorChange requestCan we add a 30-minute rate for the Banani lunch rush?
change-notes.tsx
"use client";
import { ChangeNotes, type NoteThread } from "@waveinno/ui";
import * as React from "react";
export default function NotesDemo() {
const [notes, setNotes] = React.useState<NoteThread[]>([
{
id: "n1",
author: { name: "Karim Ahmed", role: "Owner" },
at: "Today, 11:20",
tag: { label: "Decision", tone: "success" },
pinned: true,
text: "We publish v7 today at ৳70. Monthly parkers keep their price until their next bill on the 25th.",
replies: [
{
id: "n1r1",
author: { name: "Nusrat Jahan", role: "Manager" },
at: "Today, 11:31",
text: "Noted. I'll brief the attendants at the 2 pm shift change.",
replies: [{ id: "n1r1r1", author: { name: "Farhana Akter", role: "Attendant" }, at: "Today, 11:45", text: "Should we print the new rate card for the booth?" }],
},
],
},
{
id: "n2",
author: { name: "Tania Sultana", role: "Accountant" },
at: "Yesterday, 17:05",
tag: { label: "Question", tone: "info" },
text: "Does the daily cap change for motorcycles too, or only cars?",
replies: [{ id: "n2r1", author: { name: "Karim Ahmed", role: "Owner" }, at: "Yesterday, 18:40", text: "Only cars. Bikes stay at ৳150." }],
},
{ id: "n3", author: { name: "Shahidul Islam", role: "Supervisor" }, at: "Mon, 10:12", tag: { label: "Change request", tone: "warning" }, text: "Can we add a 30-minute rate for the Banani lunch rush?" },
]);
const add = (list: NoteThread[], parentId: string, text: string): NoteThread[] =>
list.map((n) =>
n.id === parentId
? { ...n, replies: [...(n.replies ?? []), { id: `r${Date.now()}`, author: { name: "You", role: "Playground" }, at: "just now", text }] }
: { ...n, replies: n.replies ? add(n.replies, parentId, text) : n.replies },
);
return (
<div className="max-w-2xl">
<ChangeNotes notes={notes} onReply={(id, text) => setNotes((all) => add(all, id, text))} />
</div>
);
}Import#
Everything comes from the package root. The example above uses:
your-component.tsx
import { ChangeNotes, type NoteThread } from "@waveinno/ui";Props#
Read from the source of this version, so they match the code exactly.
<ChangeNotes>
src/components/patterns/timeline.tsx| Prop | Type | Default | Description |
|---|---|---|---|
notesrequired | NoteThread[] | — | |
onReply | (parentId: string, text: string) => void | — | |
labels | Partial<{ reply: string; replies: (n: number) => string; hide: string; placeholder: string; send: string }> | — | |
className | string | — |
Install#
If the project does not have the package yet, add it, then follow the setup for Next.js or Vite once.
$ npm install @waveinno/ui