contactstudiocstools 1.0.258 → 1.0.260
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/dist/module.json +1 -1
- package/dist/runtime/components/Atom.ChatContactGlass.vue +186 -0
- package/dist/runtime/components/Organism.ChatScheduleGlass.vue +74 -0
- package/dist/runtime/components/Organism.Tabulation.vue +28 -1
- package/dist/runtime/components/types/dto.mjs +8 -2
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- contact -->
|
|
3
|
+
<li class="chat-contact flex items-center !pl-3 max-h-none !rounded-none">
|
|
4
|
+
<!-- avatar -->
|
|
5
|
+
<figure
|
|
6
|
+
:class="[getStatusClass]"
|
|
7
|
+
:notifications="contact.notifications"
|
|
8
|
+
class="w-9 h-9 rounded-full avatar-glass avatar-soft-secondary"
|
|
9
|
+
>
|
|
10
|
+
<img
|
|
11
|
+
v-if="contact.avatar"
|
|
12
|
+
:src="contact.avatar"
|
|
13
|
+
class="rounded-md"
|
|
14
|
+
>
|
|
15
|
+
<i
|
|
16
|
+
v-else
|
|
17
|
+
class="bi-person-fill text-2xl"
|
|
18
|
+
/>
|
|
19
|
+
</figure>
|
|
20
|
+
|
|
21
|
+
<!-- info -->
|
|
22
|
+
<div
|
|
23
|
+
class="flex-1 ml-3 py-2 !pr-3 w-24 chat-contact-border"
|
|
24
|
+
:class="{'border-t-2': index === 0}"
|
|
25
|
+
>
|
|
26
|
+
<p class="flex justify-between items-center overflow-hidden text-ellipsis whitespace-nowrap mb-1">
|
|
27
|
+
<!-- label -->
|
|
28
|
+
<span class="text-xs">
|
|
29
|
+
{{ contact.label }}
|
|
30
|
+
</span>
|
|
31
|
+
|
|
32
|
+
<!-- hours
|
|
33
|
+
<span
|
|
34
|
+
v-if="entryDateExists"
|
|
35
|
+
class="badge badge-outline-secondary ml-2"
|
|
36
|
+
tooltip="Tempo em atendimento"
|
|
37
|
+
>
|
|
38
|
+
{{ new Date(milliseconds).toISOString().slice(11, 19) }}
|
|
39
|
+
</span>
|
|
40
|
+
-->
|
|
41
|
+
|
|
42
|
+
<div class="flex items-center gap-1">
|
|
43
|
+
<!-- date message -->
|
|
44
|
+
<p
|
|
45
|
+
class="badge !p-0 text-secondary"
|
|
46
|
+
tooltip="Horário da última mensagem"
|
|
47
|
+
>
|
|
48
|
+
{{ getDate }}
|
|
49
|
+
</p>
|
|
50
|
+
<!-- expirate -->
|
|
51
|
+
<div
|
|
52
|
+
v-if="expirated"
|
|
53
|
+
tooltip="Envie uma mensagem para o cliente!"
|
|
54
|
+
class="flex justify-center items-center relative"
|
|
55
|
+
>
|
|
56
|
+
<div class="flex justify-center items-center rounded-full bg-error h-4 w-4">
|
|
57
|
+
<i class="bi bi-bell-fill text-white text-[8.4px] ml-[1px]" />
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</p>
|
|
62
|
+
<!-- typing -->
|
|
63
|
+
<p
|
|
64
|
+
v-if="contact.typing"
|
|
65
|
+
class="text-2xs text-success"
|
|
66
|
+
>
|
|
67
|
+
Digitando...
|
|
68
|
+
</p>
|
|
69
|
+
<!-- message -->
|
|
70
|
+
<div
|
|
71
|
+
v-else
|
|
72
|
+
class="markdown text-2xs opacity-50 w-full overflow-hidden text-ellipsis whitespace-nowrap"
|
|
73
|
+
v-html="marked(props.contact.message ?? '')"
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
</li>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<script setup lang="ts">
|
|
80
|
+
import { marked } from "marked";
|
|
81
|
+
import { ref, computed, onMounted } from "vue";
|
|
82
|
+
import { getMinutesBetweenDates, getTimeByDate, IChatContact } from "./types";
|
|
83
|
+
|
|
84
|
+
// props
|
|
85
|
+
interface IProps {
|
|
86
|
+
contact: IChatContact;
|
|
87
|
+
timeout: number;
|
|
88
|
+
index: number;
|
|
89
|
+
}
|
|
90
|
+
const props = defineProps<IProps>();
|
|
91
|
+
|
|
92
|
+
// mounted
|
|
93
|
+
onMounted(() => {
|
|
94
|
+
setInterval(validateExpiration, 1000);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// computed
|
|
98
|
+
const getDate = computed<string>(() => {
|
|
99
|
+
const date = props.contact.date;
|
|
100
|
+
if (!date) return "";
|
|
101
|
+
return getTimeByDate(date);
|
|
102
|
+
});
|
|
103
|
+
const entryDateExists = computed<boolean>(() => !!props.contact.entrydate)
|
|
104
|
+
const getStatusClass = computed<string>(() => {
|
|
105
|
+
if (props.contact.on) return "avatar-status-success";
|
|
106
|
+
return "avatar-status-error";
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// data
|
|
110
|
+
const expirated = ref<boolean>(false);
|
|
111
|
+
const milliseconds = ref<number>(0)
|
|
112
|
+
|
|
113
|
+
// methods
|
|
114
|
+
function validateExpiration() {
|
|
115
|
+
if (!props.contact.date) return;
|
|
116
|
+
expirated.value =
|
|
117
|
+
getMinutesBetweenDates(new Date(), props.contact.date) >= props.timeout;
|
|
118
|
+
}
|
|
119
|
+
async function setTimerContact(): Promise<void> {
|
|
120
|
+
if (!props.contact.entrydate) return
|
|
121
|
+
|
|
122
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
123
|
+
|
|
124
|
+
const now = new Date().getTime()
|
|
125
|
+
const entry = props.contact.entrydate.getTime()
|
|
126
|
+
|
|
127
|
+
milliseconds.value = Math.abs(now - entry)
|
|
128
|
+
|
|
129
|
+
setTimerContact()
|
|
130
|
+
}
|
|
131
|
+
setTimerContact()
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<style scoped>
|
|
135
|
+
.min-w-40px {
|
|
136
|
+
min-width: 40px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.text-2xs {
|
|
140
|
+
font-size: 0.7rem;
|
|
141
|
+
}
|
|
142
|
+
.avatar-glass {
|
|
143
|
+
align-items: center;
|
|
144
|
+
display: flex;
|
|
145
|
+
font-weight: 500;
|
|
146
|
+
justify-content: center;
|
|
147
|
+
position: relative;
|
|
148
|
+
text-transform: uppercase;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.avatar-glass:after,.avatar-glass:not(.avatar-glass[notifications]):after {
|
|
152
|
+
border-radius: 99999px;
|
|
153
|
+
border: 3px solid #ffffff;
|
|
154
|
+
color: #fff;
|
|
155
|
+
font-size: 11px;
|
|
156
|
+
height: 15px;
|
|
157
|
+
width: 15px;
|
|
158
|
+
position: absolute;
|
|
159
|
+
right: 0;
|
|
160
|
+
top: 0;
|
|
161
|
+
transform: translate(40%,-40%)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.avatar-glass:not([notifications=""]):after {
|
|
165
|
+
content: attr(notifications);
|
|
166
|
+
width: 25px;
|
|
167
|
+
height: 25px;
|
|
168
|
+
display: flex;
|
|
169
|
+
justify-content: center;
|
|
170
|
+
align-items: center
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.chat-contact:hover {
|
|
174
|
+
cursor: pointer;
|
|
175
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
176
|
+
}
|
|
177
|
+
.dark .chat-contact:hover {
|
|
178
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.chat-contact-border {
|
|
182
|
+
border-bottom: 2px;
|
|
183
|
+
border-style: solid;
|
|
184
|
+
border-color: rgba(0, 0, 0, 0.2);
|
|
185
|
+
}
|
|
186
|
+
</style>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div class="flex items-center text-xs">
|
|
4
|
+
<span class="w-full px-6 py-5 text-xl">
|
|
5
|
+
Conversas
|
|
6
|
+
</span>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<article class="overflow-auto max-h-full scrollbar pb-24">
|
|
10
|
+
<!-- contacts -->
|
|
11
|
+
<div
|
|
12
|
+
v-for="(contact, i) in contacts"
|
|
13
|
+
v-show="!visibleSchedules"
|
|
14
|
+
:key="i"
|
|
15
|
+
>
|
|
16
|
+
<ul class="list">
|
|
17
|
+
<AtomChatContactGlass
|
|
18
|
+
:timeout="timeout"
|
|
19
|
+
:contact="contact"
|
|
20
|
+
:class="getSelectedClass(contact)"
|
|
21
|
+
:index="i"
|
|
22
|
+
@click="select(contact)"
|
|
23
|
+
/>
|
|
24
|
+
</ul>
|
|
25
|
+
</div>
|
|
26
|
+
</article>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import { ref, computed } from "vue";
|
|
32
|
+
import { IChatContact, IChatContacts, IPortfolioSchedules } from "./types";
|
|
33
|
+
|
|
34
|
+
// props
|
|
35
|
+
interface IProps {
|
|
36
|
+
contacts: IChatContacts;
|
|
37
|
+
schedules: IPortfolioSchedules;
|
|
38
|
+
selected?: IChatContact;
|
|
39
|
+
timeout: number;
|
|
40
|
+
}
|
|
41
|
+
const props = defineProps<IProps>();
|
|
42
|
+
|
|
43
|
+
// emits
|
|
44
|
+
interface IEmits {
|
|
45
|
+
(e: "select", contact: IChatContact): void;
|
|
46
|
+
(e: "schedule", schedule: boolean): void;
|
|
47
|
+
}
|
|
48
|
+
const emit = defineEmits<IEmits>();
|
|
49
|
+
|
|
50
|
+
// computed
|
|
51
|
+
const getSelectedClass = computed<Function>(() => (contact: IChatContact) => {
|
|
52
|
+
if (props.selected?.id === contact.id)
|
|
53
|
+
return "bg-slate-100 dark:bg-slate-800";
|
|
54
|
+
return "";
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// data
|
|
58
|
+
const visibleSchedules = ref<boolean>(false)
|
|
59
|
+
|
|
60
|
+
// methods
|
|
61
|
+
function select(contact: IChatContact): void {
|
|
62
|
+
emit("select", contact);
|
|
63
|
+
}
|
|
64
|
+
function changeSchedule(): void {
|
|
65
|
+
visibleSchedules.value = !visibleSchedules.value
|
|
66
|
+
emit("schedule", visibleSchedules.value);
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
.text-2xs {
|
|
72
|
+
font-size: 0.7rem;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
<MoleculeFieldGroup
|
|
71
71
|
:rules="getRulesNote"
|
|
72
72
|
:class="{ error: noteIsInvalid }"
|
|
73
|
+
label="Observação de Tabulação"
|
|
73
74
|
>
|
|
74
75
|
<AtomFieldTextarea
|
|
75
76
|
v-model="note"
|
|
@@ -108,6 +109,29 @@
|
|
|
108
109
|
/>
|
|
109
110
|
</label>
|
|
110
111
|
</MoleculeFieldGroup>
|
|
112
|
+
|
|
113
|
+
<!-- Exibir quando ter manifestação vinculada -->
|
|
114
|
+
<div
|
|
115
|
+
v-show="!manifestationIsEmpty"
|
|
116
|
+
class="mx-5 !pb-4"
|
|
117
|
+
>
|
|
118
|
+
<div class="divider" />
|
|
119
|
+
|
|
120
|
+
<!-- manifestatio observation -->
|
|
121
|
+
<article>
|
|
122
|
+
<MoleculeFieldGroup
|
|
123
|
+
label="Nota de Manifestação"
|
|
124
|
+
class="mt-3"
|
|
125
|
+
>
|
|
126
|
+
<AtomFieldTextarea
|
|
127
|
+
v-model="manifestationNote"
|
|
128
|
+
rows="5"
|
|
129
|
+
placeholder="Nota de Manifestação"
|
|
130
|
+
@blur="persist"
|
|
131
|
+
/>
|
|
132
|
+
</MoleculeFieldGroup>
|
|
133
|
+
</article>
|
|
134
|
+
</div>
|
|
111
135
|
</details>
|
|
112
136
|
</div>
|
|
113
137
|
</article>
|
|
@@ -125,7 +149,7 @@ interface IProps {
|
|
|
125
149
|
manifestation?: INodes;
|
|
126
150
|
persist?: Function;
|
|
127
151
|
disableNode?: (({ nodeValue }: { nodeValue: string }) => boolean) | undefined;
|
|
128
|
-
defaults?: { tabulation: INodes[]; note: string };
|
|
152
|
+
defaults?: { tabulation: INodes[]; note: string ; manifestationNote?: string };
|
|
129
153
|
}
|
|
130
154
|
const props = defineProps<IProps>();
|
|
131
155
|
|
|
@@ -184,6 +208,7 @@ const MoleculeFieldGroupRef = ref<InstanceType<
|
|
|
184
208
|
> | null>(null);
|
|
185
209
|
const tabulation = reactive<INodes[]>(props.defaults?.tabulation ?? []);
|
|
186
210
|
const note = ref<string>(props.defaults?.note ?? "");
|
|
211
|
+
const manifestationNote = ref<string>(props.defaults?.manifestationNote ?? "");
|
|
187
212
|
const additionals = reactive<any>({});
|
|
188
213
|
|
|
189
214
|
// methods
|
|
@@ -198,6 +223,7 @@ function persist(): void {
|
|
|
198
223
|
props.persist({
|
|
199
224
|
tabulation: tabulation,
|
|
200
225
|
note: note.value,
|
|
226
|
+
manifestationNote: manifestationNote.value,
|
|
201
227
|
additionals,
|
|
202
228
|
});
|
|
203
229
|
}
|
|
@@ -228,6 +254,7 @@ watch(
|
|
|
228
254
|
defineExpose({
|
|
229
255
|
tabulation,
|
|
230
256
|
note: getNote,
|
|
257
|
+
manifestationNote,
|
|
231
258
|
additionals,
|
|
232
259
|
isValid,
|
|
233
260
|
isSchedule,
|
|
@@ -180,9 +180,15 @@ export function PausesDTO({ lista_pausa }) {
|
|
|
180
180
|
return pauses;
|
|
181
181
|
}
|
|
182
182
|
export function DoubtsDTO(primitives) {
|
|
183
|
-
if (!primitives)
|
|
183
|
+
if (!primitives || !Array.isArray(primitives)) {
|
|
184
184
|
return [];
|
|
185
|
-
|
|
185
|
+
}
|
|
186
|
+
return primitives.reduce((acc, primitive) => {
|
|
187
|
+
if (primitive && Array.isArray(primitive.doubt)) {
|
|
188
|
+
acc.push(...primitive.doubt);
|
|
189
|
+
}
|
|
190
|
+
return acc;
|
|
191
|
+
}, []);
|
|
186
192
|
}
|
|
187
193
|
export function UserDTO(primitive) {
|
|
188
194
|
if (!primitive.info_agente) {
|