agent-relay-server 0.4.22 → 0.4.23
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/package.json +1 -1
- package/public/dashboard.js +730 -9
- package/public/index.html +678 -5
- package/src/db.ts +154 -1
- package/src/routes.ts +105 -0
- package/src/security.ts +2 -1
- package/src/types.ts +23 -0
package/public/index.html
CHANGED
|
@@ -81,6 +81,44 @@
|
|
|
81
81
|
.msg-card.thread-child { margin-left: 24px; border-left-color: var(--tblr-border-color); }
|
|
82
82
|
|
|
83
83
|
.msg-body { white-space: pre-wrap; word-break: break-word; font-size: 13px; }
|
|
84
|
+
.inbox-fit { height: calc(100vh - 280px); min-height: 380px; }
|
|
85
|
+
.inbox-thread { cursor: pointer; min-height: 72px; }
|
|
86
|
+
.inbox-thread.active { background: var(--tblr-bg-surface-secondary); border-left: 3px solid var(--tblr-primary); }
|
|
87
|
+
.inbox-thread.attention { border-left: 3px solid var(--tblr-warning); }
|
|
88
|
+
.inbox-thread-snippet { max-width: 100%; }
|
|
89
|
+
.attention-card { border-left: 3px solid var(--tblr-warning); color: inherit; }
|
|
90
|
+
button.attention-card { background: var(--tblr-bg-surface); border-top: 1px solid var(--tblr-border-color); border-right: 1px solid var(--tblr-border-color); border-bottom: 1px solid var(--tblr-border-color); }
|
|
91
|
+
button.attention-card:hover { background: var(--tblr-bg-surface-secondary); }
|
|
92
|
+
.attention-empty { border-left-color: var(--tblr-border-color); }
|
|
93
|
+
.attention-badges .badge { font-weight: 500; }
|
|
94
|
+
.pair-badge .ti { font-size: 13px; vertical-align: -2px; }
|
|
95
|
+
.pair-card { border-left: 3px solid var(--tblr-border-color); }
|
|
96
|
+
.pair-card.active { border-left-color: var(--tblr-success); }
|
|
97
|
+
.pair-card.pending { border-left-color: var(--tblr-warning); }
|
|
98
|
+
.agent-drawer-backdrop {
|
|
99
|
+
position: fixed;
|
|
100
|
+
inset: 0;
|
|
101
|
+
background: rgba(0, 0, 0, 0.45);
|
|
102
|
+
z-index: 1030;
|
|
103
|
+
}
|
|
104
|
+
.agent-drawer {
|
|
105
|
+
position: fixed;
|
|
106
|
+
top: 0;
|
|
107
|
+
right: 0;
|
|
108
|
+
width: min(480px, 100vw);
|
|
109
|
+
height: 100vh;
|
|
110
|
+
overflow-y: auto;
|
|
111
|
+
background: var(--tblr-bg-surface);
|
|
112
|
+
border-left: 1px solid var(--tblr-border-color);
|
|
113
|
+
z-index: 1040;
|
|
114
|
+
box-shadow: -16px 0 48px rgba(0, 0, 0, 0.35);
|
|
115
|
+
}
|
|
116
|
+
.detail-row {
|
|
117
|
+
display: grid;
|
|
118
|
+
grid-template-columns: 96px minmax(0, 1fr);
|
|
119
|
+
gap: 12px;
|
|
120
|
+
align-items: start;
|
|
121
|
+
}
|
|
84
122
|
|
|
85
123
|
.fade-in { animation: fadeIn 0.2s ease-in; }
|
|
86
124
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -110,17 +148,29 @@
|
|
|
110
148
|
<nav class="nav flex-column py-2">
|
|
111
149
|
<a href="#" class="nav-link" :class="{ active: view === 'overview' }" @click.prevent="switchView('overview')">
|
|
112
150
|
<i class="ti ti-dashboard"></i>Overview
|
|
151
|
+
<span class="badge bg-warning text-white ms-auto" x-show="attentionSummary.total > 0" x-text="attentionSummary.total"></span>
|
|
113
152
|
</a>
|
|
114
153
|
<a href="#" class="nav-link" :class="{ active: view === 'agents' }" @click.prevent="switchView('agents')">
|
|
115
154
|
<i class="ti ti-robot"></i>Agents
|
|
116
|
-
<span class="badge bg-
|
|
155
|
+
<span class="badge bg-warning text-white ms-auto" x-show="attentionAgentCount > 0" x-text="attentionAgentCount"></span>
|
|
156
|
+
<span class="badge bg-success text-white ms-1" x-text="onlineCount"></span>
|
|
157
|
+
</a>
|
|
158
|
+
<a href="#" class="nav-link" :class="{ active: view === 'inbox' }" @click.prevent="switchView('inbox')">
|
|
159
|
+
<i class="ti ti-inbox"></i>Inbox
|
|
160
|
+
<span class="badge bg-danger text-white ms-auto" x-show="attentionSummary.unreadInbox > 0" x-text="attentionSummary.unreadInbox"></span>
|
|
161
|
+
</a>
|
|
162
|
+
<a href="#" class="nav-link" :class="{ active: view === 'pairs' }" @click.prevent="switchView('pairs')">
|
|
163
|
+
<i class="ti ti-link"></i>Pairs
|
|
164
|
+
<span class="badge bg-warning text-white ms-auto" x-show="attentionSummary.pendingPairInvites > 0" x-text="attentionSummary.pendingPairInvites"></span>
|
|
165
|
+
<span class="badge bg-primary text-white ms-1" x-text="pairs.length"></span>
|
|
117
166
|
</a>
|
|
118
167
|
<a href="#" class="nav-link" :class="{ active: view === 'messages' }" @click.prevent="switchView('messages')">
|
|
119
168
|
<i class="ti ti-messages"></i>Messages
|
|
120
169
|
</a>
|
|
121
170
|
<a href="#" class="nav-link" :class="{ active: view === 'tasks' }" @click.prevent="switchView('tasks')">
|
|
122
171
|
<i class="ti ti-checkup-list"></i>Tasks
|
|
123
|
-
<span class="badge bg-warning text-white ms-auto" x-
|
|
172
|
+
<span class="badge bg-warning text-white ms-auto" x-show="attentionSummary.claimableTasks > 0" x-text="attentionSummary.claimableTasks"></span>
|
|
173
|
+
<span class="badge bg-secondary text-white ms-1" x-text="stats.openTasks ?? 0"></span>
|
|
124
174
|
</a>
|
|
125
175
|
<a href="#" class="nav-link" :class="{ active: view === 'analytics' }" @click.prevent="switchView('analytics')">
|
|
126
176
|
<i class="ti ti-chart-area-line"></i>Analytics
|
|
@@ -149,7 +199,7 @@
|
|
|
149
199
|
|
|
150
200
|
<!-- Mobile nav -->
|
|
151
201
|
<div class="mobile-nav d-none border-bottom p-2 gap-1 position-fixed top-0 w-100 bg-dark" style="z-index:50">
|
|
152
|
-
<template x-for="v in ['overview','agents','messages','tasks','analytics']">
|
|
202
|
+
<template x-for="v in ['overview','agents','inbox','pairs','messages','tasks','analytics']">
|
|
153
203
|
<button class="btn btn-sm" :class="view === v ? 'btn-primary' : 'btn-ghost-secondary'" @click="switchView(v)" x-text="v.charAt(0).toUpperCase() + v.slice(1)"></button>
|
|
154
204
|
</template>
|
|
155
205
|
</div>
|
|
@@ -253,6 +303,49 @@
|
|
|
253
303
|
</div>
|
|
254
304
|
</template>
|
|
255
305
|
|
|
306
|
+
<div class="row g-3 mb-4">
|
|
307
|
+
<div class="col-md-6 col-xl">
|
|
308
|
+
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.unreadInbox === 0 }" @click="switchView('inbox')">
|
|
309
|
+
<div class="card-body py-3">
|
|
310
|
+
<div class="text-secondary small">Unread</div>
|
|
311
|
+
<div class="h2 mb-0" :class="attentionSummary.unreadInbox ? 'text-danger' : ''" x-text="attentionSummary.unreadInbox"></div>
|
|
312
|
+
</div>
|
|
313
|
+
</button>
|
|
314
|
+
</div>
|
|
315
|
+
<div class="col-md-6 col-xl">
|
|
316
|
+
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.needsHumanResponse === 0 }" @click="switchView('inbox')">
|
|
317
|
+
<div class="card-body py-3">
|
|
318
|
+
<div class="text-secondary small">Needs response</div>
|
|
319
|
+
<div class="h2 mb-0" :class="attentionSummary.needsHumanResponse ? 'text-warning' : ''" x-text="attentionSummary.needsHumanResponse"></div>
|
|
320
|
+
</div>
|
|
321
|
+
</button>
|
|
322
|
+
</div>
|
|
323
|
+
<div class="col-md-6 col-xl">
|
|
324
|
+
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.agentQuestions === 0 }" @click="switchView('inbox')">
|
|
325
|
+
<div class="card-body py-3">
|
|
326
|
+
<div class="text-secondary small">Agent questions</div>
|
|
327
|
+
<div class="h2 mb-0" :class="attentionSummary.agentQuestions ? 'text-info' : ''" x-text="attentionSummary.agentQuestions"></div>
|
|
328
|
+
</div>
|
|
329
|
+
</button>
|
|
330
|
+
</div>
|
|
331
|
+
<div class="col-md-6 col-xl">
|
|
332
|
+
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.pendingPairInvites === 0 }" @click="switchView('pairs')">
|
|
333
|
+
<div class="card-body py-3">
|
|
334
|
+
<div class="text-secondary small">Pair invites</div>
|
|
335
|
+
<div class="h2 mb-0" :class="attentionSummary.pendingPairInvites ? 'text-warning' : ''" x-text="attentionSummary.pendingPairInvites"></div>
|
|
336
|
+
</div>
|
|
337
|
+
</button>
|
|
338
|
+
</div>
|
|
339
|
+
<div class="col-md-6 col-xl">
|
|
340
|
+
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.claimableTasks === 0 }" @click="switchView('tasks')">
|
|
341
|
+
<div class="card-body py-3">
|
|
342
|
+
<div class="text-secondary small">Claimable waiting</div>
|
|
343
|
+
<div class="h2 mb-0" :class="attentionSummary.claimableTasks ? 'text-warning' : ''" x-text="attentionSummary.claimableTasks"></div>
|
|
344
|
+
</div>
|
|
345
|
+
</button>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
256
349
|
<!-- Two-column: Agents + Recent messages -->
|
|
257
350
|
<div class="row g-3">
|
|
258
351
|
<div class="col-lg-5">
|
|
@@ -263,7 +356,7 @@
|
|
|
263
356
|
</div>
|
|
264
357
|
<div class="list-group list-group-flush" style="max-height: 60vh; overflow-y: auto">
|
|
265
358
|
<template x-for="a in sortedAgents.slice(0, 20)" :key="a.id">
|
|
266
|
-
<div class="list-group-item d-flex align-items-center gap-2" style="cursor:pointer" @click="
|
|
359
|
+
<div class="list-group-item d-flex align-items-center gap-2" style="cursor:pointer" @click="openAgentDetail(a)">
|
|
267
360
|
<span class="status-dot" :class="[a.status, a.status !== 'offline' && !a.ready ? 'not-ready' : '']"></span>
|
|
268
361
|
<span class="agent-type-icon" :class="agentType(a)" :title="agentTypeTitle(a)" :aria-label="agentTypeTitle(a)">
|
|
269
362
|
<i class="ti" :class="agentTypeIcon(a)"></i>
|
|
@@ -278,6 +371,18 @@
|
|
|
278
371
|
</template>
|
|
279
372
|
</div>
|
|
280
373
|
<div class="text-secondary small text-truncate" x-text="a.id"></div>
|
|
374
|
+
<template x-if="agentPair(a)">
|
|
375
|
+
<span class="badge pair-badge mt-1" :class="pairBadgeClass(agentPair(a))" :title="pairTitle(agentPair(a), a.id)">
|
|
376
|
+
<i class="ti ti-link me-1"></i><span x-text="pairBadgeLabel(agentPair(a), a.id)"></span>
|
|
377
|
+
</span>
|
|
378
|
+
</template>
|
|
379
|
+
<div class="attention-badges d-flex gap-1 mt-1 flex-wrap" x-show="agentAttention(a).total > 0" :title="agentAttentionTitle(a)">
|
|
380
|
+
<span class="badge bg-danger-lt" x-show="agentAttention(a).unread" x-text="agentAttention(a).unread + ' unread'"></span>
|
|
381
|
+
<span class="badge bg-warning-lt" x-show="agentAttention(a).needsHumanResponse">needs response</span>
|
|
382
|
+
<span class="badge bg-info-lt" x-show="agentAttention(a).agentQuestion">question</span>
|
|
383
|
+
<span class="badge bg-warning-lt" x-show="agentAttention(a).pendingPairInvite">pair invite</span>
|
|
384
|
+
<span class="badge bg-orange-lt" x-show="agentAttention(a).claimableTasks" x-text="agentAttention(a).claimableTasks + ' claimable'"></span>
|
|
385
|
+
</div>
|
|
281
386
|
</div>
|
|
282
387
|
<span class="text-secondary small" x-text="timeAgo(a.lastSeen)"></span>
|
|
283
388
|
</div>
|
|
@@ -365,7 +470,7 @@
|
|
|
365
470
|
<div class="row g-3">
|
|
366
471
|
<template x-for="a in sortedAgents" :key="a.id">
|
|
367
472
|
<div class="col-md-6 col-xl-4">
|
|
368
|
-
<div class="card agent-card" :class="{ selected: selectedAgent === a.id }">
|
|
473
|
+
<div class="card agent-card" :class="{ selected: selectedAgent === a.id }" @click="openAgentDetail(a)">
|
|
369
474
|
<div class="card-body">
|
|
370
475
|
<div class="d-flex align-items-start gap-2">
|
|
371
476
|
<span class="status-dot mt-1" :class="[a.status, a.status !== 'offline' && !a.ready ? 'not-ready' : '']" :title="agentStatusTitle(a)"></span>
|
|
@@ -381,6 +486,20 @@
|
|
|
381
486
|
</div>
|
|
382
487
|
<div class="text-secondary small text-truncate mt-1" x-text="a.id"></div>
|
|
383
488
|
<div class="d-flex gap-1 mt-1 flex-wrap">
|
|
489
|
+
<template x-if="agentAttention(a).total > 0">
|
|
490
|
+
<span class="badge bg-warning text-white" :title="agentAttentionTitle(a)">
|
|
491
|
+
<i class="ti ti-bell me-1"></i><span x-text="agentAttention(a).total"></span>
|
|
492
|
+
</span>
|
|
493
|
+
</template>
|
|
494
|
+
<template x-if="agentPair(a)">
|
|
495
|
+
<span class="badge pair-badge" :class="pairBadgeClass(agentPair(a))" :title="pairTitle(agentPair(a), a.id)">
|
|
496
|
+
<i class="ti ti-link me-1"></i><span x-text="pairBadgeLabel(agentPair(a), a.id)"></span>
|
|
497
|
+
</span>
|
|
498
|
+
</template>
|
|
499
|
+
<span class="badge bg-danger-lt" x-show="agentAttention(a).unread" x-text="agentAttention(a).unread + ' unread'"></span>
|
|
500
|
+
<span class="badge bg-warning-lt" x-show="agentAttention(a).needsHumanResponse">needs response</span>
|
|
501
|
+
<span class="badge bg-info-lt" x-show="agentAttention(a).agentQuestion">question</span>
|
|
502
|
+
<span class="badge bg-orange-lt" x-show="agentAttention(a).claimableTasks" x-text="agentAttention(a).claimableTasks + ' claimable'"></span>
|
|
384
503
|
<template x-if="a.machine">
|
|
385
504
|
<span class="badge bg-secondary-lt" x-text="a.machine"></span>
|
|
386
505
|
</template>
|
|
@@ -439,6 +558,296 @@
|
|
|
439
558
|
</template>
|
|
440
559
|
</div>
|
|
441
560
|
|
|
561
|
+
<!-- ==================== INBOX ==================== -->
|
|
562
|
+
<div x-show="view === 'inbox'" x-cloak class="fade-in">
|
|
563
|
+
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
564
|
+
<h2 class="page-title mb-0">Inbox</h2>
|
|
565
|
+
<span class="badge bg-danger-lt" x-show="attentionSummary.unreadInbox > 0" x-text="attentionSummary.unreadInbox + ' unread'"></span>
|
|
566
|
+
<span class="badge bg-warning-lt" x-show="attentionSummary.needsHumanResponse > 0" x-text="attentionSummary.needsHumanResponse + ' need response'"></span>
|
|
567
|
+
<span class="badge bg-info-lt" x-show="attentionSummary.agentQuestions > 0" x-text="attentionSummary.agentQuestions + ' questions'"></span>
|
|
568
|
+
<div class="ms-auto d-flex gap-2 align-items-center flex-wrap">
|
|
569
|
+
<input type="search" class="form-control form-control-sm" style="width: 220px" placeholder="Search inbox" x-model.debounce.200ms="inboxSearch">
|
|
570
|
+
<label class="form-check form-switch mb-0">
|
|
571
|
+
<input type="checkbox" class="form-check-input" x-model="inboxShowArchived">
|
|
572
|
+
<span class="form-check-label small">Archived</span>
|
|
573
|
+
</label>
|
|
574
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="fetchMessages()">
|
|
575
|
+
<i class="ti ti-refresh"></i>
|
|
576
|
+
</button>
|
|
577
|
+
</div>
|
|
578
|
+
</div>
|
|
579
|
+
|
|
580
|
+
<div class="card mb-3">
|
|
581
|
+
<div class="card-body">
|
|
582
|
+
<div class="row g-2 align-items-start">
|
|
583
|
+
<div class="col-md-2">
|
|
584
|
+
<select class="form-select form-select-sm" x-model="inboxCompose.toMode" @change="resetInboxComposeTarget()">
|
|
585
|
+
<option value="agent">Agent</option>
|
|
586
|
+
<option value="tag">Tag</option>
|
|
587
|
+
<option value="cap">Capability</option>
|
|
588
|
+
</select>
|
|
589
|
+
</div>
|
|
590
|
+
<div class="col-md-3">
|
|
591
|
+
<select class="form-select form-select-sm" x-model="inboxCompose.to">
|
|
592
|
+
<option value="">Target</option>
|
|
593
|
+
<template x-for="option in inboxComposeTargetOptions" :key="option.value">
|
|
594
|
+
<option :value="option.value" x-text="option.label"></option>
|
|
595
|
+
</template>
|
|
596
|
+
</select>
|
|
597
|
+
</div>
|
|
598
|
+
<div class="col-md-3">
|
|
599
|
+
<input type="text" class="form-control form-control-sm" placeholder="Subject" x-model="inboxCompose.subject">
|
|
600
|
+
</div>
|
|
601
|
+
<div class="col-md-2">
|
|
602
|
+
<input type="text" class="form-control form-control-sm" placeholder="Channel" x-model="inboxCompose.channel">
|
|
603
|
+
</div>
|
|
604
|
+
<div class="col-md-2 d-flex gap-2 align-items-center">
|
|
605
|
+
<label class="form-check mb-0">
|
|
606
|
+
<input type="checkbox" class="form-check-input" x-model="inboxCompose.claimable">
|
|
607
|
+
<span class="form-check-label small">Claimable</span>
|
|
608
|
+
</label>
|
|
609
|
+
</div>
|
|
610
|
+
<div class="col-12">
|
|
611
|
+
<div class="d-flex gap-2">
|
|
612
|
+
<textarea class="form-control" rows="2" placeholder="Message" x-model="inboxCompose.body" @keydown.ctrl.enter.prevent="doSendInboxCompose()" @keydown.meta.enter.prevent="doSendInboxCompose()"></textarea>
|
|
613
|
+
<button class="btn btn-primary align-self-stretch" @click="doSendInboxCompose()" :disabled="!inboxCompose.to || !inboxCompose.body">
|
|
614
|
+
<i class="ti ti-send"></i>
|
|
615
|
+
</button>
|
|
616
|
+
</div>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
|
|
622
|
+
<div class="row g-3 inbox-fit">
|
|
623
|
+
<div class="col-lg-4 h-100">
|
|
624
|
+
<div class="card h-100">
|
|
625
|
+
<div class="list-group list-group-flush h-100 overflow-auto">
|
|
626
|
+
<template x-for="thread in inboxThreads" :key="thread.id">
|
|
627
|
+
<div
|
|
628
|
+
class="list-group-item list-group-item-action inbox-thread"
|
|
629
|
+
:class="{ active: selectedInboxThreadData?.id === thread.id, attention: thread.attention?.score > 0, 'text-secondary': thread.archived }"
|
|
630
|
+
@click="openInboxThread(thread)"
|
|
631
|
+
>
|
|
632
|
+
<div class="d-flex align-items-center gap-2">
|
|
633
|
+
<span class="agent-type-icon" :class="agentType(agentsById[thread.peer])" :title="agentTypeTitle(agentsById[thread.peer])">
|
|
634
|
+
<i class="ti" :class="agentTypeIcon(agentsById[thread.peer])"></i>
|
|
635
|
+
</span>
|
|
636
|
+
<span class="fw-bold text-truncate" x-text="conversationTitle(thread)"></span>
|
|
637
|
+
<span class="badge bg-danger text-white" x-show="thread.attention?.unread" x-text="thread.attention.unread"></span>
|
|
638
|
+
<span class="text-secondary small ms-auto" x-text="timeAgo(thread.lastMessage?.createdAt)"></span>
|
|
639
|
+
</div>
|
|
640
|
+
<div class="text-secondary small text-truncate mt-1 inbox-thread-snippet" x-text="messagePreview(thread.lastMessage)"></div>
|
|
641
|
+
<div class="attention-badges d-flex gap-1 mt-1 flex-wrap" x-show="thread.attention?.score > 0">
|
|
642
|
+
<span class="badge bg-warning-lt" x-show="thread.attention?.needsHumanResponse">needs response</span>
|
|
643
|
+
<span class="badge bg-info-lt" x-show="thread.attention?.agentQuestion">agent asked a question</span>
|
|
644
|
+
</div>
|
|
645
|
+
<div class="d-flex align-items-center gap-1 mt-2">
|
|
646
|
+
<span class="badge bg-secondary-lt" x-show="thread.archived">archived</span>
|
|
647
|
+
<span class="badge bg-primary-lt" x-show="thread.draft">draft</span>
|
|
648
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1 ms-auto" title="Mark unread" @click.stop="markInboxThreadUnread(thread)">
|
|
649
|
+
<i class="ti ti-mail"></i>
|
|
650
|
+
</button>
|
|
651
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" :title="thread.archived ? 'Unarchive' : 'Archive'" @click.stop="thread.archived ? unarchiveInboxThread(thread) : archiveInboxThread(thread)">
|
|
652
|
+
<i class="ti" :class="thread.archived ? 'ti-archive-off' : 'ti-archive'"></i>
|
|
653
|
+
</button>
|
|
654
|
+
<button class="btn btn-sm btn-ghost-danger py-0 px-1" title="Delete thread" @click.stop="confirmDeleteInboxThread(thread)">
|
|
655
|
+
<i class="ti ti-trash"></i>
|
|
656
|
+
</button>
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
</template>
|
|
660
|
+
<template x-if="inboxThreads.length === 0">
|
|
661
|
+
<div class="list-group-item text-secondary text-center py-5">
|
|
662
|
+
<i class="ti ti-inbox-off" style="font-size:48px; opacity:0.3"></i>
|
|
663
|
+
<p class="mt-2 mb-0" x-text="inboxSearch ? 'No matching threads' : 'No correspondence'"></p>
|
|
664
|
+
</div>
|
|
665
|
+
</template>
|
|
666
|
+
</div>
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
|
+
|
|
670
|
+
<div class="col-lg-8 h-100">
|
|
671
|
+
<div class="card h-100">
|
|
672
|
+
<template x-if="selectedInboxThreadData">
|
|
673
|
+
<div class="card-header d-flex align-items-center gap-2">
|
|
674
|
+
<h3 class="card-title mb-0 text-truncate" x-text="conversationTitle(selectedInboxThreadData)"></h3>
|
|
675
|
+
<div class="attention-badges d-flex gap-1 flex-wrap">
|
|
676
|
+
<span class="badge bg-danger-lt" x-show="selectedInboxThreadData.attention?.unread" x-text="selectedInboxThreadData.attention.unread + ' unread'"></span>
|
|
677
|
+
<span class="badge bg-warning-lt" x-show="selectedInboxThreadData.attention?.needsHumanResponse">needs response</span>
|
|
678
|
+
<span class="badge bg-info-lt" x-show="selectedInboxThreadData.attention?.agentQuestion">agent asked a question</span>
|
|
679
|
+
<span class="badge bg-secondary-lt" x-show="selectedInboxThreadData.archived">archived</span>
|
|
680
|
+
</div>
|
|
681
|
+
<button class="btn btn-sm btn-ghost-secondary ms-auto" title="Mark unread" @click="markInboxThreadUnread(selectedInboxThreadData)">
|
|
682
|
+
<i class="ti ti-mail"></i>
|
|
683
|
+
</button>
|
|
684
|
+
<button class="btn btn-sm btn-ghost-secondary" :title="selectedInboxThreadData.archived ? 'Unarchive' : 'Archive'" @click="selectedInboxThreadData.archived ? unarchiveInboxThread(selectedInboxThreadData) : archiveInboxThread(selectedInboxThreadData)">
|
|
685
|
+
<i class="ti" :class="selectedInboxThreadData.archived ? 'ti-archive-off' : 'ti-archive'"></i>
|
|
686
|
+
</button>
|
|
687
|
+
<button class="btn btn-sm btn-ghost-danger" title="Delete thread" @click="confirmDeleteInboxThread(selectedInboxThreadData)">
|
|
688
|
+
<i class="ti ti-trash"></i>
|
|
689
|
+
</button>
|
|
690
|
+
</div>
|
|
691
|
+
</template>
|
|
692
|
+
<div class="card-body p-0 overflow-auto">
|
|
693
|
+
<template x-if="!selectedInboxThreadData">
|
|
694
|
+
<div class="p-4 text-center text-secondary">
|
|
695
|
+
<i class="ti ti-inbox" style="font-size:48px; opacity:0.3"></i>
|
|
696
|
+
<p class="mt-2">Select a conversation</p>
|
|
697
|
+
</div>
|
|
698
|
+
</template>
|
|
699
|
+
<template x-for="m in selectedInboxMessages" :key="m.id">
|
|
700
|
+
<div class="msg-card p-3 border-bottom" :class="{ claimed: m.claimedBy }">
|
|
701
|
+
<div class="d-flex align-items-center gap-2 mb-1">
|
|
702
|
+
<span class="fw-bold small" x-text="displayTarget(m.from)"></span>
|
|
703
|
+
<i class="ti ti-arrow-right text-secondary" style="font-size:12px"></i>
|
|
704
|
+
<span class="small" x-text="displayTarget(m.to)"></span>
|
|
705
|
+
<span class="text-secondary small ms-auto" x-text="'#' + m.id + ' · ' + fmtTime(m.createdAt)"></span>
|
|
706
|
+
</div>
|
|
707
|
+
<template x-if="m.subject">
|
|
708
|
+
<div class="fw-bold small mb-1" x-text="m.subject"></div>
|
|
709
|
+
</template>
|
|
710
|
+
<div class="msg-body" x-text="m.body"></div>
|
|
711
|
+
<div class="d-flex align-items-center gap-2 mt-2 flex-wrap">
|
|
712
|
+
<button class="btn btn-sm btn-ghost-primary py-0 px-1" @click="startReply(m)">
|
|
713
|
+
<i class="ti ti-corner-up-left" style="font-size:14px"></i> Reply
|
|
714
|
+
</button>
|
|
715
|
+
<template x-if="m.channel">
|
|
716
|
+
<span class="badge bg-warning-lt" x-text="'#' + m.channel"></span>
|
|
717
|
+
</template>
|
|
718
|
+
<template x-if="m.claimedBy">
|
|
719
|
+
<span class="badge bg-success-lt" x-text="'claimed by ' + displayTarget(m.claimedBy)"></span>
|
|
720
|
+
</template>
|
|
721
|
+
<template x-if="m.replyTo">
|
|
722
|
+
<span class="text-secondary small ms-auto" x-text="'reply to #' + m.replyTo"></span>
|
|
723
|
+
</template>
|
|
724
|
+
</div>
|
|
725
|
+
</div>
|
|
726
|
+
</template>
|
|
727
|
+
</div>
|
|
728
|
+
<template x-if="selectedInboxThreadData">
|
|
729
|
+
<div class="card-footer">
|
|
730
|
+
<div class="d-flex gap-2 align-items-start">
|
|
731
|
+
<textarea
|
|
732
|
+
class="form-control"
|
|
733
|
+
rows="3"
|
|
734
|
+
placeholder="Reply"
|
|
735
|
+
:value="replyDraftForThread(selectedInboxThreadData)"
|
|
736
|
+
@input="setReplyDraft(selectedInboxThreadData, $event.target.value)"
|
|
737
|
+
@keydown.ctrl.enter.prevent="sendInboxReply(selectedInboxThreadData)"
|
|
738
|
+
@keydown.meta.enter.prevent="sendInboxReply(selectedInboxThreadData)"
|
|
739
|
+
></textarea>
|
|
740
|
+
<div class="d-flex flex-column gap-2">
|
|
741
|
+
<button class="btn btn-primary" @click="sendInboxReply(selectedInboxThreadData)" :disabled="!replyDraftForThread(selectedInboxThreadData)">
|
|
742
|
+
<i class="ti ti-corner-up-left"></i>
|
|
743
|
+
</button>
|
|
744
|
+
<button class="btn btn-ghost-secondary" @click="clearReplyDraft(selectedInboxThreadData)" :disabled="!replyDraftForThread(selectedInboxThreadData)">
|
|
745
|
+
<i class="ti ti-eraser"></i>
|
|
746
|
+
</button>
|
|
747
|
+
</div>
|
|
748
|
+
</div>
|
|
749
|
+
</div>
|
|
750
|
+
</template>
|
|
751
|
+
</div>
|
|
752
|
+
</div>
|
|
753
|
+
</div>
|
|
754
|
+
</div>
|
|
755
|
+
|
|
756
|
+
<!-- ==================== PAIRS ==================== -->
|
|
757
|
+
<div x-show="view === 'pairs'" x-cloak class="fade-in">
|
|
758
|
+
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
759
|
+
<h2 class="page-title mb-0">Pairs</h2>
|
|
760
|
+
<span class="badge bg-warning-lt" x-show="attentionSummary.pendingPairInvites > 0" x-text="attentionSummary.pendingPairInvites + ' invites pending'"></span>
|
|
761
|
+
<div class="ms-auto d-flex gap-2 align-items-center flex-wrap">
|
|
762
|
+
<select class="form-select form-select-sm" style="width:auto; min-width: 150px" x-model="pairStatusFilter" @change="fetchPairs()">
|
|
763
|
+
<option value="open">Status: Open</option>
|
|
764
|
+
<option value="">Status: All</option>
|
|
765
|
+
<option value="pending">Status: Pending</option>
|
|
766
|
+
<option value="active">Status: Active</option>
|
|
767
|
+
<option value="ended">Status: Ended</option>
|
|
768
|
+
<option value="rejected">Status: Rejected</option>
|
|
769
|
+
<option value="expired">Status: Expired</option>
|
|
770
|
+
</select>
|
|
771
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="fetchPairs()">
|
|
772
|
+
<i class="ti ti-refresh"></i>
|
|
773
|
+
</button>
|
|
774
|
+
<button class="btn btn-sm btn-primary" @click="openPairInvite()">
|
|
775
|
+
<i class="ti ti-link-plus me-1"></i>Pair agents
|
|
776
|
+
</button>
|
|
777
|
+
</div>
|
|
778
|
+
</div>
|
|
779
|
+
|
|
780
|
+
<div class="row g-3">
|
|
781
|
+
<template x-for="pair in pairs" :key="pair.id">
|
|
782
|
+
<div class="col-lg-6">
|
|
783
|
+
<div class="card pair-card" :class="pair.status">
|
|
784
|
+
<div class="card-body">
|
|
785
|
+
<div class="d-flex align-items-start gap-3">
|
|
786
|
+
<span class="badge text-white mt-1" :class="pairStatusClass(pair)" x-text="pair.status"></span>
|
|
787
|
+
<div class="flex-grow-1 min-width-0">
|
|
788
|
+
<template x-if="pair.status === 'pending'">
|
|
789
|
+
<span class="badge bg-warning-lt mb-2">
|
|
790
|
+
<i class="ti ti-bell me-1"></i>pair invite pending
|
|
791
|
+
</span>
|
|
792
|
+
</template>
|
|
793
|
+
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
794
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="openAgentDetail(agentsById[pair.requesterId])">
|
|
795
|
+
<i class="ti ti-arrow-up-right me-1"></i><span x-text="displayTarget(pair.requesterId)"></span>
|
|
796
|
+
</button>
|
|
797
|
+
<i class="ti ti-arrows-exchange text-secondary"></i>
|
|
798
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="openAgentDetail(agentsById[pair.targetId])">
|
|
799
|
+
<span x-text="displayTarget(pair.targetId)"></span>
|
|
800
|
+
</button>
|
|
801
|
+
</div>
|
|
802
|
+
<div class="text-secondary small mt-1 text-truncate">
|
|
803
|
+
<span x-text="pair.id"></span>
|
|
804
|
+
<span class="mx-1">·</span>
|
|
805
|
+
<span x-text="'Updated ' + timeAgo(pair.updatedAt || pair.createdAt)"></span>
|
|
806
|
+
</div>
|
|
807
|
+
<template x-if="pair.objective">
|
|
808
|
+
<div class="msg-body mt-2" x-text="pair.objective"></div>
|
|
809
|
+
</template>
|
|
810
|
+
<div class="d-flex align-items-center gap-2 mt-3 flex-wrap">
|
|
811
|
+
<template x-if="pair.status === 'pending'">
|
|
812
|
+
<button class="btn btn-sm btn-success" @click="doAcceptPair(pair)">
|
|
813
|
+
<i class="ti ti-check me-1"></i>Accept
|
|
814
|
+
</button>
|
|
815
|
+
</template>
|
|
816
|
+
<template x-if="pair.status === 'pending'">
|
|
817
|
+
<button class="btn btn-sm btn-ghost-danger" @click="doRejectPair(pair)">
|
|
818
|
+
<i class="ti ti-x me-1"></i>Reject
|
|
819
|
+
</button>
|
|
820
|
+
</template>
|
|
821
|
+
<template x-if="pair.status === 'active'">
|
|
822
|
+
<button class="btn btn-sm btn-primary" @click="openPairMessage(pair)">
|
|
823
|
+
<i class="ti ti-send me-1"></i>Message
|
|
824
|
+
</button>
|
|
825
|
+
</template>
|
|
826
|
+
<template x-if="pair.status === 'active'">
|
|
827
|
+
<button class="btn btn-sm btn-ghost-danger" @click="doHangupPair(pair)">
|
|
828
|
+
<i class="ti ti-phone-off me-1"></i>Hang up
|
|
829
|
+
</button>
|
|
830
|
+
</template>
|
|
831
|
+
<span class="text-secondary small ms-auto" x-text="'Created ' + fmtTime(pair.createdAt)"></span>
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
</div>
|
|
835
|
+
</div>
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
838
|
+
</template>
|
|
839
|
+
</div>
|
|
840
|
+
|
|
841
|
+
<template x-if="pairs.length === 0">
|
|
842
|
+
<div class="card">
|
|
843
|
+
<div class="card-body text-center text-secondary py-5">
|
|
844
|
+
<i class="ti ti-link-off" style="font-size:48px; opacity:0.3"></i>
|
|
845
|
+
<p class="mt-2">No pair sessions</p>
|
|
846
|
+
</div>
|
|
847
|
+
</div>
|
|
848
|
+
</template>
|
|
849
|
+
</div>
|
|
850
|
+
|
|
442
851
|
<!-- ==================== MESSAGES ==================== -->
|
|
443
852
|
<div x-show="view === 'messages'" x-cloak class="fade-in">
|
|
444
853
|
|
|
@@ -539,6 +948,7 @@
|
|
|
539
948
|
<div x-show="view === 'tasks'" x-cloak class="fade-in">
|
|
540
949
|
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
541
950
|
<h2 class="page-title mb-0">Tasks</h2>
|
|
951
|
+
<span class="badge bg-warning-lt" x-show="attentionSummary.claimableTasks > 0" x-text="attentionSummary.claimableTasks + ' claimable waiting'"></span>
|
|
542
952
|
<div class="ms-auto d-flex gap-2 align-items-center flex-wrap">
|
|
543
953
|
<select class="form-select form-select-sm" style="width:auto; min-width: 150px" x-model="taskStatusFilter" @change="fetchTasks()">
|
|
544
954
|
<option value="">Status: Active</option>
|
|
@@ -568,6 +978,7 @@
|
|
|
568
978
|
<div class="d-flex align-items-center gap-2">
|
|
569
979
|
<span class="fw-bold text-truncate" x-text="task.title"></span>
|
|
570
980
|
<span class="badge bg-secondary-lt" x-text="task.status"></span>
|
|
981
|
+
<span class="badge bg-warning-lt" x-show="['open','blocked'].includes(task.status) && !task.claimedBy">claimable waiting</span>
|
|
571
982
|
</div>
|
|
572
983
|
<div class="text-secondary small mt-1">
|
|
573
984
|
<span x-text="'#' + task.id"></span>
|
|
@@ -692,6 +1103,173 @@
|
|
|
692
1103
|
</div>
|
|
693
1104
|
</main>
|
|
694
1105
|
|
|
1106
|
+
<!-- ==================== AGENT DETAIL DRAWER ==================== -->
|
|
1107
|
+
<template x-if="selectedAgentDetail">
|
|
1108
|
+
<div>
|
|
1109
|
+
<div class="agent-drawer-backdrop" x-show="agentDetailOpen" x-cloak @click="closeAgentDetail()"></div>
|
|
1110
|
+
<aside class="agent-drawer" x-show="agentDetailOpen" x-cloak>
|
|
1111
|
+
<div class="p-3 border-bottom d-flex align-items-start gap-2">
|
|
1112
|
+
<span class="status-dot mt-2" :class="[selectedAgentDetail.status, selectedAgentDetail.status !== 'offline' && !selectedAgentDetail.ready ? 'not-ready' : '']"></span>
|
|
1113
|
+
<span class="agent-type-icon mt-1" :class="agentType(selectedAgentDetail)" :title="agentTypeTitle(selectedAgentDetail)">
|
|
1114
|
+
<i class="ti" :class="agentTypeIcon(selectedAgentDetail)"></i>
|
|
1115
|
+
</span>
|
|
1116
|
+
<div class="flex-grow-1 min-width-0">
|
|
1117
|
+
<div class="d-flex align-items-center gap-2">
|
|
1118
|
+
<template x-if="selectedAgentDetail.label">
|
|
1119
|
+
<span class="agent-label text-truncate" x-text="selectedAgentDetail.label"></span>
|
|
1120
|
+
</template>
|
|
1121
|
+
<span class="fw-bold text-truncate" x-text="selectedAgentDetail.name || selectedAgentDetail.id.slice(-12)"></span>
|
|
1122
|
+
</div>
|
|
1123
|
+
<div class="text-secondary small text-truncate" x-text="selectedAgentDetail.id"></div>
|
|
1124
|
+
</div>
|
|
1125
|
+
<button class="btn btn-sm btn-ghost-secondary p-1" @click="closeAgentDetail()" title="Close">
|
|
1126
|
+
<i class="ti ti-x"></i>
|
|
1127
|
+
</button>
|
|
1128
|
+
</div>
|
|
1129
|
+
|
|
1130
|
+
<div class="p-3 border-bottom d-flex gap-2 flex-wrap">
|
|
1131
|
+
<button class="btn btn-sm btn-primary" @click="openComposeToAgent(selectedAgentDetail)">
|
|
1132
|
+
<i class="ti ti-send me-1"></i>Message
|
|
1133
|
+
</button>
|
|
1134
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="selectedAgent = selectedAgentDetail.id; switchView('messages'); closeAgentDetail()">
|
|
1135
|
+
<i class="ti ti-filter me-1"></i>Messages
|
|
1136
|
+
</button>
|
|
1137
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="switchView('pairs'); closeAgentDetail()">
|
|
1138
|
+
<i class="ti ti-link me-1"></i>Pairs
|
|
1139
|
+
</button>
|
|
1140
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="openPairInvite(selectedAgentDetail.id); closeAgentDetail()">
|
|
1141
|
+
<i class="ti ti-link-plus me-1"></i>Pair
|
|
1142
|
+
</button>
|
|
1143
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="openRename(selectedAgentDetail)">
|
|
1144
|
+
<i class="ti ti-pencil me-1"></i>Rename
|
|
1145
|
+
</button>
|
|
1146
|
+
</div>
|
|
1147
|
+
|
|
1148
|
+
<div class="p-3 border-bottom">
|
|
1149
|
+
<h3 class="card-title mb-3">Status</h3>
|
|
1150
|
+
<template x-if="agentAttention(selectedAgentDetail).total > 0">
|
|
1151
|
+
<div class="alert alert-warning py-2 mb-3">
|
|
1152
|
+
<div class="fw-bold small mb-1">Needs attention</div>
|
|
1153
|
+
<div class="attention-badges d-flex gap-1 flex-wrap">
|
|
1154
|
+
<span class="badge bg-danger-lt" x-show="agentAttention(selectedAgentDetail).unread" x-text="agentAttention(selectedAgentDetail).unread + ' unread'"></span>
|
|
1155
|
+
<span class="badge bg-warning-lt" x-show="agentAttention(selectedAgentDetail).needsHumanResponse">needs human response</span>
|
|
1156
|
+
<span class="badge bg-info-lt" x-show="agentAttention(selectedAgentDetail).agentQuestion">agent asked a question</span>
|
|
1157
|
+
<span class="badge bg-warning-lt" x-show="agentAttention(selectedAgentDetail).pendingPairInvite">pair invite pending</span>
|
|
1158
|
+
<span class="badge bg-orange-lt" x-show="agentAttention(selectedAgentDetail).claimableTasks" x-text="agentAttention(selectedAgentDetail).claimableTasks + ' claimable waiting'"></span>
|
|
1159
|
+
</div>
|
|
1160
|
+
</div>
|
|
1161
|
+
</template>
|
|
1162
|
+
<div class="detail-row mb-2">
|
|
1163
|
+
<div class="text-secondary small">State</div>
|
|
1164
|
+
<div>
|
|
1165
|
+
<span class="badge bg-secondary-lt" x-text="agentStatusTitle(selectedAgentDetail)"></span>
|
|
1166
|
+
<template x-if="selectedAgentDetail.ready">
|
|
1167
|
+
<span class="badge bg-success-lt ms-1">ready</span>
|
|
1168
|
+
</template>
|
|
1169
|
+
</div>
|
|
1170
|
+
</div>
|
|
1171
|
+
<div class="detail-row mb-2">
|
|
1172
|
+
<div class="text-secondary small">Last seen</div>
|
|
1173
|
+
<div class="small" x-text="timeAgo(selectedAgentDetail.lastSeen)"></div>
|
|
1174
|
+
</div>
|
|
1175
|
+
<div class="detail-row mb-2">
|
|
1176
|
+
<div class="text-secondary small">Created</div>
|
|
1177
|
+
<div class="small" x-text="fmtTime(selectedAgentDetail.createdAt)"></div>
|
|
1178
|
+
</div>
|
|
1179
|
+
<template x-if="selectedAgentDetail.machine || selectedAgentDetail.rig">
|
|
1180
|
+
<div class="detail-row mb-2">
|
|
1181
|
+
<div class="text-secondary small">Host</div>
|
|
1182
|
+
<div class="d-flex gap-1 flex-wrap">
|
|
1183
|
+
<template x-if="selectedAgentDetail.machine">
|
|
1184
|
+
<span class="badge bg-secondary-lt" x-text="selectedAgentDetail.machine"></span>
|
|
1185
|
+
</template>
|
|
1186
|
+
<template x-if="selectedAgentDetail.rig">
|
|
1187
|
+
<span class="badge bg-primary-lt" x-text="selectedAgentDetail.rig"></span>
|
|
1188
|
+
</template>
|
|
1189
|
+
</div>
|
|
1190
|
+
</div>
|
|
1191
|
+
</template>
|
|
1192
|
+
</div>
|
|
1193
|
+
|
|
1194
|
+
<template x-if="agentPair(selectedAgentDetail)">
|
|
1195
|
+
<div class="p-3 border-bottom">
|
|
1196
|
+
<h3 class="card-title mb-3">Pair</h3>
|
|
1197
|
+
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
1198
|
+
<span class="badge pair-badge" :class="pairBadgeClass(agentPair(selectedAgentDetail))" :title="pairTitle(agentPair(selectedAgentDetail), selectedAgentDetail.id)">
|
|
1199
|
+
<i class="ti ti-link me-1"></i><span x-text="pairBadgeLabel(agentPair(selectedAgentDetail), selectedAgentDetail.id)"></span>
|
|
1200
|
+
</span>
|
|
1201
|
+
<span class="text-secondary small" x-text="agentPair(selectedAgentDetail).id"></span>
|
|
1202
|
+
</div>
|
|
1203
|
+
<template x-if="agentPair(selectedAgentDetail).objective">
|
|
1204
|
+
<div class="msg-body mt-2" x-text="agentPair(selectedAgentDetail).objective"></div>
|
|
1205
|
+
</template>
|
|
1206
|
+
<div class="d-flex gap-2 mt-3 flex-wrap">
|
|
1207
|
+
<template x-if="agentPair(selectedAgentDetail).status === 'active'">
|
|
1208
|
+
<button class="btn btn-sm btn-primary" @click="openPairMessage(agentPair(selectedAgentDetail), selectedAgentDetail.id)">
|
|
1209
|
+
<i class="ti ti-send me-1"></i>Pair message
|
|
1210
|
+
</button>
|
|
1211
|
+
</template>
|
|
1212
|
+
<template x-if="agentPair(selectedAgentDetail).status === 'pending' && agentPair(selectedAgentDetail).targetId === selectedAgentDetail.id">
|
|
1213
|
+
<button class="btn btn-sm btn-success" @click="doAcceptPair(agentPair(selectedAgentDetail))">
|
|
1214
|
+
<i class="ti ti-check me-1"></i>Accept
|
|
1215
|
+
</button>
|
|
1216
|
+
</template>
|
|
1217
|
+
<template x-if="agentPair(selectedAgentDetail).status === 'pending' && agentPair(selectedAgentDetail).targetId === selectedAgentDetail.id">
|
|
1218
|
+
<button class="btn btn-sm btn-ghost-danger" @click="doRejectPair(agentPair(selectedAgentDetail))">
|
|
1219
|
+
<i class="ti ti-x me-1"></i>Reject
|
|
1220
|
+
</button>
|
|
1221
|
+
</template>
|
|
1222
|
+
<template x-if="agentPair(selectedAgentDetail).status === 'active'">
|
|
1223
|
+
<button class="btn btn-sm btn-ghost-danger" @click="doHangupPair(agentPair(selectedAgentDetail), selectedAgentDetail.id)">
|
|
1224
|
+
<i class="ti ti-phone-off me-1"></i>Hang up
|
|
1225
|
+
</button>
|
|
1226
|
+
</template>
|
|
1227
|
+
</div>
|
|
1228
|
+
</div>
|
|
1229
|
+
</template>
|
|
1230
|
+
|
|
1231
|
+
<div class="p-3 border-bottom">
|
|
1232
|
+
<h3 class="card-title mb-3">Tags</h3>
|
|
1233
|
+
<div class="d-flex gap-1 flex-wrap">
|
|
1234
|
+
<template x-for="tag in (selectedAgentDetail.tags || [])" :key="tag">
|
|
1235
|
+
<span class="badge bg-cyan-lt" x-text="tag"></span>
|
|
1236
|
+
</template>
|
|
1237
|
+
<template x-if="!(selectedAgentDetail.tags || []).length">
|
|
1238
|
+
<span class="text-secondary small">No tags</span>
|
|
1239
|
+
</template>
|
|
1240
|
+
</div>
|
|
1241
|
+
<template x-if="selectedAgentDetail.capabilities && selectedAgentDetail.capabilities.length">
|
|
1242
|
+
<div class="d-flex gap-1 flex-wrap mt-2">
|
|
1243
|
+
<template x-for="cap in selectedAgentDetail.capabilities" :key="cap">
|
|
1244
|
+
<span class="badge bg-purple-lt" x-text="cap"></span>
|
|
1245
|
+
</template>
|
|
1246
|
+
</div>
|
|
1247
|
+
</template>
|
|
1248
|
+
</div>
|
|
1249
|
+
|
|
1250
|
+
<div class="p-3">
|
|
1251
|
+
<h3 class="card-title mb-3">Recent Messages</h3>
|
|
1252
|
+
<div class="list-group list-group-flush">
|
|
1253
|
+
<template x-for="m in agentDetailMessages" :key="m.id">
|
|
1254
|
+
<div class="list-group-item px-0">
|
|
1255
|
+
<div class="d-flex align-items-center gap-2 mb-1">
|
|
1256
|
+
<span class="fw-bold small" x-text="displayTarget(m.from)"></span>
|
|
1257
|
+
<i class="ti ti-arrow-right text-secondary" style="font-size:12px"></i>
|
|
1258
|
+
<span class="small" x-text="displayTarget(m.to)"></span>
|
|
1259
|
+
<span class="text-secondary small ms-auto" x-text="'#' + m.id"></span>
|
|
1260
|
+
</div>
|
|
1261
|
+
<div class="text-secondary small text-truncate" x-text="messagePreview(m)"></div>
|
|
1262
|
+
</div>
|
|
1263
|
+
</template>
|
|
1264
|
+
<template x-if="agentDetailMessages.length === 0">
|
|
1265
|
+
<div class="text-secondary small">No recent messages loaded</div>
|
|
1266
|
+
</template>
|
|
1267
|
+
</div>
|
|
1268
|
+
</div>
|
|
1269
|
+
</aside>
|
|
1270
|
+
</div>
|
|
1271
|
+
</template>
|
|
1272
|
+
|
|
695
1273
|
<!-- ==================== COMPOSE MODAL ==================== -->
|
|
696
1274
|
<div class="modal modal-blur" :class="{ show: composeOpen }" :style="composeOpen ? 'display:block' : 'display:none'" tabindex="-1" @click.self="composeOpen = false">
|
|
697
1275
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
@@ -776,6 +1354,101 @@
|
|
|
776
1354
|
</div>
|
|
777
1355
|
<div class="modal-backdrop fade show" x-show="composeOpen" x-cloak></div>
|
|
778
1356
|
|
|
1357
|
+
<!-- ==================== PAIR INVITE MODAL ==================== -->
|
|
1358
|
+
<div class="modal modal-blur" :class="{ show: pairInviteOpen }" :style="pairInviteOpen ? 'display:block' : 'display:none'" tabindex="-1" @click.self="closePairInvite()">
|
|
1359
|
+
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
1360
|
+
<div class="modal-content">
|
|
1361
|
+
<div class="modal-header">
|
|
1362
|
+
<h5 class="modal-title">Pair Agents</h5>
|
|
1363
|
+
<button class="btn-close" @click="closePairInvite()"></button>
|
|
1364
|
+
</div>
|
|
1365
|
+
<div class="modal-body">
|
|
1366
|
+
<div class="row g-3">
|
|
1367
|
+
<div class="col-md-6">
|
|
1368
|
+
<label class="form-label">Requester</label>
|
|
1369
|
+
<select class="form-select" x-model="pairInvite.requesterId">
|
|
1370
|
+
<option value="">Select requester…</option>
|
|
1371
|
+
<template x-for="a in composeAgents" :key="a.id">
|
|
1372
|
+
<option :value="a.id" x-text="displayName(a) + ' [' + a.id.slice(-6) + ']'"></option>
|
|
1373
|
+
</template>
|
|
1374
|
+
</select>
|
|
1375
|
+
</div>
|
|
1376
|
+
<div class="col-md-6">
|
|
1377
|
+
<label class="form-label">Target</label>
|
|
1378
|
+
<select class="form-select" x-model="pairInvite.targetId">
|
|
1379
|
+
<option value="">Select target…</option>
|
|
1380
|
+
<template x-for="a in composeAgents" :key="a.id">
|
|
1381
|
+
<option :value="a.id" x-text="displayName(a) + ' [' + a.id.slice(-6) + ']'"></option>
|
|
1382
|
+
</template>
|
|
1383
|
+
</select>
|
|
1384
|
+
</div>
|
|
1385
|
+
<div class="col-12">
|
|
1386
|
+
<label class="form-label">Objective <span class="text-secondary">(optional)</span></label>
|
|
1387
|
+
<textarea class="form-control" rows="4" x-model="pairInvite.objective" x-ref="pairInviteObjective"></textarea>
|
|
1388
|
+
</div>
|
|
1389
|
+
</div>
|
|
1390
|
+
</div>
|
|
1391
|
+
<div class="modal-footer">
|
|
1392
|
+
<button class="btn btn-ghost-secondary" @click="closePairInvite()">Cancel</button>
|
|
1393
|
+
<button class="btn btn-primary" @click="doCreatePair()">
|
|
1394
|
+
<i class="ti ti-link-plus me-1"></i>Create invite
|
|
1395
|
+
</button>
|
|
1396
|
+
</div>
|
|
1397
|
+
</div>
|
|
1398
|
+
</div>
|
|
1399
|
+
</div>
|
|
1400
|
+
<div class="modal-backdrop fade show" x-show="pairInviteOpen" x-cloak></div>
|
|
1401
|
+
|
|
1402
|
+
<!-- ==================== PAIR MESSAGE MODAL ==================== -->
|
|
1403
|
+
<div class="modal modal-blur" :class="{ show: pairMessageOpen }" :style="pairMessageOpen ? 'display:block' : 'display:none'" tabindex="-1" @click.self="closePairMessage()">
|
|
1404
|
+
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
1405
|
+
<div class="modal-content">
|
|
1406
|
+
<div class="modal-header">
|
|
1407
|
+
<h5 class="modal-title">Pair Message</h5>
|
|
1408
|
+
<button class="btn-close" @click="closePairMessage()"></button>
|
|
1409
|
+
</div>
|
|
1410
|
+
<div class="modal-body">
|
|
1411
|
+
<template x-if="pairMessagePair">
|
|
1412
|
+
<div class="alert alert-info py-2">
|
|
1413
|
+
<i class="ti ti-link me-1"></i>
|
|
1414
|
+
<span x-text="displayTarget(pairMessagePair.requesterId)"></span>
|
|
1415
|
+
<span class="mx-1">↔</span>
|
|
1416
|
+
<span x-text="displayTarget(pairMessagePair.targetId)"></span>
|
|
1417
|
+
</div>
|
|
1418
|
+
</template>
|
|
1419
|
+
<div class="row g-3">
|
|
1420
|
+
<div class="col-md-6">
|
|
1421
|
+
<label class="form-label">From</label>
|
|
1422
|
+
<select class="form-select" x-model="pairMessage.from">
|
|
1423
|
+
<template x-if="pairMessagePair">
|
|
1424
|
+
<option :value="pairMessagePair.requesterId" x-text="displayTarget(pairMessagePair.requesterId)"></option>
|
|
1425
|
+
</template>
|
|
1426
|
+
<template x-if="pairMessagePair">
|
|
1427
|
+
<option :value="pairMessagePair.targetId" x-text="displayTarget(pairMessagePair.targetId)"></option>
|
|
1428
|
+
</template>
|
|
1429
|
+
</select>
|
|
1430
|
+
</div>
|
|
1431
|
+
<div class="col-md-6">
|
|
1432
|
+
<label class="form-label">Subject <span class="text-secondary">(optional)</span></label>
|
|
1433
|
+
<input type="text" class="form-control" x-model="pairMessage.subject">
|
|
1434
|
+
</div>
|
|
1435
|
+
<div class="col-12">
|
|
1436
|
+
<label class="form-label">Message</label>
|
|
1437
|
+
<textarea class="form-control" rows="5" x-model="pairMessage.body" x-ref="pairMessageBody"></textarea>
|
|
1438
|
+
</div>
|
|
1439
|
+
</div>
|
|
1440
|
+
</div>
|
|
1441
|
+
<div class="modal-footer">
|
|
1442
|
+
<button class="btn btn-ghost-secondary" @click="closePairMessage()">Cancel</button>
|
|
1443
|
+
<button class="btn btn-primary" @click="doSendPairMessage()">
|
|
1444
|
+
<i class="ti ti-send me-1"></i>Send
|
|
1445
|
+
</button>
|
|
1446
|
+
</div>
|
|
1447
|
+
</div>
|
|
1448
|
+
</div>
|
|
1449
|
+
</div>
|
|
1450
|
+
<div class="modal-backdrop fade show" x-show="pairMessageOpen" x-cloak></div>
|
|
1451
|
+
|
|
779
1452
|
<!-- ==================== THREAD MODAL ==================== -->
|
|
780
1453
|
<div class="modal modal-blur" :class="{ show: threadOpen }" :style="threadOpen ? 'display:block' : 'display:none'" tabindex="-1" @click.self="threadOpen = false">
|
|
781
1454
|
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
|