@dcodegroup-au/vue-activity-log
v0.1.6
Published
A Vue 3 component for displaying activity logs with timeline, comments, and notifications.
Readme
Vue Activity Log Components
A collection of Vue 3 components for displaying activity logs, comments, and communications. Includes timeline view, table list view, and modal dialogs for email previews and delete confirmations.
Installation
npm install @dcodegroup-au/vue-activity-logSetup
1. Register components globally (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. Register components locally
<script setup>
import { VActivityLog, ActivityEmail, ActivityLogDeleteComment, ActivityLogList } from '@dcodegroup-au/vue-activity-log'
import '@dcodegroup-au/vue-activity-log/dist/style.css'
</script>3. Setup dependencies
Make sure your project has these peer dependencies installed:
npm install vue@^3.0.0 axios vue-i18n @heroicons/vue @dcodegroup/vue-mention mitt vue-markdown-render4. Setup event bus (optional, but required for modals)
If using modals, provide a bus (mitt event emitter) via provide:
import mitt from 'mitt'
import { createApp } from 'vue'
const bus = mitt()
const app = createApp(...)
app.provide('bus', bus)
app.mount('#app')Or in your root component:
<script setup>
import mitt from 'mitt'
const bus = mitt()
</script>
<template>
<div>
<provide key="bus" :value="bus">
<YourComponent />
</provide>
</div>
</template>Components
1. VActivityLog (Timeline View)
Main activity log component displaying activities in a vertical timeline format with comments support.
Basic Usage
<template>
<VActivityLog
model-class="Post"
model-id="123"
get-url="/api/activity-logs"
comment-url="/api/activity-logs/comments"
load-users-url="/api/activity-logs/filters/facets/created_by"
:current-user="{ id: 1, full_name: 'John Doe' }"
/>
</template>
<script setup>
import { VActivityLog } from '@dcodegroup-au/vue-activity-log'
</script>Props
{
// URLs
getUrl: String, // Default: "/activity-logs"
commentUrl: String, // Default: "/activity-logs/comments"
loadUsersUrl: String, // Default: "/activity-logs/filters/facets/created_by"
resendUrl: String, // Default: "/activity-logs/resent-communication"
// Model identification (required)
modelClass: String, // e.g., "Post", "User" (required)
modelId: String, // e.g., "123" (required)
// User info
currentUser: Object, // { id, full_name, ... }
// Display options
allowComment: Boolean, // Default: false
allowResend: Boolean, // Default: false
isWidgetView: Boolean, // Default: false (compact view)
defaultCollapView: Boolean, // Default: false
// Comment options
canMentionInComment: Boolean, // Default: true
canMentionSpace: Boolean, // Default: true
enterToComment: Boolean, // Default: false (Ctrl+Enter to submit)
autoGrowInput: Boolean, // Default: false (auto-expand textarea)
// Content options
isMarkdownContent: Boolean, // Default: false
showFullComment: Boolean, // Default: false
noActivityText: String, // Default: "No activity found"
// Timezone
timezone: String, // e.g., "UTC", "Asia/Bangkok"
// Additional models
extra_models: String, // For filtering by related models
// Events/Callbacks
filterEvent: String, // Default: "activityLogFilterChange"
resendEvent: String, // Default: "activityLogResend"
modalEvent: String, // Default: "openActivityLogModal"
activityEmailComponentName: String,// Default: "ActivityEmail"
// Behavior
refreshSelf: Boolean, // Default: false (refresh on comment)
}Events
<template>
<VActivityLog
@comment-added="handleCommentAdded"
/>
</template>
<script setup>
const handleCommentAdded = (data) => {
console.log(data)
// {
// event,
// activities,
// modelId,
// modelClass
// }
}
</script>Slots
<template>
<VActivityLog model-class="Post" model-id="123">
<!-- Custom action slot in search bar -->
<button>Export</button>
</VActivityLog>
</template>Full Example with Comments
<template>
<div class="p-4">
<VActivityLog
model-class="Post"
model-id="123"
get-url="/api/activity-logs"
comment-url="/api/activity-logs/comments"
load-users-url="/api/activity-logs/filters/facets/created_by"
:current-user="currentUser"
:allow-comment="true"
:allow-resend="true"
:can-mention-in-comment="true"
:enter-to-comment="false"
:auto-grow-input="true"
:timezone="timezone"
@comment-added="onCommentAdded"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VActivityLog } from '@dcodegroup-au/vue-activity-log'
const currentUser = ref({
id: 1,
full_name: 'John Doe'
})
const timezone = ref('Asia/Bangkok')
const onCommentAdded = (data) => {
console.log('Comment added:', data)
}
</script>2. ActivityLogList (Table View)
Displays activities in a table format with search and filtering.
Basic Usage
<template>
<ActivityLogList
model-class="Post"
model-id="123"
get-url="/api/activity-logs"
modal-event="openActivityLogModal"
/>
</template>
<script setup>
import { ActivityLogList } from '@dcodegroup-au/vue-activity-log'
</script>Props
{
// URLs
getUrl: String, // Default: "/activity-logs"
// Model identification (required)
modelClass: String, // (required)
modelId: String | Number, // (required)
// Display options
allowComment: Boolean, // Default: false
refreshSelf: Boolean, // Default: false
// User
currentUser: String, // Default: "Guest"
// Events
modalEvent: String, // Default: "openActivityLogModal"
filterEvent: String, // Default: "activityLogFilterChange"
}Full Example
<template>
<div class="p-4">
<ActivityLogList
model-class="Post"
model-id="123"
get-url="/api/activity-logs"
current-user="John Doe"
:allow-comment="false"
/>
</div>
</template>
<script setup>
import { ActivityLogList } from '@your-org/vue-activity-log'
</script>3. ActivityEmail (Modal Content)
Component for previewing email communications.
Basic Usage
<template>
<ActivityEmail
to="[email protected]"
subject="Welcome to our platform"
content="<p>Hello,</p><p>Welcome!</p>"
date="2024-06-16 10:30 AM"
:is-markdown-content="false"
/>
</template>
<script setup>
import { ActivityEmail } from '@dcodegroup-au/vue-activity-log'
</script>Props
{
to: String, // (required)
subject: String, // (required)
content: String, // (required) - HTML or Markdown
date: String, // Optional
isMarkdownContent: Boolean, // Default: false
}Used with ActivityLogModal
<template>
<ActivityLogModal />
</template>
<script setup>
import { ref } from 'vue'
import { ActivityLogModal } from '@your-org/vue-activity-log'
import mitt from 'mitt'
const bus = mitt()
// Somewhere in your code, emit to open modal:
const openEmailPreview = (communication) => {
bus.emit('openActivityLogModal', {
componentName: 'ActivityEmail',
componentData: {
to: communication.to,
subject: communication.subject,
content: communication.content,
date: communication.date,
isMarkdownContent: true
}
})
}
</script>4. ActivityLogDeleteComment (Modal Content)
Component for confirming comment deletion.
Props
{
endpoint: String, // (required) - DELETE endpoint for the comment
}Events
Emits on bus:
refreshActivityLog- to refresh activity listcloseActivityLogModal- to close modal
Used with ActivityLogModal
<template>
<ActivityLogModal />
</template>
<script setup>
import { ref } from 'vue'
import { ActivityLogModal } from '@your-org/vue-activity-log'
import mitt from 'mitt'
const bus = mitt()
// Somewhere in your code, emit to open delete modal:
const openDeleteModal = (comment) => {
bus.emit('openActivityLogModal', {
componentName: 'ActivityLogDeleteComment',
componentData: {
endpoint: `/api/activity-logs/comments/${comment.id}`
}
})
}
</script>Complete App Example
<template>
<div class="app">
<header>
<h1>Activity Log Demo</h1>
</header>
<main>
<div class="tabs">
<button
@click="activeTab = 'timeline'"
:class="{ active: activeTab === 'timeline' }"
>
Timeline View
</button>
<button
@click="activeTab = 'list'"
:class="{ active: activeTab === 'list' }"
>
List View
</button>
</div>
<!-- Timeline View -->
<div v-if="activeTab === 'timeline'" class="tab-content">
<VActivityLog
model-class="Post"
model-id="123"
get-url="/api/activity-logs"
comment-url="/api/activity-logs/comments"
load-users-url="/api/activity-logs/filters/facets/created_by"
resend-url="/api/activity-logs/resent-communication"
:current-user="currentUser"
:allow-comment="true"
:allow-resend="true"
:can-mention-in-comment="true"
:auto-grow-input="true"
:timezone="timezone"
@comment-added="onCommentAdded"
/>
</div>
<!-- List View -->
<div v-if="activeTab === 'list'" class="tab-content">
<ActivityLogList
model-class="Post"
model-id="123"
get-url="/api/activity-logs"
:current-user="currentUser.full_name"
/>
</div>
</main>
<!-- Modal for previews -->
<ActivityLogModal />
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import mitt from 'mitt'
import {
VActivityLog,
ActivityLogList,
ActivityLogModal
} from '@dcodegroup-au/vue-activity-log'
import '@dcodegroup-au/vue-activity-log/dist/style.css'
// Setup event bus
const bus = mitt()
provide('bus', bus)
// State
const activeTab = ref('timeline')
const currentUser = ref({
id: 1,
full_name: 'John Doe'
})
const timezone = ref('Asia/Bangkok')
// Handlers
const onCommentAdded = (data) => {
console.log('Comment added on:', data.modelClass, data.modelId)
}
</script>
<style scoped>
.app {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.tabs button {
padding: 10px 20px;
border: 1px solid #ccc;
background: #f5f5f5;
cursor: pointer;
border-radius: 4px;
}
.tabs button.active {
background: #007bff;
color: white;
border-color: #007bff;
}
.tab-content {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
}
</style>API Requirements
Your backend should provide these endpoints:
GET /activity-logs
// Query params:
// modelClass: string
// modelId: string
// timezone: string (optional)
// filter[term]: string (search)
// filter[created_by]: string (user filter)
// Response:
{
"data": [
{
"id": 1,
"user": "John Doe",
"title": "Created post",
"description": "Post description",
"type": "Create", // or "Update", "Delete", "Comment", "Phone Call"
"color": "blue",
"icon": "PlusIcon",
"created_at_date": "2024-06-16 10:30 AM",
"is_edited": false,
"communication": {
"type": "Email", // or "Sms"
"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": "/api/activity-logs/comments/1"
}
]
}POST /activity-logs/comments
// Request body:
{
"modelClass": "Post",
"modelId": "123",
"comment": "User's comment",
"currentUrl": "http://...",
"currentUser": "John Doe",
"timezone": "Asia/Bangkok"
}
// Response: { "data": [...activities] }PATCH /activity-logs/comments/:id
// Same as POST but updates existing commentDELETE /activity-logs/comments/:id
// Response: { "data": [...activities] }POST /activity-logs/resent-communication/:id
// Response: successGET /activity-logs/filters/facets/created_by
// Query params:
// s: string (search text)
// modelClass: string
// modelId: string
// Response:
{
"data": [
{ "label": "John Doe", "value": 1 },
{ "label": "Jane Smith", "value": 2 }
]
}Styling
Components use Tailwind CSS classes. Make sure Tailwind is configured in your project. The package exports minimal CSS (mostly for animations and component-specific styles).
License
MIT
