@dcodegroup-au/vue-activity-log

A Vue 3 component for displaying activity logs with timeline, comments, and notifications.
3,805
Install
npm install @dcodegroup-au/vue-activity-log
Latest Version:0.1.20
Vue:3
License:MIT
Last Updated:Sep 22, 2026
Links: npm
Maintainer: dcodegroup-forge

Vue Activity Log

Vue 3 components for displaying activity logs from dcodegroup/activity-log: timeline view, table list, comments, emoji reactions, email/SMS previews, and delete confirmation modals.

Package: @dcodegroup-au/vue-activity-log
Peer backend: dcodegroup/activity-log

Requirements

Dependency Version
Vue >=3.0.0
axios any
vue-i18n ^9.0.0
@heroicons/vue ^2.0.0
@dcodegroup/vue-mention ^0.0.2
mitt ^3.0.0 (or any bus with $on / $off / $emit)
vue-markdown-render ^2.1.1

Installation

npm install @dcodegroup-au/vue-activity-log
# or
pnpm add @dcodegroup-au/vue-activity-log

Install peer dependencies if they are not already in your app:

npm install vue axios vue-i18n @heroicons/vue @dcodegroup/vue-mention mitt vue-markdown-render

Setup

1. Register the plugin (recommended)

import { createApp } from 'vue'
import App from './App.vue'
import VueActivityLogPlugin from '@dcodegroup-au/vue-activity-log'
import '@dcodegroup-au/vue-activity-log/dist/style.css'

const app = createApp(App)
app.use(VueActivityLogPlugin)
app.mount('#app')

2. Or import components locally

<script setup>
import {
  VActivityLog,
  ActivityLogList,
  ActivityLogModal,
  ActivityEmail,
  ActivityLogDeleteComment,
} from '@dcodegroup-au/vue-activity-log'
import '@dcodegroup-au/vue-activity-log/dist/style.css'
</script>

3. Provide an event bus

Most components inject: ['bus'] and call bus.$on / bus.$off / bus.$emit. Raw mitt uses on / off / emit, so wrap it:

import mitt from 'mitt'
import { createApp } from 'vue'
import App from './App.vue'

const emitter = mitt()
const bus = {
  $on: (...args) => emitter.on(...args),
  $off: (...args) => emitter.off(...args),
  $emit: (...args) => emitter.emit(...args),
}

const app = createApp(App)
app.provide('bus', bus)
app.mount('#app')

Or in a parent component:

<script setup>
import { provide } from 'vue'
import mitt from 'mitt'

const emitter = mitt()
provide('bus', {
  $on: (...args) => emitter.on(...args),
  $off: (...args) => emitter.off(...args),
  $emit: (...args) => emitter.emit(...args),
})
</script>

Mount <ActivityLogModal /> once near the app root so email previews and delete confirmations can open.

4. Configure vue-i18n

Components use $t(...) keys under activity-log.* and generic.*. Align messages with the Laravel package lang file, or add a minimal locale:

const messages = {
  en: {
    'activity-log': {
      headings: {
        title: 'Activity Log',
        confirm_delete_comment: 'Confirm Delete Comment',
      },
      fields: {
        updated_model: 'Updated',
        to: 'To',
        subject: 'Subject',
        date: 'Date',
        collapsed_view: 'Collapsed view',
        my_activities: 'My activities',
        system: 'System',
        loading: 'Loading...',
        no_result: 'No results',
      },
      buttons: {
        download_phone_call: 'Download Phone Call',
        comment: 'Comment',
        save: 'Save',
        preview_email: 'Preview Email',
        preview_sms: 'Preview SMS',
        delete: 'Delete',
        edit: 'Edit',
        cancel: 'Cancel',
        resend: 'Resend',
        resent: 'Resent',
      },
      placeholders: {
        add_comment: 'Add your comment...',
        search_description: 'Search by description',
      },
      search: {
        placeholder: 'Search...',
      },
      words: {
        edited: 'Edited',
        loading: 'Loading ...',
        views: 'views',
        read_more: 'Read more',
        read_less: 'Read less',
        unknown: 'Someone',
      },
      phases: {
        opened_on: 'Opened on',
        email_has_not_been_opened: 'Email is unopened.',
      },
    },
    generic: {
      created_by: 'Created by',
      buttons: {
        cancel: 'Cancel',
        confirm: 'Confirm',
        delete: 'Delete',
      },
    },
  },
}

