useActivityLog
Writes audit entries to the backend. useCrud already calls it on every create, update and delete.
Import
ts
import { activityEntityLabel, useActivityLog } from '@shared/composables/useActivityLog'Signature
ts
function useActivityLog(): {
log: (entry: Omit<ActivityEntry, 'id' | 'timestamp'>) => Promise<void>
}
function activityEntityLabel(entity: string): stringReturns
| Member | Type | Description |
|---|---|---|
log | (entry) => Promise<void> | POSTs to /activity-log. The id and timestamp are the server's. |
ActivityEntry
| Key | Type | Description |
|---|---|---|
id | string | Assigned by the backend. |
action | 'create' | 'update' | 'delete' | string | A free string, so an app can log its own verbs. |
entity | string | The resource. A stable identifier, not a label. |
entityId | string | number | Which row. |
details | Record<string, unknown> | Whatever is worth keeping — usually the payload. |
userId | string | Who did it. |
timestamp | Date | Assigned by the backend. |
activityEntityLabel
Turns an entity string into something readable. It looks for activity-log.entities.{entity} in lower case and returns the translation if it exists, or the entity as logged if it does not.
That fallback matters because entity is a free string — a module name, a table name, whatever the app passed. Only the ones with a key translate; the rest render exactly as they were recorded.
Example
vue
<script setup lang="ts">
import { activityEntityLabel, useActivityLog } from '@shared/composables/useActivityLog'
const { log } = useActivityLog()
async function approve(id: number) {
await fetch(`/api/expenses/${id}/approve`, { method: 'POST' })
await log({
action: 'approve',
entity: 'expense',
entityId: id,
details: { approvedBy: 'finance' },
})
}
</script>
<template>
<div>
<button type="button" @click="approve(41)">
Approve
</button>
<p>{{ activityEntityLabel('expense') }}</p>
</div>
</template>Notes
entitymust not change with the language. It is data in the audit trail, so it is'warehouse'— never "Warehouse", never a translated word.activityEntityLabelis what turns it into something to read, at the point it is displayed.logis a plain POST and it rejects on failure.useCrudcalls it fire-and-forget, so a failed audit never breaks the operation that triggered it. Do the same if the entry is not more important than the action.- The activity-log page reads entries back through its own
useCrudinstance, and setsactivityLog: falsethere so reading the log does not write to it. - The whole feature sits behind
config.features.activityLog.
Related
useCrud— the automatic caller.- Auth and permissions — who is recorded.