Demo

Change Log

Show the activity feed for a business record with the ChangeLog component and related hooks from @quan-erp/base-frontend. Backend writes go through ChangeLogService — frontend only renders and reacts.

Overview

ChangeLog is the shared UI for audit/activity timelines. Scope every feed by pluginName + reference.prefix + reference.number — the same triple the backend ChangeLogService uses when writing entries.

  • Import ChangeLog and hooks from @quan-erp/base-frontend
  • reference.prefix — entity kind (e.g. SO, EMP, INV)
  • reference.number — record id/code as string
  • Backend must call ChangeLogService.addChangeLog for entries to appear

1. ChangeLog component

Drop the component on a detail page (or tab). It loads and renders the feed for that reference.

  • Signature: ({ pluginName, reference: { prefix, number } }) => ReactNode
  • pluginName must match the value used when writing logs on the backend
  • Always String(id) for reference.number
TSXdetail page
1import { ChangeLog } from "@quan-erp/base-frontend"; 2import metadata from "../../../module.metadata.json" with { type: "json" }; 3 4export function SalesOrderDetail({ id }: { id: number }) { 5 return ( 6 <ChangeLog 7 pluginName={metadata.name} 8 reference={{ prefix: "SO", number: String(id) }} 9 /> 10 ); 11}

2. Query & mutation hooks

Use these when you need a custom feed UI instead of the stock ChangeLog component.

  • useChangeLogQuery(query, option?) — paginated list; query needs pluginName, referencePrefix, referenceNumber; optional parentId
  • useInfiniteChangeLog(pluginName, referencePrefix, referenceNumber, limit?) — infinite scroll feed
  • useReactChangeLogQuery() — add a reaction { reaction, reactBy, changeLogId }
  • useRemoveReactChangeLogQuery() — remove reaction by change-log id
  • clearChangeLogCache({ pluginName, reference, logId? }) — invalidate cached entries after writes
TSXcustom query
1import { 2 useChangeLogQuery, 3 useReactChangeLogQuery, 4 clearChangeLogCache, 5} from "@quan-erp/base-frontend"; 6 7const { data: logs = [] } = useChangeLogQuery({ 8 pluginName: metadata.name, 9 referencePrefix: "SO", 10 referenceNumber: String(orderId), 11 currentPage: 1, 12 pageSize: 20, 13}); 14 15const react = useReactChangeLogQuery(); 16await react.mutateAsync({ 17 reaction: "👍", 18 reactBy: currentUserId, 19 changeLogId: logs[0].id, 20}); 21 22clearChangeLogCache({ 23 pluginName: metadata.name, 24 reference: { prefix: "SO", number: String(orderId) }, 25});

3. Result shape

Entries returned by the hooks follow GetChangeLogDto.

TSGetChangeLogDto (shape)
1interface GetChangeLogDto { 2 id: number; 3 message: string; 4 createdBy: UserDto; 5 isCreatedBySystem: boolean; 6 pluginName: string; 7 referencePrefix: string; 8 referenceNumber: string; 9 createDate: string; 10 reactions: ChangeLogReaction[]; 11} 12 13interface ChangeLogReaction { 14 id: number; 15 reaction: string; 16 reactBy: UserDto; 17}

4. Backend pairing

The UI only shows what ChangeLogService recorded. On create/update/status changes, call addChangeLog with the same pluginName / referencePrefix / referenceNumber the page passes to <ChangeLog />.

  • See Documentation → Backend → Builtin Services → ChangeLogService
  • Mismatch in prefix/number/pluginName = empty feed
  • After server-side writes from another client, clearChangeLogCache if you keep a long-lived view open

Critical rules

  • Import from @quan-erp/base-frontend — not shared-ui
  • Keep frontend reference and backend reference fields identical
  • reference.number is always a string
  • Prefer <ChangeLog /> for standard timelines; use hooks only for custom layouts
  • Do not invent a plugin-local activity table when ChangeLogService already covers the use case

Checklist

  • Backend addChangeLog on the relevant mutations
  • Detail page renders <ChangeLog pluginName reference />
  • prefix/number match backend writes
  • Optional: reactions via useReactChangeLogQuery
  • Verify feed after create/update in the ERP UI

Common failures

  • Empty feed — backend never wrote logs, or pluginName/prefix/number mismatch
  • Import error — ChangeLog is not on @quan-erp/shared-ui
  • Stale list — forgot clearChangeLogCache after external write
  • Reaction fails — missing reactBy / changeLogId or permission
  • Wrong record’s history — number not String(id) or wrong prefix