Components

Component Export Purpose
VActivityLog yes Timeline with search, filters, comments, reactions, resend
ActivityLogList yes Compact table/list view
ActivityLogModal yes Host modal opened via the bus
ActivityEmail yes Email/SMS preview content for the modal
ActivityLogDeleteComment yes Delete-comment confirmation for the modal
VActivityReactions internal Emoji reactions UI used by VActivityLog

VActivityLog — timeline view

<template>
  <VActivityLog
    model-class="Post"
    model-id="123"
    get-url="/activity-logs"
    comment-url="/activity-logs/comments"
    load-users-url="/activity-logs/filters/facets/created_by"
    resend-url="/activity-logs/resend-communication"
    :current-user="{ id: 1, full_name: 'John Doe' }"
    :allow-comment="true"
    :allow-attachments="true"
    :allow-resend="true"
    :allow-action="true"
    :auto-grow-input="true"
    timezone="Asia/Bangkok"
    @comment-added="onCommentAdded"
  />
</template>
Props
Prop Type Default Description
modelClass String — Required. Model class name sent to the API
modelId String — Required. Model id
getUrl String "/activity-logs" Index endpoint
commentUrl String "/activity-logs/comments" Create/update comment endpoint
loadUsersUrl String "/activity-logs/filters/facets/created_by" Mention user facet endpoint
resendUrl String "/activity-logs/resent-communication" Resend communication base URL
currentUser Object — { id, full_name, ... } for comments/reactions
allowComment Boolean false Show comment composer
allowAttachments Boolean false Show file attachment controls in the comment composer and editor
attachmentAccept String images, PDF, Word Value passed to the file input's accept attribute
maxAttachments Number 10 Maximum files allowed per comment
attachFilesText String "Attach files" Attachment button label
allowResend Boolean false Show resend action on communications
allowAction Boolean false Show the edit/delete action menu even when not a comment or when in widget view
hasReaction Boolean true Show emoji reactions on activities
isWidgetView Boolean false Compact/widget layout
defaultCollapView Boolean false Start in collapsed view
refreshSelf Boolean false Refetch after posting a comment
canMentionInComment Boolean true Enable @ mentions
canMentionSpace Boolean true Allow spaces in mentions
enterToComment Boolean false Submit on Enter (otherwise use the button)
autoGrowInput Boolean false Auto-expand comment textarea
isMarkdownContent Boolean false Treat communication content as markdown
showFullComment Boolean false Skip “read more” truncation
noActivityText String "No activity found" Empty state text
timezone String — Passed as query param
extra_models String — Related models filter
filterEvent String "activityLogFilterChange" Bus event for external filters
resendEvent String "activityLogResend" Bus event name for resend
modalEvent String "openActivityLogModal" Bus event to open the modal
activityEmailComponentName String "ActivityEmail" Component name passed into the modal
Events
Event Payload
commentAdded { event, activities, modelId, modelClass }
attachmentError Upload or comment request error
Default slot

Rendered in the search/toolbar area (e.g. an export button).

Bus events used
Event Direction Purpose
refreshActivityLog listen / emit Refresh this (or sibling) timeline instances
activityLogFilterChange listen / emit Apply external filter params
activityLogTermChanged listen Set filter[name] from external search
openActivityLogModal emit Open email preview or delete dialog
Reactions

When hasReaction is true (default), activities show emoji reactions inline with the title. Set :has-reaction="false" to hide them. Clicking an emoji posts:

POST {getUrl}/{activityId}/reactions
Content-Type: application/json

