Koala logo Design

Note bubble

A compact icon-with-count trigger rendered next to the client name on the quote and transaction list rows. Clicking opens a modal that fetches notes one-at-a-time on demand (newest first), with prev/next pagination and a "View all" link to the entity's notes tab.

Trigger

The chat-bubble icon (MessageSquare) with the note count rendered as a small overlaid digit. Standalone button — no surrounding pill — so it reads as part of the row rather than a separate badge.

<button type="button"
        x-on:click.stop.prevent="$dispatch('open-note-browser', {
            url: '/partner/quotes/@quoteId?handler=Note',
            allUrl: '/partner/quotes/@quoteId?tab=notes',
            total: @noteCount
        })"
        class="relative inline-flex shrink-0 items-center justify-center
               w-8 h-8 rounded-md text-gray-500 dark:text-gray-400
               hover:text-gray-700 dark:hover:text-gray-200
               hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
    <koala-icon name="MessageSquare" class="w-7 h-7" />
    <span class="absolute inset-0 flex items-center justify-center
                 text-[10px] font-bold leading-none -translate-y-px
                 pointer-events-none tabular-nums">@noteCount</span>
</button>

Modal

The trigger dispatches an open-note-browser window event with { url, allUrl, total }. The shared _NoteBrowserModal partial listens, opens, and clicks a hidden Alpine-AJAX loader anchor to fetch index 0 (newest first) into #note-browser-content. Prev / Next buttons step the index, refire the loader. The "View all" footer link routes to the entity's notes tab.

Notes

SJ
Sarah Johnson
5 minutes ago

Spoke to the seller — happy to proceed once the survey results land.

1 of 3
View all notes
@* Render once per page that contains note bubbles *@
<partial name="_NoteBrowserModal" />

@* The modal listens for the window event and clicks a hidden
   Alpine-AJAX loader anchor — server returns _NoteBrowserItem
   partial with #note-browser-content for swap. *@
public async Task<IActionResult> OnGetNoteAsync(
    Guid id, int index, CancellationToken cancellationToken)
{
    // tenant-scoped query, OrderByDescending(Id), Skip(index).Take(1)
    var item = new NoteBrowserItemModel(...);
    return Partial("Shared/_NoteBrowserItem", item);
}

Empty state

The notes tab itself uses the same MessageSquare glyph for its empty state so the pattern is recognisable across the bubble, the modal, and the tab.

No notes yet

Notes added here will appear in this list.

<koala-empty-state icon="MessageSquare" title="No notes yet">
    Notes added here will appear in this list.
</koala-empty-state>

Source

Triggers: _QuoteNoteBubble.cshtml and _TransactionNoteBubble.cshtml in the partner area. Modal: Pages/Shared/_NoteBrowserModal.cshtml. Per-note partial: Pages/Shared/_NoteBrowserItem.cshtml. Backend handlers: OnGetNoteAsync(Guid id, int index) on Partner ViewQuote / ViewTransaction page models — sanitises content through MarkdownService.SanitizeHtml.