Skip to content

Commit f4d6267

Browse files
committed
feat: add ChatSurfaceSettings component for managing external chat connections
1 parent 032e318 commit f4d6267

4 files changed

Lines changed: 323 additions & 34 deletions

File tree

custom/ChatSurfaceSettings.vue

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<template>
2+
<div class="flex flex-col justify-center mr-6 md:mr-12">
3+
<h2 class="flex items-start justify-start leading-none text-gray-800 dark:text-gray-50 text-3xl font-semibold">
4+
{{ $t('Chat Surfaces') }}
5+
</h2>
6+
<p class="text-sm mt-3">
7+
{{ $t('Connect external chat accounts to your AdminForth user') }}
8+
</p>
9+
10+
<div class="mt-6 flex flex-wrap gap-4">
11+
<div
12+
v-for="surface in surfaces"
13+
:key="surface.name"
14+
class="flex flex-col w-full lg:w-72 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4 shadow-sm"
15+
>
16+
<div class="flex items-center justify-between gap-3 mb-4">
17+
<div class="min-w-0">
18+
<p class="font-semibold text-gray-900 dark:text-white truncate">
19+
{{ formatSurfaceName(surface.name) }}
20+
</p>
21+
<p class="text-xs text-gray-500 dark:text-gray-400">
22+
{{ surface.externalUserId || $t('Not connected') }}
23+
</p>
24+
</div>
25+
<span
26+
class="shrink-0 inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium"
27+
:class="surface.externalUserId
28+
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
29+
: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300'"
30+
>
31+
{{ surface.externalUserId ? $t('Active') : $t('Inactive') }}
32+
</span>
33+
</div>
34+
35+
<div class="grid gap-2 mt-auto" :class="surface.externalUserId ? 'grid-cols-2' : 'grid-cols-1'">
36+
<Button
37+
class="w-full"
38+
:disabled="isSurfaceBusy(surface.name)"
39+
:loader="connectingSurfaceName === surface.name"
40+
@click="connectSurface(surface.name)"
41+
>
42+
{{ surface.externalUserId ? $t('Reconnect') : $t('Connect') }}
43+
</Button>
44+
<Button
45+
v-if="surface.externalUserId"
46+
class="w-full"
47+
:disabled="isSurfaceBusy(surface.name)"
48+
:loader="disconnectingSurfaceName === surface.name"
49+
@click="disconnectSurface(surface.name)"
50+
>
51+
{{ $t('Disconnect') }}
52+
</Button>
53+
</div>
54+
</div>
55+
</div>
56+
</div>
57+
</template>
58+
59+
<script setup lang="ts">
60+
import { onMounted, ref } from 'vue';
61+
import { Button } from '@/afcl';
62+
import { callAdminForthApi } from '@/utils';
63+
64+
type ChatSurface = {
65+
name: string;
66+
externalUserId: string | null;
67+
};
68+
69+
const surfaces = ref<ChatSurface[]>([]);
70+
const connectingSurfaceName = ref<string | null>(null);
71+
const disconnectingSurfaceName = ref<string | null>(null);
72+
73+
onMounted(loadSurfaces);
74+
75+
async function loadSurfaces() {
76+
const response = await callAdminForthApi({
77+
method: 'POST',
78+
path: '/agent/surfaces/connectable',
79+
body: {},
80+
});
81+
82+
surfaces.value = response.surfaces;
83+
}
84+
85+
async function connectSurface(surfaceName: string) {
86+
connectingSurfaceName.value = surfaceName;
87+
88+
try {
89+
const response = await callAdminForthApi({
90+
method: 'POST',
91+
path: `/agent/surface/${surfaceName}/connect-action`,
92+
body: {},
93+
});
94+
95+
if (response.action.type === 'url') {
96+
window.open(response.action.url, '_blank', 'noopener,noreferrer');
97+
}
98+
} finally {
99+
connectingSurfaceName.value = null;
100+
}
101+
}
102+
103+
async function disconnectSurface(surfaceName: string) {
104+
disconnectingSurfaceName.value = surfaceName;
105+
106+
try {
107+
await callAdminForthApi({
108+
method: 'POST',
109+
path: `/agent/surface/${surfaceName}/disconnect`,
110+
body: {},
111+
});
112+
await loadSurfaces();
113+
} finally {
114+
disconnectingSurfaceName.value = null;
115+
}
116+
}
117+
118+
function isSurfaceBusy(surfaceName: string) {
119+
return connectingSurfaceName.value === surfaceName || disconnectingSurfaceName.value === surfaceName;
120+
}
121+
122+
function formatSurfaceName(surfaceName: string) {
123+
return surfaceName.charAt(0).toUpperCase() + surfaceName.slice(1);
124+
}
125+
</script>

custom/incremark_code_renderers/incremarkRenderer.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -625,11 +625,11 @@ function sanitizeClassToken(value: string): string {
625625

626626
function escapeHtml(value: string): string {
627627
return value
628-
.replaceAll('&', '&amp;')
629-
.replaceAll('<', '&lt;')
630-
.replaceAll('>', '&gt;')
631-
.replaceAll('"', '&quot;')
632-
.replaceAll("'", '&#39;');
628+
.replace(/&/g, '&amp;')
629+
.replace(/</g, '&lt;')
630+
.replace(/>/g, '&gt;')
631+
.replace(/"/g, '&quot;')
632+
.replace(/'/g, '&#39;');
633633
}
634634

635635
function escapeAttribute(value: string): string {

0 commit comments

Comments
 (0)