@escalated-dev/escalated 0.11.8 → 0.11.10
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 +7 -0
- package/package.json +1 -1
- package/pages.json +1 -1
- package/src/components/AuditLogEntry.vue +15 -9
- package/src/components/ChatQueue.vue +1 -1
- package/src/components/EscalatedLayout.vue +224 -38
- package/src/components/PeriodSelector.vue +1 -1
- package/src/components/PermissionMatrix.vue +1 -1
- package/src/components/QuickFilters.vue +11 -5
- package/src/components/TicketList.vue +10 -7
- package/src/components/admin/newsletters/DeliveriesTable.vue +29 -27
- package/src/components/admin/newsletters/ListMemberTable.vue +30 -28
- package/src/pages/Admin/ApiTokens/Index.vue +1 -1
- package/src/pages/Admin/Automations/Form.vue +10 -2
- package/src/pages/Admin/Automations/Index.vue +1 -1
- package/src/pages/Admin/BusinessHours/Index.vue +1 -1
- package/src/pages/Admin/Capacity/Index.vue +1 -1
- package/src/pages/Admin/CustomFields/Index.vue +1 -1
- package/src/pages/Admin/CustomObjects/Index.vue +1 -1
- package/src/pages/Admin/CustomObjects/Records.vue +1 -1
- package/src/pages/Admin/Departments/Index.vue +1 -1
- package/src/pages/Admin/EscalationRules/Index.vue +1 -1
- package/src/pages/Admin/Import/Index.vue +1 -1
- package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +3 -3
- package/src/pages/Admin/Macros/Index.vue +1 -1
- package/src/pages/Admin/Newsletters/Index.vue +31 -29
- package/src/pages/Admin/Newsletters/Lists/Index.vue +22 -20
- package/src/pages/Admin/Reports/AgentMetrics.vue +1 -1
- package/src/pages/Admin/Reports/AgentRanking.vue +3 -3
- package/src/pages/Admin/Reports/Cohorts.vue +7 -5
- package/src/pages/Admin/Reports/Comparison.vue +3 -3
- package/src/pages/Admin/Reports/Dashboard.vue +7 -5
- package/src/pages/Admin/Reports/ResolutionTimes.vue +54 -46
- package/src/pages/Admin/Reports/ResponseTimes.vue +54 -46
- package/src/pages/Admin/Reports/SlaReport.vue +70 -68
- package/src/pages/Admin/Reports/SlaTrends.vue +2 -2
- package/src/pages/Admin/Reports.vue +1 -1
- package/src/pages/Admin/Roles/Index.vue +1 -1
- package/src/pages/Admin/Settings/DatabaseConnection.vue +3 -3
- package/src/pages/Admin/Settings/EmailSettings.vue +1 -1
- package/src/pages/Admin/Skills/Index.vue +1 -1
- package/src/pages/Admin/SlaPolicies/Index.vue +1 -1
- package/src/pages/Admin/Statuses/Index.vue +1 -1
- package/src/pages/Admin/Tags/Index.vue +1 -1
- package/src/pages/Admin/Users/Index.vue +1 -1
- package/src/pages/Admin/Webhooks/DeliveryLog.vue +1 -1
- package/src/pages/Admin/Webhooks/Index.vue +1 -1
- package/src/pages/Admin/Workflows/Index.vue +1 -1
- package/src/pages/Admin/Workflows/Logs.vue +1 -1
- package/src/pages/Agent/Dashboard.vue +1 -1
- package/src/plugin.js +19 -0
- package/src/utils/scrollAffordance.js +87 -0
|
@@ -58,9 +58,9 @@ function toggleSort(col) {
|
|
|
58
58
|
|
|
59
59
|
<template>
|
|
60
60
|
<EscalatedLayout title="Resolution Time Analysis">
|
|
61
|
-
<div class="mb-6 flex items-center justify-between">
|
|
61
|
+
<div class="mb-6 flex flex-wrap items-center justify-between gap-3">
|
|
62
62
|
<PeriodSelector :model-value="periodDays" @update:model-value="changePeriod" />
|
|
63
|
-
<div class="flex items-center gap-3">
|
|
63
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
64
64
|
<ReportExportButton report-name="resolution-times" :period-days="periodDays" />
|
|
65
65
|
<Link
|
|
66
66
|
:href="route('escalated.admin.reports')"
|
|
@@ -114,51 +114,59 @@ function toggleSort(col) {
|
|
|
114
114
|
<div class="border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)] px-4 py-3">
|
|
115
115
|
<h3 class="text-sm font-semibold text-[var(--esc-panel-text-secondary)]">Resolution Time by Agent</h3>
|
|
116
116
|
</div>
|
|
117
|
-
<
|
|
118
|
-
<
|
|
119
|
-
<
|
|
120
|
-
<
|
|
121
|
-
|
|
117
|
+
<div class="esc-table-scroll">
|
|
118
|
+
<table class="w-full">
|
|
119
|
+
<thead>
|
|
120
|
+
<tr class="border-b border-[var(--esc-panel-border)]">
|
|
121
|
+
<th
|
|
122
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
123
|
+
>
|
|
124
|
+
Agent
|
|
125
|
+
</th>
|
|
126
|
+
<th
|
|
127
|
+
v-for="col in [
|
|
128
|
+
{ key: 'avg', label: 'Avg' },
|
|
129
|
+
{ key: 'median', label: 'Median' },
|
|
130
|
+
{ key: 'p90', label: 'P90' },
|
|
131
|
+
]"
|
|
132
|
+
:key="col.key"
|
|
133
|
+
class="cursor-pointer px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)] hover:text-[var(--esc-panel-text-secondary)]"
|
|
134
|
+
@click="toggleSort(col.key)"
|
|
135
|
+
>
|
|
136
|
+
{{ col.label }}
|
|
137
|
+
<span v-if="sortColumn === col.key">{{
|
|
138
|
+
sortDir === 'asc' ? ' ▲' : ' ▼'
|
|
139
|
+
}}</span>
|
|
140
|
+
</th>
|
|
141
|
+
</tr>
|
|
142
|
+
</thead>
|
|
143
|
+
<tbody class="divide-y divide-[var(--esc-panel-border)]">
|
|
144
|
+
<tr
|
|
145
|
+
v-for="agent in sortedAgents"
|
|
146
|
+
:key="agent.agent_id"
|
|
147
|
+
class="hover:bg-[var(--esc-panel-hover)]"
|
|
122
148
|
>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class="
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
</td>
|
|
145
|
-
<td class="px-4 py-3 text-sm font-medium" :class="resColor(agent.avg)">
|
|
146
|
-
{{ agent.avg ?? '—' }}h
|
|
147
|
-
</td>
|
|
148
|
-
<td class="px-4 py-3 text-sm font-medium" :class="resColor(agent.median)">
|
|
149
|
-
{{ agent.median ?? '—' }}h
|
|
150
|
-
</td>
|
|
151
|
-
<td class="px-4 py-3 text-sm font-medium" :class="resColor(agent.p90)">
|
|
152
|
-
{{ agent.p90 ?? '—' }}h
|
|
153
|
-
</td>
|
|
154
|
-
</tr>
|
|
155
|
-
<tr v-if="!sortedAgents.length">
|
|
156
|
-
<td colspan="4" class="px-4 py-8 text-center text-sm text-[var(--esc-panel-text-muted)]">
|
|
157
|
-
No agent data for this period.
|
|
158
|
-
</td>
|
|
159
|
-
</tr>
|
|
160
|
-
</tbody>
|
|
161
|
-
</table>
|
|
149
|
+
<td class="px-4 py-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
|
|
150
|
+
{{ agent.agent_name }}
|
|
151
|
+
</td>
|
|
152
|
+
<td class="px-4 py-3 text-sm font-medium" :class="resColor(agent.avg)">
|
|
153
|
+
{{ agent.avg ?? '—' }}h
|
|
154
|
+
</td>
|
|
155
|
+
<td class="px-4 py-3 text-sm font-medium" :class="resColor(agent.median)">
|
|
156
|
+
{{ agent.median ?? '—' }}h
|
|
157
|
+
</td>
|
|
158
|
+
<td class="px-4 py-3 text-sm font-medium" :class="resColor(agent.p90)">
|
|
159
|
+
{{ agent.p90 ?? '—' }}h
|
|
160
|
+
</td>
|
|
161
|
+
</tr>
|
|
162
|
+
<tr v-if="!sortedAgents.length">
|
|
163
|
+
<td colspan="4" class="px-4 py-8 text-center text-sm text-[var(--esc-panel-text-muted)]">
|
|
164
|
+
No agent data for this period.
|
|
165
|
+
</td>
|
|
166
|
+
</tr>
|
|
167
|
+
</tbody>
|
|
168
|
+
</table>
|
|
169
|
+
</div>
|
|
162
170
|
</div>
|
|
163
171
|
</EscalatedLayout>
|
|
164
172
|
</template>
|
|
@@ -58,9 +58,9 @@ function toggleSort(col) {
|
|
|
58
58
|
|
|
59
59
|
<template>
|
|
60
60
|
<EscalatedLayout title="First Response Time Analysis">
|
|
61
|
-
<div class="mb-6 flex items-center justify-between">
|
|
61
|
+
<div class="mb-6 flex flex-wrap items-center justify-between gap-3">
|
|
62
62
|
<PeriodSelector :model-value="periodDays" @update:model-value="changePeriod" />
|
|
63
|
-
<div class="flex items-center gap-3">
|
|
63
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
64
64
|
<ReportExportButton report-name="response-times" :period-days="periodDays" />
|
|
65
65
|
<Link
|
|
66
66
|
:href="route('escalated.admin.reports')"
|
|
@@ -114,51 +114,59 @@ function toggleSort(col) {
|
|
|
114
114
|
<div class="border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)] px-4 py-3">
|
|
115
115
|
<h3 class="text-sm font-semibold text-[var(--esc-panel-text-secondary)]">FRT by Agent</h3>
|
|
116
116
|
</div>
|
|
117
|
-
<
|
|
118
|
-
<
|
|
119
|
-
<
|
|
120
|
-
<
|
|
121
|
-
|
|
117
|
+
<div class="esc-table-scroll">
|
|
118
|
+
<table class="w-full">
|
|
119
|
+
<thead>
|
|
120
|
+
<tr class="border-b border-[var(--esc-panel-border)]">
|
|
121
|
+
<th
|
|
122
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
123
|
+
>
|
|
124
|
+
Agent
|
|
125
|
+
</th>
|
|
126
|
+
<th
|
|
127
|
+
v-for="col in [
|
|
128
|
+
{ key: 'avg', label: 'Avg' },
|
|
129
|
+
{ key: 'median', label: 'Median' },
|
|
130
|
+
{ key: 'p90', label: 'P90' },
|
|
131
|
+
]"
|
|
132
|
+
:key="col.key"
|
|
133
|
+
class="cursor-pointer px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)] hover:text-[var(--esc-panel-text-secondary)]"
|
|
134
|
+
@click="toggleSort(col.key)"
|
|
135
|
+
>
|
|
136
|
+
{{ col.label }}
|
|
137
|
+
<span v-if="sortColumn === col.key">{{
|
|
138
|
+
sortDir === 'asc' ? ' ▲' : ' ▼'
|
|
139
|
+
}}</span>
|
|
140
|
+
</th>
|
|
141
|
+
</tr>
|
|
142
|
+
</thead>
|
|
143
|
+
<tbody class="divide-y divide-[var(--esc-panel-border)]">
|
|
144
|
+
<tr
|
|
145
|
+
v-for="agent in sortedAgents"
|
|
146
|
+
:key="agent.agent_id"
|
|
147
|
+
class="hover:bg-[var(--esc-panel-hover)]"
|
|
122
148
|
>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class="
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
</td>
|
|
145
|
-
<td class="px-4 py-3 text-sm font-medium" :class="frtColor(agent.avg)">
|
|
146
|
-
{{ agent.avg ?? '—' }}h
|
|
147
|
-
</td>
|
|
148
|
-
<td class="px-4 py-3 text-sm font-medium" :class="frtColor(agent.median)">
|
|
149
|
-
{{ agent.median ?? '—' }}h
|
|
150
|
-
</td>
|
|
151
|
-
<td class="px-4 py-3 text-sm font-medium" :class="frtColor(agent.p90)">
|
|
152
|
-
{{ agent.p90 ?? '—' }}h
|
|
153
|
-
</td>
|
|
154
|
-
</tr>
|
|
155
|
-
<tr v-if="!sortedAgents.length">
|
|
156
|
-
<td colspan="4" class="px-4 py-8 text-center text-sm text-[var(--esc-panel-text-muted)]">
|
|
157
|
-
No agent data for this period.
|
|
158
|
-
</td>
|
|
159
|
-
</tr>
|
|
160
|
-
</tbody>
|
|
161
|
-
</table>
|
|
149
|
+
<td class="px-4 py-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
|
|
150
|
+
{{ agent.agent_name }}
|
|
151
|
+
</td>
|
|
152
|
+
<td class="px-4 py-3 text-sm font-medium" :class="frtColor(agent.avg)">
|
|
153
|
+
{{ agent.avg ?? '—' }}h
|
|
154
|
+
</td>
|
|
155
|
+
<td class="px-4 py-3 text-sm font-medium" :class="frtColor(agent.median)">
|
|
156
|
+
{{ agent.median ?? '—' }}h
|
|
157
|
+
</td>
|
|
158
|
+
<td class="px-4 py-3 text-sm font-medium" :class="frtColor(agent.p90)">
|
|
159
|
+
{{ agent.p90 ?? '—' }}h
|
|
160
|
+
</td>
|
|
161
|
+
</tr>
|
|
162
|
+
<tr v-if="!sortedAgents.length">
|
|
163
|
+
<td colspan="4" class="px-4 py-8 text-center text-sm text-[var(--esc-panel-text-muted)]">
|
|
164
|
+
No agent data for this period.
|
|
165
|
+
</td>
|
|
166
|
+
</tr>
|
|
167
|
+
</tbody>
|
|
168
|
+
</table>
|
|
169
|
+
</div>
|
|
162
170
|
</div>
|
|
163
171
|
</EscalatedLayout>
|
|
164
172
|
</template>
|
|
@@ -89,74 +89,76 @@ const resolutionBreaches = computed(() => {
|
|
|
89
89
|
<div class="border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)] px-4 py-3">
|
|
90
90
|
<h3 class="text-sm font-semibold text-[var(--esc-panel-text-secondary)]">Breach Details</h3>
|
|
91
91
|
</div>
|
|
92
|
-
<
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
<
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
92
|
+
<div class="esc-table-scroll">
|
|
93
|
+
<table class="w-full">
|
|
94
|
+
<thead>
|
|
95
|
+
<tr class="border-b border-[var(--esc-panel-border)]">
|
|
96
|
+
<th
|
|
97
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
98
|
+
>
|
|
99
|
+
Ticket
|
|
100
|
+
</th>
|
|
101
|
+
<th
|
|
102
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
103
|
+
>
|
|
104
|
+
Subject
|
|
105
|
+
</th>
|
|
106
|
+
<th
|
|
107
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
108
|
+
>
|
|
109
|
+
SLA Policy
|
|
110
|
+
</th>
|
|
111
|
+
<th
|
|
112
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
113
|
+
>
|
|
114
|
+
Breach Type
|
|
115
|
+
</th>
|
|
116
|
+
<th
|
|
117
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
118
|
+
>
|
|
119
|
+
Created
|
|
120
|
+
</th>
|
|
121
|
+
</tr>
|
|
122
|
+
</thead>
|
|
123
|
+
<tbody class="divide-y divide-[var(--esc-panel-border)]">
|
|
124
|
+
<tr v-for="ticket in breaches" :key="ticket.id" class="hover:bg-[var(--esc-panel-hover)]">
|
|
125
|
+
<td class="px-4 py-3 text-sm font-mono text-[var(--esc-panel-accent)]">
|
|
126
|
+
{{ ticket.reference }}
|
|
127
|
+
</td>
|
|
128
|
+
<td class="max-w-xs truncate px-4 py-3 text-sm text-[var(--esc-panel-text-secondary)]">
|
|
129
|
+
{{ ticket.subject }}
|
|
130
|
+
</td>
|
|
131
|
+
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
|
|
132
|
+
{{ ticket.sla_policy?.name || '—' }}
|
|
133
|
+
</td>
|
|
134
|
+
<td class="px-4 py-3">
|
|
135
|
+
<div class="flex gap-1.5">
|
|
136
|
+
<span
|
|
137
|
+
v-if="ticket.sla_first_response_breached"
|
|
138
|
+
class="inline-flex rounded-full border border-red-500/20 bg-red-500/10 px-2 py-0.5 text-xs font-medium text-red-400"
|
|
139
|
+
>
|
|
140
|
+
Response
|
|
141
|
+
</span>
|
|
142
|
+
<span
|
|
143
|
+
v-if="ticket.sla_resolution_breached"
|
|
144
|
+
class="inline-flex rounded-full border border-red-500/20 bg-red-500/10 px-2 py-0.5 text-xs font-medium text-red-400"
|
|
145
|
+
>
|
|
146
|
+
Resolution
|
|
147
|
+
</span>
|
|
148
|
+
</div>
|
|
149
|
+
</td>
|
|
150
|
+
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-muted)]">
|
|
151
|
+
{{ ticket.created_at ? new Date(ticket.created_at).toLocaleDateString() : '—' }}
|
|
152
|
+
</td>
|
|
153
|
+
</tr>
|
|
154
|
+
<tr v-if="!breaches?.length">
|
|
155
|
+
<td colspan="5" class="px-4 py-8 text-center text-sm text-[var(--esc-panel-text-muted)]">
|
|
156
|
+
No SLA breaches in this period.
|
|
157
|
+
</td>
|
|
158
|
+
</tr>
|
|
159
|
+
</tbody>
|
|
160
|
+
</table>
|
|
161
|
+
</div>
|
|
160
162
|
</div>
|
|
161
163
|
</EscalatedLayout>
|
|
162
164
|
</template>
|
|
@@ -39,9 +39,9 @@ const riskCategories = computed(() => {
|
|
|
39
39
|
|
|
40
40
|
<template>
|
|
41
41
|
<EscalatedLayout title="SLA Breach Trends">
|
|
42
|
-
<div class="mb-6 flex items-center justify-between">
|
|
42
|
+
<div class="mb-6 flex flex-wrap items-center justify-between gap-3">
|
|
43
43
|
<PeriodSelector :model-value="periodDays" @update:model-value="changePeriod" />
|
|
44
|
-
<div class="flex items-center gap-3">
|
|
44
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
45
45
|
<ReportExportButton report-name="sla-trends" :period-days="periodDays" />
|
|
46
46
|
<Link
|
|
47
47
|
:href="route('escalated.admin.reports')"
|
|
@@ -31,7 +31,7 @@ const { getPageComponents } = usePluginExtensions();
|
|
|
31
31
|
<EscalatedLayout title="Reports">
|
|
32
32
|
<!-- Sub-report navigation -->
|
|
33
33
|
<div
|
|
34
|
-
class="mb-6 flex items-center gap-3 rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)] p-3"
|
|
34
|
+
class="mb-6 flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)] p-3"
|
|
35
35
|
>
|
|
36
36
|
<span class="text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
37
37
|
>Detailed Reports:</span
|
|
@@ -22,7 +22,7 @@ function destroy(role) {
|
|
|
22
22
|
Add Role
|
|
23
23
|
</Link>
|
|
24
24
|
</div>
|
|
25
|
-
<div class="
|
|
25
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
26
26
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
27
27
|
<thead>
|
|
28
28
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -88,7 +88,7 @@ function formatCount(value) {
|
|
|
88
88
|
table is not affected — it stays wherever your application keeps it.
|
|
89
89
|
</p>
|
|
90
90
|
|
|
91
|
-
<dl v-if="active" class="grid grid-cols-
|
|
91
|
+
<dl v-if="active" class="grid grid-cols-[auto_minmax(0,1fr)] gap-x-6 gap-y-3 text-sm">
|
|
92
92
|
<dt class="text-[var(--esc-panel-text-muted)]">Connection</dt>
|
|
93
93
|
<dd class="font-medium text-[var(--esc-panel-text)]">
|
|
94
94
|
{{ active.name }}
|
|
@@ -160,7 +160,7 @@ function formatCount(value) {
|
|
|
160
160
|
{{ statusLabel(connection) }}
|
|
161
161
|
</span>
|
|
162
162
|
</span>
|
|
163
|
-
<span class="mt-0.5 block text-xs text-[var(--esc-panel-text-muted)]">
|
|
163
|
+
<span class="mt-0.5 block break-all text-xs text-[var(--esc-panel-text-muted)]">
|
|
164
164
|
{{ connection.driver ?? 'unknown driver' }} ·
|
|
165
165
|
{{ connection.database ?? 'no database configured' }}
|
|
166
166
|
<template v-if="connection.ticket_count !== null">
|
|
@@ -217,7 +217,7 @@ function formatCount(value) {
|
|
|
217
217
|
<button
|
|
218
218
|
type="submit"
|
|
219
219
|
:disabled="form.processing || !isChanging"
|
|
220
|
-
class="rounded-lg bg-[var(--esc-accent)] px-4 py-2 text-sm font-medium text-white disabled:opacity-50"
|
|
220
|
+
class="rounded-lg bg-[var(--esc-panel-accent)] px-4 py-2 text-sm font-medium text-white disabled:opacity-50"
|
|
221
221
|
>
|
|
222
222
|
{{ form.processing ? 'Saving…' : 'Use this connection' }}
|
|
223
223
|
</button>
|
|
@@ -71,7 +71,7 @@ const dkimStatusClasses = {
|
|
|
71
71
|
<!-- Address Table -->
|
|
72
72
|
<div
|
|
73
73
|
v-if="form.addresses.length"
|
|
74
|
-
class="
|
|
74
|
+
class="esc-table-scroll rounded-lg border border-[var(--esc-panel-border)]"
|
|
75
75
|
>
|
|
76
76
|
<table class="w-full">
|
|
77
77
|
<thead>
|
|
@@ -21,7 +21,7 @@ function destroy(id) {
|
|
|
21
21
|
Add Skill
|
|
22
22
|
</Link>
|
|
23
23
|
</div>
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
25
25
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
26
26
|
<thead>
|
|
27
27
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -21,7 +21,7 @@ function destroy(id) {
|
|
|
21
21
|
Add Policy
|
|
22
22
|
</Link>
|
|
23
23
|
</div>
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
25
25
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
26
26
|
<thead>
|
|
27
27
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -70,7 +70,7 @@ function destroy(id) {
|
|
|
70
70
|
{{ categoryLabels[cat] || cat }}
|
|
71
71
|
</h3>
|
|
72
72
|
<div
|
|
73
|
-
class="
|
|
73
|
+
class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]"
|
|
74
74
|
>
|
|
75
75
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
76
76
|
<thead>
|
|
@@ -85,7 +85,7 @@ function destroy(id) {
|
|
|
85
85
|
Create
|
|
86
86
|
</button>
|
|
87
87
|
</form>
|
|
88
|
-
<div class="
|
|
88
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
89
89
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
90
90
|
<thead>
|
|
91
91
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -42,7 +42,7 @@ function isSelf(user) {
|
|
|
42
42
|
</form>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
|
-
<div class="
|
|
45
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
46
46
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
47
47
|
<thead>
|
|
48
48
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -30,7 +30,7 @@ function togglePayload(id) {
|
|
|
30
30
|
</a>
|
|
31
31
|
</div>
|
|
32
32
|
|
|
33
|
-
<div class="
|
|
33
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
34
34
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
35
35
|
<thead>
|
|
36
36
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -27,7 +27,7 @@ function lastDeliveryStatus(webhook) {
|
|
|
27
27
|
Add Webhook
|
|
28
28
|
</Link>
|
|
29
29
|
</div>
|
|
30
|
-
<div class="
|
|
30
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
31
31
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
32
32
|
<thead>
|
|
33
33
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -95,7 +95,7 @@ function triggerBadgeClass(trigger) {
|
|
|
95
95
|
</div>
|
|
96
96
|
</div>
|
|
97
97
|
|
|
98
|
-
<div class="
|
|
98
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
99
99
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
100
100
|
<thead>
|
|
101
101
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -114,7 +114,7 @@ function actionStatusIcon(status) {
|
|
|
114
114
|
</div>
|
|
115
115
|
|
|
116
116
|
<!-- Logs table -->
|
|
117
|
-
<div class="
|
|
117
|
+
<div class="esc-table-scroll rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
|
|
118
118
|
<table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
|
|
119
119
|
<thead>
|
|
120
120
|
<tr class="bg-[var(--esc-panel-hover)]">
|
|
@@ -144,7 +144,7 @@ const loadWidgetComponent = (plugin, component) => {
|
|
|
144
144
|
</h2>
|
|
145
145
|
<div
|
|
146
146
|
:class="[
|
|
147
|
-
'
|
|
147
|
+
'esc-table-scroll rounded-xl border',
|
|
148
148
|
escDark ? 'border-white/[0.06] bg-neutral-900/60' : 'border-gray-200 bg-white',
|
|
149
149
|
]"
|
|
150
150
|
>
|
package/src/plugin.js
CHANGED
|
@@ -159,6 +159,18 @@ const panelTokenMap = {
|
|
|
159
159
|
active: '--esc-panel-active',
|
|
160
160
|
};
|
|
161
161
|
|
|
162
|
+
// Optional panel hooks. The layout reads each through a CSS fallback to the
|
|
163
|
+
// token it used before the hook existed, so a host that leaves one unset sees
|
|
164
|
+
// no change. They are set only when given, and cleared otherwise, so a host
|
|
165
|
+
// stylesheet can also set them on :root.
|
|
166
|
+
const panelOptionalTokenMap = {
|
|
167
|
+
activeText: '--esc-panel-active-text', // active nav item text + icon (falls back to text)
|
|
168
|
+
headerBg: '--esc-panel-header-bg', // admin top bar / agent top nav (falls back to topbarBg / sidebarBg)
|
|
169
|
+
headerText: '--esc-panel-header-text', // page title and menu button in that bar (falls back to text)
|
|
170
|
+
logoTileBg: '--esc-panel-logo-tile-bg', // square behind the logo (falls back to borderInput)
|
|
171
|
+
logoTileFg: '--esc-panel-logo-tile-fg', // currentColor inside the logo tile (falls back to text)
|
|
172
|
+
};
|
|
173
|
+
|
|
162
174
|
function applyPanelTheme(panelConfig) {
|
|
163
175
|
const defaults = panelConfig.mode === 'light' ? panelLightDefaults : panelDarkDefaults;
|
|
164
176
|
const overrides = {};
|
|
@@ -172,4 +184,11 @@ function applyPanelTheme(panelConfig) {
|
|
|
172
184
|
for (const [key, cssVar] of Object.entries(panelTokenMap)) {
|
|
173
185
|
style.setProperty(cssVar, merged[key]);
|
|
174
186
|
}
|
|
187
|
+
for (const [key, cssVar] of Object.entries(panelOptionalTokenMap)) {
|
|
188
|
+
if (panelConfig[key] !== undefined && panelConfig[key] !== null) {
|
|
189
|
+
style.setProperty(cssVar, panelConfig[key]);
|
|
190
|
+
} else {
|
|
191
|
+
style.removeProperty(cssVar);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
175
194
|
}
|