@escalated-dev/escalated 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -2
- package/package.json +31 -2
- package/src/components/ActivityTimeline.stories.js +55 -0
- package/src/components/ActivityTimeline.vue +24 -7
- package/src/components/AdminDashboard.stories.js +1095 -0
- package/src/components/AgentLoadIndicator.stories.js +37 -0
- package/src/components/AgentLoadIndicator.vue +49 -0
- package/src/components/AgentPerformanceCard.vue +44 -0
- package/src/components/ArticleEditor.vue +82 -0
- package/src/components/AssigneeSelect.vue +19 -5
- package/src/components/AttachmentList.vue +53 -37
- package/src/components/AuditLogEntry.vue +95 -0
- package/src/components/BulkActionBar.vue +99 -30
- package/src/components/CategoryTree.vue +89 -0
- package/src/components/ChartWidget.vue +46 -0
- package/src/components/CollisionWarning.stories.js +30 -0
- package/src/components/CollisionWarning.vue +68 -0
- package/src/components/ConditionalFieldRules.vue +135 -0
- package/src/components/ContextPanel.vue +66 -0
- package/src/components/ContextPanelSection.vue +65 -0
- package/src/components/CustomFieldRenderer.vue +159 -0
- package/src/components/EscalatedLayout.vue +400 -69
- package/src/components/FileDropzone.vue +26 -10
- package/src/components/FollowButton.stories.js +30 -0
- package/src/components/FollowButton.vue +53 -28
- package/src/components/KeyboardShortcutHelp.vue +129 -39
- package/src/components/KnowledgePanel.vue +122 -0
- package/src/components/KpiCard.stories.js +32 -0
- package/src/components/KpiCard.vue +88 -0
- package/src/components/MacroDropdown.vue +95 -35
- package/src/components/PermissionMatrix.vue +107 -0
- package/src/components/PinnedNotes.vue +70 -31
- package/src/components/PresenceIndicator.vue +93 -27
- package/src/components/PriorityBadge.stories.js +33 -0
- package/src/components/PriorityBadge.vue +9 -2
- package/src/components/QuickFilters.vue +65 -49
- package/src/components/ReplyComposer.vue +144 -30
- package/src/components/ReplyThread.vue +72 -20
- package/src/components/SatisfactionRating.vue +98 -46
- package/src/components/ScheduleEditor.vue +104 -0
- package/src/components/SideConversation.vue +165 -0
- package/src/components/SideConversationList.vue +147 -0
- package/src/components/SkillTagManager.vue +79 -0
- package/src/components/SlaComplianceChart.vue +53 -0
- package/src/components/SlaTimer.stories.js +35 -0
- package/src/components/SlaTimer.vue +17 -4
- package/src/components/StatsCard.stories.js +24 -0
- package/src/components/StatsCard.vue +58 -46
- package/src/components/StatusBadge.stories.js +53 -0
- package/src/components/StatusBadge.vue +36 -4
- package/src/components/TagSelect.vue +43 -12
- package/src/components/TicketFilters.vue +225 -69
- package/src/components/TicketLinkPanel.vue +193 -0
- package/src/components/TicketList.vue +594 -193
- package/src/components/TicketMergeDialog.vue +157 -0
- package/src/components/TicketSidebar.vue +167 -99
- package/src/components/TicketTypeSelector.vue +21 -0
- package/src/components/TotpInput.stories.js +9 -0
- package/src/components/TotpInput.vue +81 -0
- package/src/components/TwoFactorSetup.vue +113 -0
- package/src/composables/useDebouncedSearch.js +38 -0
- package/src/composables/usePluginExtensions.js +95 -3
- package/src/composables/usePluginHooks.js +65 -0
- package/src/defineEscalatedPlugin.js +34 -8
- package/src/index.js +72 -35
- package/src/locales/en.json +25 -0
- package/src/pages/Admin/ApiTokens/Index.vue +231 -67
- package/src/pages/Admin/AuditLog/Index.vue +154 -0
- package/src/pages/Admin/Automations/Form.vue +184 -0
- package/src/pages/Admin/Automations/Index.vue +129 -0
- package/src/pages/Admin/BusinessHours/Form.vue +172 -0
- package/src/pages/Admin/BusinessHours/Index.vue +113 -0
- package/src/pages/Admin/CannedResponses/Index.vue +127 -70
- package/src/pages/Admin/Capacity/Index.vue +134 -0
- package/src/pages/Admin/CustomFields/Form.vue +195 -0
- package/src/pages/Admin/CustomFields/Index.vue +134 -0
- package/src/pages/Admin/CustomObjects/Form.vue +202 -0
- package/src/pages/Admin/CustomObjects/Index.vue +127 -0
- package/src/pages/Admin/CustomObjects/Records.vue +228 -0
- package/src/pages/Admin/Departments/Form.vue +75 -51
- package/src/pages/Admin/Departments/Index.vue +109 -57
- package/src/pages/Admin/EscalationRules/Form.vue +157 -96
- package/src/pages/Admin/EscalationRules/Index.vue +178 -57
- package/src/pages/Admin/Import/Connect.vue +96 -0
- package/src/pages/Admin/Import/Index.vue +102 -0
- package/src/pages/Admin/Import/Mapping.vue +110 -0
- package/src/pages/Admin/Import/Progress.vue +153 -0
- package/src/pages/Admin/Import/Review.vue +76 -0
- package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -0
- package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -0
- package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -0
- package/src/pages/Admin/Macros/Index.vue +250 -89
- package/src/pages/Admin/Plugins/Index.vue +205 -75
- package/src/pages/Admin/Reports/AgentMetrics.vue +115 -0
- package/src/pages/Admin/Reports/CsatReport.vue +127 -0
- package/src/pages/Admin/Reports/Dashboard.vue +211 -0
- package/src/pages/Admin/Reports/SlaReport.vue +166 -0
- package/src/pages/Admin/Reports.vue +139 -34
- package/src/pages/Admin/Roles/Form.vue +71 -0
- package/src/pages/Admin/Roles/Index.vue +122 -0
- package/src/pages/Admin/Settings/CsatSettings.vue +165 -0
- package/src/pages/Admin/Settings/DataRetention.vue +182 -0
- package/src/pages/Admin/Settings/EmailSettings.vue +243 -0
- package/src/pages/Admin/Settings/Sandbox.vue +135 -0
- package/src/pages/Admin/Settings/SsoSettings.vue +218 -0
- package/src/pages/Admin/Settings/TwoFactor.vue +110 -0
- package/src/pages/Admin/Settings.vue +468 -260
- package/src/pages/Admin/Skills/Form.vue +53 -0
- package/src/pages/Admin/Skills/Index.vue +94 -0
- package/src/pages/Admin/SlaPolicies/Form.vue +118 -75
- package/src/pages/Admin/SlaPolicies/Index.vue +120 -59
- package/src/pages/Admin/Statuses/Form.vue +114 -0
- package/src/pages/Admin/Statuses/Index.vue +153 -0
- package/src/pages/Admin/Tags/Index.vue +191 -105
- package/src/pages/Admin/Tickets/Index.vue +91 -39
- package/src/pages/Admin/Tickets/Show.vue +101 -37
- package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -0
- package/src/pages/Admin/Webhooks/Form.vue +110 -0
- package/src/pages/Admin/Webhooks/Index.vue +136 -0
- package/src/pages/Agent/Dashboard.vue +311 -186
- package/src/pages/Agent/TicketIndex.vue +88 -38
- package/src/pages/Agent/TicketShow.vue +258 -51
- package/src/pages/Customer/Create.vue +36 -9
- package/src/pages/Customer/Index.vue +31 -24
- package/src/pages/Customer/KnowledgeBase/Article.vue +127 -0
- package/src/pages/Customer/KnowledgeBase/Index.vue +151 -0
- package/src/plugin.js +86 -1
- package/src/utils/constants.js +30 -0
- package/src/utils/formatting.js +35 -0
- package/src/pages/Admin/Plugins.vue +0 -129
package/README.md
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
Escalated
|
|
4
4
|
</h1>
|
|
5
5
|
|
|
6
|
+
[](https://github.com/escalated-dev/escalated/actions/workflows/run-tests.yml)
|
|
7
|
+
[](https://www.npmjs.com/package/@escalated-dev/escalated)
|
|
8
|
+
[](https://vuejs.org/)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
6
11
|
Escalated is an embeddable support ticket system with SLA tracking, escalation rules, agent workflows, and a customer portal. This repo contains all the shared frontend assets (Vue 3 + Inertia.js) used across every supported backend framework.
|
|
7
12
|
|
|
8
13
|
👉 **Learn more, view demos, and compare Cloud vs Self-Hosted options at** **[https://escalated.dev](https://escalated.dev)**
|
|
@@ -19,6 +24,10 @@ Pick your framework:
|
|
|
19
24
|
| **Rails** | [escalated-dev/escalated-rails](https://github.com/escalated-dev/escalated-rails) | `gem "escalated"` |
|
|
20
25
|
| **Django** | [escalated-dev/escalated-django](https://github.com/escalated-dev/escalated-django) | `pip install escalated-django` |
|
|
21
26
|
| **AdonisJS** | [escalated-dev/escalated-adonis](https://github.com/escalated-dev/escalated-adonis) | `npm install @escalated-dev/escalated-adonis` |
|
|
27
|
+
| **WordPress** | [escalated-dev/escalated-wordpress](https://github.com/escalated-dev/escalated-wordpress) | [Download escalated.zip](https://github.com/escalated-dev/escalated-wordpress/releases/latest) |
|
|
28
|
+
| **Filament** | [escalated-dev/escalated-filament](https://github.com/escalated-dev/escalated-filament) | `composer require escalated-dev/escalated-filament` |
|
|
29
|
+
| **React Native** | [escalated-dev/escalated-react-native](https://github.com/escalated-dev/escalated-react-native) | `npm install @escalated-dev/escalated-react-native` |
|
|
30
|
+
| **Flutter** | [escalated-dev/escalated-flutter](https://github.com/escalated-dev/escalated-flutter) | See [pubspec.yaml setup](https://github.com/escalated-dev/escalated-flutter#quick-start) |
|
|
22
31
|
|
|
23
32
|
Each backend repo has full setup instructions — install command, migrations, config, and frontend integration.
|
|
24
33
|
|
|
@@ -127,12 +136,31 @@ All the Vue 3 + Inertia.js components that power the Escalated UI. These are ide
|
|
|
127
136
|
|
|
128
137
|
## 📸 Screenshots
|
|
129
138
|
|
|
139
|
+
> Screenshots are auto-generated from Storybook via the [component-screenshots](.github/workflows/screenshots.yml) workflow.
|
|
140
|
+
|
|
141
|
+
<p align="center">
|
|
142
|
+
<strong>Admin Panel (Dark)</strong><br/>
|
|
143
|
+
<img src="docs/assets/escalated_admin_1.png" alt="Escalated Admin Panel — dark mode with sidebar navigation, KPI cards, stats, and ticket list" width="800" />
|
|
144
|
+
</p>
|
|
145
|
+
|
|
130
146
|
<p align="center">
|
|
131
|
-
<
|
|
147
|
+
<strong>Admin Panel (Light)</strong><br/>
|
|
148
|
+
<img src="docs/assets/escalated_admin_3.png" alt="Escalated Admin Panel — light mode with sidebar navigation, KPI cards, stats, and ticket list" width="800" />
|
|
132
149
|
</p>
|
|
133
150
|
|
|
134
151
|
<p align="center">
|
|
135
|
-
<
|
|
152
|
+
<strong>Ticket Queue</strong><br/>
|
|
153
|
+
<img src="docs/assets/escalated_ticket_list.png" alt="Escalated Ticket Queue — agent ticket list with filters, search, bulk actions, and SLA indicators" width="800" />
|
|
154
|
+
</p>
|
|
155
|
+
|
|
156
|
+
<p align="center">
|
|
157
|
+
<strong>Agent Panel</strong><br/>
|
|
158
|
+
<img src="docs/assets/escalated_admin_2.png" alt="Escalated Agent Panel — top navigation, stats, and assigned ticket queue" width="800" />
|
|
159
|
+
</p>
|
|
160
|
+
|
|
161
|
+
<p align="center">
|
|
162
|
+
<strong>Ticket Detail View</strong><br/>
|
|
163
|
+
<img src="docs/assets/escalated_ticket_view.png" alt="Escalated Ticket Detail View — conversation thread, reply composer, and ticket sidebar with SLA timer" width="800" />
|
|
136
164
|
</p>
|
|
137
165
|
|
|
138
166
|
### Pages
|
|
@@ -197,6 +225,70 @@ Reusable building blocks used across the pages above.
|
|
|
197
225
|
|--------|-------------|
|
|
198
226
|
| `EscalatedPlugin` | Vue plugin for layout injection and CSS theming |
|
|
199
227
|
|
|
228
|
+
## Plugin Development
|
|
229
|
+
|
|
230
|
+
Escalated supports framework-agnostic plugins built with the [Plugin SDK](https://github.com/escalated-dev/escalated-plugin-sdk). Plugins are written once in TypeScript and work across all Escalated backends.
|
|
231
|
+
|
|
232
|
+
### How the Frontend Plugin System Works
|
|
233
|
+
|
|
234
|
+
The frontend uses `defineEscalatedPlugin()` to register Vue components — custom admin pages, ticket sidebar widgets, or dashboard panels — that are mounted automatically when the plugin is active.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { defineEscalatedPlugin } from '@escalated-dev/escalated'
|
|
238
|
+
import MySettingsPage from './MySettingsPage.vue'
|
|
239
|
+
|
|
240
|
+
export default defineEscalatedPlugin({
|
|
241
|
+
name: 'my-plugin',
|
|
242
|
+
pages: {
|
|
243
|
+
'admin/my-plugin/settings': MySettingsPage,
|
|
244
|
+
},
|
|
245
|
+
})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### How It Connects to the Backend
|
|
249
|
+
|
|
250
|
+
The backend uses `definePlugin()` from the [Plugin SDK](https://github.com/escalated-dev/escalated-plugin-sdk) to handle TypeScript business logic — subscribing to ticket lifecycle hooks, exposing API endpoints, and persisting data. The frontend and backend entries work together as a single npm package.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// backend entry (index.ts)
|
|
254
|
+
import { definePlugin } from '@escalated-dev/plugin-sdk'
|
|
255
|
+
|
|
256
|
+
export default definePlugin({
|
|
257
|
+
name: 'my-plugin',
|
|
258
|
+
version: '1.0.0',
|
|
259
|
+
actions: {
|
|
260
|
+
'ticket.created': async (event, ctx) => {
|
|
261
|
+
ctx.log.info('New ticket!', event)
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Quick Example: Both Entry Points
|
|
268
|
+
|
|
269
|
+
A published plugin package typically exports both:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
my-plugin/
|
|
273
|
+
index.ts ← backend: definePlugin() for TypeScript logic
|
|
274
|
+
frontend.ts ← frontend: defineEscalatedPlugin() for Vue components
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The backend framework (Laravel, Rails, Django, AdonisJS) loads `index.ts` via the [Plugin Runtime](https://github.com/escalated-dev/escalated-plugin-runtime). The Vue app imports `frontend.ts` and registers it with `app.use()`.
|
|
278
|
+
|
|
279
|
+
### Installing Plugins
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
npm install @escalated-dev/plugin-slack
|
|
283
|
+
npm install @escalated-dev/plugin-jira
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Resources
|
|
287
|
+
|
|
288
|
+
- [Plugin SDK](https://github.com/escalated-dev/escalated-plugin-sdk) — TypeScript SDK for building plugins
|
|
289
|
+
- [Plugin Runtime](https://github.com/escalated-dev/escalated-plugin-runtime) — Runtime host for plugins
|
|
290
|
+
- [Plugin Development Guide](https://github.com/escalated-dev/escalated-docs) — Full documentation
|
|
291
|
+
|
|
200
292
|
## For Package Maintainers
|
|
201
293
|
|
|
202
294
|
If you're building a new backend integration, this package is available on npm:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escalated-dev/escalated",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Vue 3 + Inertia.js UI components for Escalated — the embeddable support ticket system",
|
|
5
5
|
"author": "Escalated Dev <hello@escalated.dev>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,13 +33,42 @@
|
|
|
33
33
|
"vue": "^3.3.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
|
-
"test": "vitest run"
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"lint": "eslint src/",
|
|
38
|
+
"lint:fix": "eslint src/ --fix",
|
|
39
|
+
"format": "prettier --write src/",
|
|
40
|
+
"format:check": "prettier --check src/",
|
|
41
|
+
"prepare": "husky",
|
|
42
|
+
"storybook": "storybook dev -p 6006",
|
|
43
|
+
"build-storybook": "storybook build",
|
|
44
|
+
"screenshot": "npx playwright test --config=playwright-screenshots.config.js"
|
|
45
|
+
},
|
|
46
|
+
"lint-staged": {
|
|
47
|
+
"*.{js,vue}": [
|
|
48
|
+
"eslint --fix",
|
|
49
|
+
"prettier --write"
|
|
50
|
+
]
|
|
37
51
|
},
|
|
38
52
|
"devDependencies": {
|
|
53
|
+
"@eslint/js": "^10.0.1",
|
|
39
54
|
"@inertiajs/vue3": "^2.0.0",
|
|
55
|
+
"@playwright/test": "^1.50.0",
|
|
56
|
+
"@storybook/addon-a11y": "^10.2.12",
|
|
57
|
+
"@storybook/addon-docs": "^10.2.12",
|
|
58
|
+
"@storybook/vue3-vite": "^10.2.12",
|
|
40
59
|
"@vitejs/plugin-vue": "^5.0.0",
|
|
41
60
|
"@vue/test-utils": "^2.4.0",
|
|
61
|
+
"autoprefixer": "^10.4.20",
|
|
62
|
+
"eslint": "^10.0.1",
|
|
63
|
+
"eslint-plugin-storybook": "^10.2.12",
|
|
64
|
+
"eslint-plugin-vue": "^10.8.0",
|
|
42
65
|
"happy-dom": "^15.0.0",
|
|
66
|
+
"husky": "^9.1.7",
|
|
67
|
+
"lint-staged": "^16.2.7",
|
|
68
|
+
"postcss": "^8.5.0",
|
|
69
|
+
"prettier": "^3.8.1",
|
|
70
|
+
"storybook": "^10.2.12",
|
|
71
|
+
"tailwindcss": "^3.4.0",
|
|
43
72
|
"vitest": "^2.0.0",
|
|
44
73
|
"vue": "^3.5.0"
|
|
45
74
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import ActivityTimeline from './ActivityTimeline.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/ActivityTimeline',
|
|
5
|
+
component: ActivityTimeline,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const sampleActivities = [
|
|
9
|
+
{
|
|
10
|
+
id: 1,
|
|
11
|
+
type: 'status_changed',
|
|
12
|
+
causer: { name: 'Alice Chen' },
|
|
13
|
+
properties: { from: 'open', to: 'in_progress' },
|
|
14
|
+
created_at: new Date(Date.now() - 300000).toISOString(),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 2,
|
|
18
|
+
type: 'assigned',
|
|
19
|
+
causer: { name: 'Bob Smith' },
|
|
20
|
+
properties: { agent_name: 'Alice Chen' },
|
|
21
|
+
created_at: new Date(Date.now() - 600000).toISOString(),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 3,
|
|
25
|
+
type: 'priority_changed',
|
|
26
|
+
causer: { name: 'Alice Chen' },
|
|
27
|
+
properties: { from: 'medium', to: 'high' },
|
|
28
|
+
created_at: new Date(Date.now() - 1200000).toISOString(),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 4,
|
|
32
|
+
type: 'tag_added',
|
|
33
|
+
causer: { name: 'Alice Chen' },
|
|
34
|
+
properties: { tag: 'billing' },
|
|
35
|
+
created_at: new Date(Date.now() - 1800000).toISOString(),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 5,
|
|
39
|
+
type: 'replied',
|
|
40
|
+
causer: { name: 'Alice Chen' },
|
|
41
|
+
properties: {},
|
|
42
|
+
created_at: new Date(Date.now() - 2400000).toISOString(),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 6,
|
|
46
|
+
type: 'sla_breached',
|
|
47
|
+
causer: null,
|
|
48
|
+
properties: {},
|
|
49
|
+
created_at: new Date(Date.now() - 3600000).toISOString(),
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export const Default = { args: { activities: sampleActivities } };
|
|
54
|
+
export const Empty = { args: { activities: [] } };
|
|
55
|
+
export const SingleItem = { args: { activities: [sampleActivities[0]] } };
|
|
@@ -5,7 +5,10 @@ defineProps({
|
|
|
5
5
|
activities: { type: Array, required: true },
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
const dark = inject(
|
|
8
|
+
const dark = inject(
|
|
9
|
+
'esc-dark',
|
|
10
|
+
computed(() => false),
|
|
11
|
+
);
|
|
9
12
|
|
|
10
13
|
const typeLabels = {
|
|
11
14
|
status_changed: 'changed status',
|
|
@@ -53,14 +56,28 @@ function describeActivity(activity) {
|
|
|
53
56
|
</script>
|
|
54
57
|
|
|
55
58
|
<template>
|
|
56
|
-
<div class="space-y-3">
|
|
57
|
-
<div v-for="activity in activities" :key="activity.id" class="flex gap-3 text-sm">
|
|
58
|
-
<div
|
|
59
|
+
<div class="space-y-3" role="feed">
|
|
60
|
+
<div v-for="activity in activities" :key="activity.id" role="article" class="flex gap-3 text-sm">
|
|
61
|
+
<div
|
|
62
|
+
:class="[
|
|
63
|
+
'mt-1 h-2 w-2 flex-shrink-0 rounded-full',
|
|
64
|
+
dark ? 'bg-[var(--esc-panel-text-muted)]' : 'bg-gray-400',
|
|
65
|
+
]"
|
|
66
|
+
></div>
|
|
59
67
|
<div class="flex-1">
|
|
60
|
-
<p :class="dark ? 'text-
|
|
61
|
-
|
|
68
|
+
<p :class="dark ? 'text-[var(--esc-panel-text-secondary)]' : 'text-gray-700'">
|
|
69
|
+
{{ describeActivity(activity) }}
|
|
70
|
+
</p>
|
|
71
|
+
<p :class="['text-xs', dark ? 'text-[var(--esc-panel-text-muted)]' : 'text-gray-400']">
|
|
72
|
+
{{ formatDate(activity.created_at) }}
|
|
73
|
+
</p>
|
|
62
74
|
</div>
|
|
63
75
|
</div>
|
|
64
|
-
<div
|
|
76
|
+
<div
|
|
77
|
+
v-if="!activities?.length"
|
|
78
|
+
:class="['py-4 text-center text-sm', dark ? 'text-[var(--esc-panel-text-muted)]' : 'text-gray-500']"
|
|
79
|
+
>
|
|
80
|
+
No activity yet.
|
|
81
|
+
</div>
|
|
65
82
|
</div>
|
|
66
83
|
</template>
|