Skip to content

Commit 80bb7a5

Browse files
feat: connect email fields to reply,reply all and other fixes
1 parent 90bb078 commit 80bb7a5

File tree

10 files changed

+193
-101
lines changed

10 files changed

+193
-101
lines changed

desk/src/components/CommunicationArea.vue

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,9 @@
2323
</template>
2424
</Button>
2525
</div>
26-
<div v-show="showEmailBox" class="flex gap-1.5">
27-
<Button
28-
label="CC"
29-
:class="[false ? 'bg-gray-300 hover:bg-gray-200' : '']"
30-
@click="toggleCC()"
31-
/>
32-
<Button
33-
label="BCC"
34-
:class="[false ? 'bg-gray-300 hover:bg-gray-200' : '']"
35-
@click="toggleBCC()"
36-
/>
37-
</div>
3826
</div>
3927
<div v-show="showCommentBox">
4028
<CommentTextEditor
41-
ref="newCommentEditor"
4229
v-model="doc"
4330
v-model:attachments="attachments"
4431
:editable="showCommentBox"
@@ -64,7 +51,8 @@
6451
@keydown.meta.enter.capture.stop="submitEmail"
6552
>
6653
<EmailEditor
67-
ref="newEmailEditor"
54+
ref="emailEditorRef"
55+
v-model="doc"
6856
v-model:content="content"
6957
v-model:attachments="attachments"
7058
:to-emails="toEmails"
@@ -86,21 +74,18 @@
8674
</template>
8775