{
  "emoji": "👍",
  "modelClass": "Post",
  "modelId": "123",
  "currentUser": { "id": 1, "full_name": "John Doe" }
}

Expected response shape matches the index endpoint: { "data": [ /* activities */ ] }.

Each activity may include reactions, reaction_groups, or reactionGroups for display.


ActivityLogList — table / list view

<template>
  <ActivityLogList
    model-class="Post"
    model-id="123"
    get-url="/activity-logs"
    current-user="John Doe"
  />
</template>
Props
Prop Type Default Description
modelClass String — Required
modelId String | Number — Required
getUrl String "/activity-logs" Index endpoint
allowComment Boolean false Reserved for comment UI
refreshSelf Boolean false Reserved refresh flag
currentUser String "Guest" Display name (string, unlike timeline)
modalEvent String "openActivityLogModal" Bus event for email preview
filterEvent String "activityLogFilterChange" Bus event for external filters

ActivityLogModal

Renders a centered overlay. Place once in the app shell:

<template>
  <ActivityLogModal />
</template>

Open it via the bus:

bus.$emit('openActivityLogModal', {
  componentName: 'ActivityEmail', // or 'ActivityLogDeleteComment'
  componentData: {
    to: 'user@example.com',
    subject: 'Welcome',
    content: '<p>Hello</p>',
    date: '2024-06-16 10:30 AM',
    isMarkdownContent: false,
  },
  // optional:
  cancelTitle: 'Cancel',
  confirmTitle: 'OK',
  scrollable: true,
  isAsyncCallback: false,
  callback: () => {},
  cancelCallback: () => {},
})

Close with:

bus.$emit('closeActivityLogModal')

ActivityEmail

Modal body for email/SMS preview.

Prop Type Required Default
to String yes —
subject String yes —
content String yes —
date String no —
isMarkdownContent Boolean no false

When isMarkdownContent is true and content looks like markdown, it renders via vue-markdown-render; otherwise HTML is rendered with v-html.


ActivityLogDeleteComment

Prop Type Required
endpoint String yes — full DELETE URL for the comment

On success it emits refreshActivityLog and closeActivityLogModal on the bus.

bus.$emit('openActivityLogModal', {
  componentName: 'ActivityLogDeleteComment',
  componentData: {
    endpoint: `/activity-logs/comment/${commentId}`,
  },
})

Complete example

<template>
  <div class="app">
    <VActivityLog
      model-class="Post"
      model-id="123"
      get-url="/activity-logs"
      comment-url="/activity-logs/comments"
      load-users-url="/activity-logs/filters/facets/created_by"
      resend-url="/activity-logs/resend-communication"
      :current-user="currentUser"
      :allow-comment="true"
      :allow-attachments="true"
      :allow-resend="true"
      :allow-action="true"
      :can-mention-in-comment="true"
      :auto-grow-input="true"
      :timezone="timezone"
      @comment-added="onCommentAdded"
    />

    <ActivityLogModal />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import mitt from 'mitt'
import {
  VActivityLog,
  ActivityLogModal,
} from '@dcodegroup-au/vue-activity-log'
import '@dcodegroup-au/vue-activity-log/dist/style.css'

const emitter = mitt()
provide('bus', {
  $on: (...args) => emitter.on(...args),
  $off: (...args) => emitter.off(...args),
  $emit: (...args) => emitter.emit(...args),
})

const currentUser = ref({ id: 1, full_name: 'John Doe' })
const timezone = ref('Asia/Bangkok')

const onCommentAdded = (data) => {
  console.log('Comment added:', data.modelClass, data.modelId)
}
</script>

API contract

Designed to work with dcodegroup/activity-log routes (defaults below). Adjust URLs via props if your route_path differs.

GET /activity-logs

Query: modelClass, modelId, optional timezone, extra_models, filter[term], filter[created_by], …

Response:

{
  "data": [
    {
      "id": 1,
      "user": "John Doe",
      "title": "Created post",
      "description": "Post description",
      "type": "Create",
      "color": "blue",
      "icon": "PlusIcon",
      "created_at_date": "2024-06-16 10:30 AM",
      "is_edited": false,
      "communication": {
        "type": "Email",
        "to": "user@example.com",
        "subject": "Subject",
        "content": "<p>Content</p>",
        "date": "2024-06-16",
        "reads_count": 2,
        "read_at_date": "2024-06-17"
      },
      "meta": { "action": "Created" },
      "delete_comment_endpoint": "/activity-logs/comment/1",
      "reactions": [],
      "reaction_groups": {}
    }
  ]
}

type values commonly used: Create, Update, Delete, Comment, Phone Call, plus notification/communication types from the backend.

POST /activity-logs/comments

{
  "modelClass": "Post",
  "modelId": "123",
  "comment": "User's comment",
  "currentUrl": "https://...",
  "currentUser": "John Doe",
  "timezone": "Asia/Bangkok",
  "attachment_ids": [10, 11]
}

Response: { "data": [ /* activities */ ] }

Files are never uploaded separately. When the user has picked new files, the same request is sent as multipart/form-data instead of JSON, with the fields above plus attachments[] (the raw files) and attachment_ids[] (IDs already attached to the comment that should be kept). The backend stores the files and links them to the activity log it creates, all in one round trip.

Activity responses can include:

{
  "attachments": [
    {
      "id": 10,
      "url": "/attachments/10",
      "custom_properties": {
        "original_filename": "screenshot.png"
      }
    }
  ]
}

PATCH /activity-logs/comments/{id}

Same body as create; updates an existing comment. attachment_ids lists the already-stored attachments the comment should keep, so the backend should sync against it and detach anything missing.

When new files are attached during an edit the request is sent as POST with _method=PATCH, because PHP does not parse multipart bodies on PATCH requests.

DELETE /activity-logs/comment/{id}

Backend default path uses singular comment. Response: { "data": [ /* activities */ ] } (or empty; the modal also emits a refresh).

POST /activity-logs/resend-communication/{id}

Resends a communication log. Note: the Vue prop default is historically "/activity-logs/resent-communication" — pass resend-url explicitly to match the Laravel route (resend-communication).

POST /activity-logs/{id}/reactions

{
  "emoji": "👍",
  "modelClass": "Post",
  "modelId": "123",
  "currentUser": { "id": 1, "full_name": "John Doe" }
}

Response: { "data": [ /* activities */ ] }

GET /activity-logs/filters/facets/created_by

Query: s, modelClass, modelId (and filter[admin]=1 when loading mention users)

Response: array of { "label": "John Doe", "value": 1 } (axios response.data is mapped directly in the mention loader).

Styling

Components ship Tailwind-oriented utility classes plus package CSS:

import '@dcodegroup-au/vue-activity-log/dist/style.css'

Ensure your app’s Tailwind setup (or the host design system) provides compatible utility classes / theme tokens used by the components (e.g. btn-primary, tertiary-*).

Root markup uses the .vue-activity-log scope class. Timeline status helpers are BEM-style (content__status__time, content__status__description). Activity DB change chips (.activity__db-content) use break-all so long values wrap instead of overflowing.

Development

pnpm install
pnpm build

Build output is written to dist/ (vue-activity-log.es.js, vue-activity-log.umd.js, style.css).

License

MIT

Related Packages

@enso-ui/notifications

Notification center pages and navbar widgets for Enso UI

10,889 0
@growthbunker/vuetimeline

One easy-to-use component to build beautiful responsive timelines.

11,806 350
@enso-ui/comments

Comments cards and inline comment threads for Enso UI.

10,609 0
vue-pipeline

One easy-to-use component to show beautiful responsive timeline like jenkins blu...

7,867 332

Version History

Version Released Vue License
0.1.20 >=3.0.0 MIT
0.1.19 >=3.0.0 MIT
0.1.18 >=3.0.0 MIT
0.1.16 >=3.0.0 MIT
0.1.15 >=3.0.0 MIT