npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dcodegroup-au/vue-activity-log

v0.1.19

Published

A Vue 3 component for displaying activity logs with timeline, comments, and notifications.

Readme

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"
    :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 | | 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: '[email protected]',
    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"
      :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": "[email protected]",
        "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