8876
<script setup lang="ts">
89-
import { createResource } from "frappe-ui";
90-
import EmailIcon from "@/components/icons/EmailIcon.vue";
91-
import CommentIcon from "@/components/icons/CommentIcon.vue";
92-
import { useAuthStore } from "@/stores/auth";
77+
import { ref, defineModel } from "vue";
78+
9379
import { EmailEditor, CommentTextEditor } from "@/components";
94-
import { ref, defineModel, nextTick } from "vue";
80+
import { EmailIcon, CommentIcon } from "@/components/icons/";
9581
96-
const authStore = useAuthStore();
9782
const content = defineModel("content");
9883
const showEmailBox = ref(false);
9984
const showCommentBox = ref(false);
10085
const doc = defineModel();
10186
const attachments = ref([]);
102-
const newEmailEditor = ref(null);
10387
const emit = defineEmits(["update"]);
88+
const emailEditorRef = ref(null);
10489
10590
function toggleEmailBox() {
10691
if (showCommentBox.value) {
@@ -116,20 +101,14 @@ function toggleCommentBox() {
116101
showCommentBox.value = !showCommentBox.value;
117102
}
118103
119-
function toggleCC() {
120-
newEmailEditor.value.cc = !newEmailEditor.value.cc;
121-
newEmailEditor.value.cc &&
122-
nextTick(() => {
123-
newEmailEditor.value.ccInput.setFocus();
124-
});
125-
}
126-
127-
function toggleBCC() {
128-
newEmailEditor.value.bcc = !newEmailEditor.value.bcc;
129-
newEmailEditor.value.bcc &&
130-
nextTick(() => {
131-
newEmailEditor.value.bccInput.setFocus();
132-
});
104+
function replyToEmail(data: object) {
105+
showEmailBox.value = true;
106+
emailEditorRef.value.addToReply(
107+
data.content,
108+
data.to?.split(","),
109+
data.cc?.split(","),
110+
data.bcc?.split(",")
111+
);
133112
}
134113
135114
const props = defineProps({
@@ -149,9 +128,9 @@ const props = defineProps({
149128
type: Array,
150129
default: () => [],
151130
},
152-
content: {
153-
type: String,
154-
default: "",
155-
},
131+
});
132+
133+
defineExpose({
134+
replyToEmail,
156135
});
157136
</script>

desk/src/components/EmailBox.vue

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,32 @@
1515
</Tooltip>
1616
</div>
1717
<div class="flex gap-0.5">
18-
<Tooltip text="Reply">
19-
<Button
20-
variant="ghost"
21-
class="text-gray-700"
22-
@click="console.log('reply')"
23-
>
24-
<ReplyIcon class="h-4 w-4" />
25-
</Button>
26-
</Tooltip>
27-
<Tooltip text="Reply All">
28-
<Button
29-
variant="ghost"
30-
class="text-gray-700"
31-
@click="console.log('reply all')"
32-
>
33-
<ReplyAllIcon class="h-4 w-4" />
34-
</Button>
35-
</Tooltip>
18+
<Button
19+
variant="ghost"
20+
class="text-gray-700"
21+
@click="
22+
emit('reply', {
23+
content: content,
24+
to: sender.name,
25+
})
26+
"
27+
>
28+
<ReplyIcon class="h-4 w-4" />
29+
</Button>
30+
<Button
31+
variant="ghost"
32+
class="text-gray-700"
33+
@click="
34+
emit('reply', {
35+
content: content,
36+
to: to,
37+
cc: cc,
38+
bcc: bcc,
39+
})
40+
"
41+
>
42+
<ReplyAllIcon class="h-4 w-4" />
43+
</Button>
3644
</div>
3745
</div>
3846
<div class="text-sm leading-5 text-gray-600">
@@ -68,8 +76,7 @@
6876
<script setup lang="ts">
6977
import { UserAvatar, AttachmentItem } from "@/components";
7078
import { dateFormat, timeAgo, dateTooltipFormat } from "@/utils";
71-
import ReplyIcon from "./icons/ReplyIcon.vue";
72-
import ReplyAllIcon from "./icons/ReplyAllIcon.vue";
79+
import { ReplyIcon, ReplyAllIcon } from "./icons/";
7380
7481
const props = defineProps({
7582
sender: {
@@ -93,4 +100,6 @@ const props = defineProps({
93100
required: true,
94101
},
95102
});
103+
104+
const emit = defineEmits(["reply"]);
96105
</script>

desk/src/components/EmailEditor.vue

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<template>
22
<TextEditor
3-
ref="e"
3+
ref="editorRef"
44
:editor-class="[
55
'prose-sm max-w-none mx-10 max-h-[50vh] overflow-y-auto border-t py-3',
66
true && 'min-h-[7rem]',
77
]"
8-
:content="content"
8+
:content="newEmail"
99
:starterkit-options="{ heading: { levels: [2, 3, 4, 5, 6] } }"
1010
:placeholder="placeholder"
1111
:editable="editable"
12-
@change="editable ? (content = $event) : null"
12+
@change="editable ? (newEmail = $event) : null"
1313
>
1414
<template #top>
1515
<div
@@ -23,9 +23,19 @@
2323
:validate="validateEmail"
2424
:error-message="(value) => `${value} is an invalid email address`"
2525
/>
26+
<Button
27+
:label="'CC'"
28+
:class="[cc ? 'bg-gray-300 hover:bg-gray-200' : '']"
29+
@click="toggleCC()"
30+
/>
31+
<Button
32+
:label="'BCC'"
33+
:class="[bcc ? 'bg-gray-300 hover:bg-gray-200' : '']"
34+
@click="toggleBCC()"
35+
/>
2636
</div>
2737
<div
28-
v-if="cc"
38+
v-if="showCC || cc"
2939
class="mx-10 flex items-center gap-2 py-2.5"
3040
:class="bcc ? 'border-b' : ''"
3141
>
@@ -38,7 +48,7 @@
3848
:error-message="(value) => `${value} is an invalid email address`"
3949
/>
4050
</div>
41-
<div v-if="bcc" class="mx-10 flex items-center gap-2 py-2.5">
51+
<div v-if="showBCC || bcc" class="mx-10 flex items-center gap-2 py-2.5">
4252
<span class="text-xs text-gray-500">BCC:</span>
4353
<MultiSelectInput
4454
ref="bccInput"
@@ -99,7 +109,11 @@
99109
variant="solid"
100110
:disabled="emailEmpty"
101111
label="Submit"
102-
@click="() => {}"
112+
@click="
113+
() => {
114+
submitMail();
115+
}
116+
"
103117
/>
104118
</div>
105119
</div>
@@ -108,7 +122,7 @@
108122
</template>
109123

110124
<script setup lang="ts">
111-
import { defineModel, ref, computed } from "vue";
125+
import { defineModel, ref, computed, nextTick } from "vue";
112126
import { useStorage } from "@vueuse/core";
113127
import {
114128
FileUploader,
@@ -119,6 +133,7 @@ import {
119133
import { validateEmail } from "@/utils";
120134
import { MultiSelectInput } from "@/components";
121135
import { AttachmentIcon } from "@/components/icons";
136+
const editorRef = ref(null);
122137
123138
const props = defineProps({
124139
placeholder: {
@@ -133,14 +148,6 @@ const props = defineProps({
133148
type: String,
134149
default: "HD Ticket",
135150
},
136-
submitButtonProps: {
137-
type: Object,
138-
default: () => ({}),
139-
},
140-
discardButtonProps: {
141-
type: Object,
142-
default: () => ({}),
143-
},
144151
toEmails: {
145152
type: Array,
146153
default: () => [],
@@ -164,13 +171,14 @@ const emailEmpty = computed(() => {
164171
const emit = defineEmits(["submit", "discard"]);
165172
166173
const doc = defineModel();
167-
const content = defineModel("content");
168174
const attachments = defineModel("attachments");
169-
const cc = ref(props.ccEmails.length ? true : false);
170-
const bcc = ref(props.bccEmails.length ? true : false);
171175
const toEmailsClone = ref([...props.toEmails]);
172176
const ccEmailsClone = ref([...props.ccEmails]);
173177
const bccEmailsClone = ref([...props.bccEmails]);
178+
const showCC = ref(false);
179+
const showBCC = ref(false);
180+
const cc = computed(() => (ccEmailsClone.value.length ? true : false));
181+
const bcc = computed(() => (bccEmailsClone.value.length ? true : false));
174182
const ccInput = ref(null);
175183
const bccInput = ref(null);
176184
@@ -179,22 +187,61 @@ function submitMail() {
179187
url: "run_doc_method",
180188
makeParams: () => ({
181189
dt: props.doctype,
182-
dn: doc.value.data.name,
190+
dn: doc.value.name,
183191
method: "reply_via_agent",
184192
args: {
185193
attachments: attachments.value.map((x) => x.name),
186-
cc: ccEmailsClone,
187-
bcc: bccEmailsClone,
194+
to: toEmailsClone.value.join(","),
195+
cc: ccEmailsClone.value.join(","),
196+
bcc: bccEmailsClone.value.join(","),
188197
message: newEmail.value,
189198
},
190199
}),
191200
onSuccess: () => {
192201
emit("submit");
193202
},
194203
});
204+
195205
sendMail.submit();
196206
}
197207
208+
function toggleCC() {
209+
showCC.value = !showCC.value;
210+
211+
showCC.value &&
212+
nextTick(() => {
213+
ccInput.value.setFocus();
214+
});
215+
}
216+
217+
function toggleBCC() {
218+
showBCC.value = !showBCC.value;
219+
showBCC.value &&
220+
nextTick(() => {
221+
bccInput.value.setFocus();
222+
});
223+
}
224+
225+
function addToReply(
226+
body: string,
227+
toEmails: string[],
228+
ccEmails: string[],
229+
bccEmails: string[]
230+
) {
231+
toEmailsClone.value = toEmails;
232+
ccEmailsClone.value = ccEmails;
233+
bccEmailsClone.value = bccEmails;
234+
editorRef.value.editor
235+
.chain()
236+
.clearContent()
237+
.insertContent(body)
238+
.focus("all")
239+
.setBlockquote()
240+
.insertContentAt(0, { type: "paragraph" })
241+
.focus("start")
242+
.run();
243+
}
244+
198245
const textEditorMenuButtons = [
199246
"Paragraph",
200247
["Heading 2", "Heading 3", "Heading 4", "Heading 5", "Heading 6"],
@@ -232,4 +279,8 @@ const textEditorMenuButtons = [
232279
"DeleteTable",
233280
],
234281
];
282+
283+
defineExpose({
284+
addToReply,
285+
});
235286
</script>

desk/src/components/icons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export { default as CommentIcon } from "./CommentIcon.vue";
88
export { default as IndicatorIcon } from "./IndicatorIcon.vue";
99
export { default as TicketIcon } from "./TicketIcon.vue";
1010
export { default as AttachmentIcon } from "./AttachmentIcon.vue";
11+
export { default as ReplyIcon } from "./ReplyIcon.vue";
12+
export { default as ReplyAllIcon } from "./ReplyAllIcon.vue";

desk/src/components/ticket/TicketAgentActivities.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
</div>
2424
</div>
2525
<div class="mt-4 w-full">
26-
<EmailBox v-if="activity.type === 'email'" v-bind="activity" />
26+
<EmailBox
27+
v-if="activity.type === 'email'"
28+
v-bind="activity"
29+
@reply="(e) => emit('email:reply', e)"
30+
/>
2731
<CommentBox
2832
v-else-if="activity.type === 'comment'"
2933
v-bind="activity"
@@ -47,6 +51,8 @@ defineProps({
4751
},
4852
});
4953
54+
const emit = defineEmits(["email:reply"]);
55+
5056
function getActivityIcon(type) {
5157
if (type === "email") return EmailAtIcon;
5258
else if (type === "comment") return CommentIcon;

0 commit comments

Comments
 (0)