Skip to content

Commit 3a35a33

Browse files
authored
✨ [Frontend] Support: Book a call (#8297)
1 parent 63eac38 commit 3a35a33

File tree

11 files changed

+484
-57
lines changed

11 files changed

+484
-57
lines changed

services/static-webserver/client/source/class/osparc/conversation/AddMessage.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ qx.Class.define("osparc.conversation.AddMessage", {
175175
addComment: function() {
176176
const conversationId = this.getConversationId();
177177
if (conversationId) {
178-
this.__postMessage();
178+
return this.__postMessage();
179179
} else {
180180
const studyData = this.getStudyData();
181181
let promise = null;
@@ -191,10 +191,10 @@ qx.Class.define("osparc.conversation.AddMessage", {
191191
}
192192
promise = osparc.store.ConversationsSupport.getInstance().postConversation(extraContext);
193193
}
194-
promise
194+
return promise
195195
.then(data => {
196196
this.setConversationId(data["conversationId"]);
197-
this.__postMessage();
197+
return this.__postMessage();
198198
});
199199
}
200200
},
@@ -211,12 +211,14 @@ qx.Class.define("osparc.conversation.AddMessage", {
211211
} else {
212212
promise = osparc.store.ConversationsSupport.getInstance().postMessage(conversationId, content);
213213
}
214-
promise
214+
return promise
215215
.then(data => {
216216
this.fireDataEvent("messageAdded", data);
217217
commentField.getChildControl("text-area").setValue("");
218+
return data;
218219
});
219220
}
221+
return Promise.reject();
220222
},
221223

222224
__editComment: function() {

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1515,7 +1515,7 @@ qx.Class.define("osparc.data.Resources", {
15151515
method: "GET",
15161516
url: statics.API + "/conversations/{conversationId}"
15171517
},
1518-
renameConversation: {
1518+
patchConversation: {
15191519
method: "PATCH",
15201520
url: statics.API + "/conversations/{conversationId}"
15211521
},

services/static-webserver/client/source/class/osparc/data/model/Conversation.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ qx.Class.define("osparc.data.model.Conversation", {
246246
});
247247
},
248248

249+
patchExtraContext: function(extraContext) {
250+
osparc.store.ConversationsSupport.getInstance().patchExtraContext(this.getConversationId(), extraContext)
251+
.then(() => {
252+
this.setExtraContext(extraContext);
253+
});
254+
},
255+
249256
addMessage: function(message) {
250257
if (message) {
251258
const found = this.__messages.find(msg => msg["messageId"] === message["messageId"]);
@@ -284,6 +291,27 @@ qx.Class.define("osparc.data.model.Conversation", {
284291
return this.getExtraContext()["projectId"];
285292
}
286293
return null;
287-
}
294+
},
295+
296+
getAppointment: function() {
297+
if (this.getExtraContext() && "appointment" in this.getExtraContext()) {
298+
return this.getExtraContext()["appointment"];
299+
}
300+
return null;
301+
},
302+
303+
setAppointment: function(appointment) {
304+
const extraContext = this.getExtraContext() || {};
305+
extraContext["appointment"] = appointment ? appointment.toISOString() : null;
306+
// OM: Supporters are not allowed to patch the conversation metadata yet
307+
const backendAllowsPatch = osparc.store.Products.getInstance().amIASupportUser() ? false : true;
308+
if (backendAllowsPatch) {
309+
return osparc.store.ConversationsSupport.getInstance().patchExtraContext(this.getConversationId(), extraContext)
310+
.then(() => {
311+
this.setExtraContext(Object.assign({}, extraContext));
312+
});
313+
}
314+
return Promise.resolve(this.setExtraContext(Object.assign({}, extraContext)));
315+
},
288316
},
289317
});

services/static-webserver/client/source/class/osparc/store/ConversationsSupport.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,19 @@ qx.Class.define("osparc.store.ConversationsSupport", {
124124
name,
125125
}
126126
};
127-
return osparc.data.Resources.fetch("conversationsSupport", "renameConversation", params);
127+
return osparc.data.Resources.fetch("conversationsSupport", "patchConversation", params);
128+
},
129+
130+
patchExtraContext: function(conversationId, extraContext) {
131+
const params = {
132+
url: {
133+
conversationId,
134+
},
135+
data: {
136+
extraContext,
137+
}
138+
};
139+
return osparc.data.Resources.fetch("conversationsSupport", "patchConversation", params);
128140
},
129141

130142
fetchLastMessage: function(conversationId) {

services/static-webserver/client/source/class/osparc/support/ConversationPage.js

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,8 @@ qx.Class.define("osparc.support.ConversationPage", {
8484
});
8585
this.getChildControl("conversation-header-center-layout").addAt(control, 0);
8686
break;
87-
case "conversation-extra-content":
88-
control = new qx.ui.basic.Label().set({
89-
font: "text-12",
90-
textColor: "text-disabled",
91-
rich: true,
92-
allowGrowX: true,
93-
selectable: true,
94-
});
87+
case "conversation-extra-layout":
88+
control = new qx.ui.container.Composite(new qx.ui.layout.VBox(2));
9589
this.getChildControl("conversation-header-center-layout").addAt(control, 1);
9690
break;
9791
case "open-project-button":
@@ -105,6 +99,19 @@ qx.Class.define("osparc.support.ConversationPage", {
10599
control.addListener("execute", () => this.__openProjectDetails());
106100
this.getChildControl("conversation-header-layout").addAt(control, 2);
107101
break;
102+
case "set-appointment-button": {
103+
control = new qx.ui.form.Button().set({
104+
maxWidth: 26,
105+
maxHeight: 24,
106+
padding: [0, 6],
107+
alignX: "center",
108+
alignY: "middle",
109+
icon: "@FontAwesome5Solid/clock/12",
110+
});
111+
control.addListener("execute", () => this.__openAppointmentDetails());
112+
this.getChildControl("conversation-header-layout").addAt(control, 3);
113+
break;
114+
}
108115
case "conversation-options": {
109116
control = new qx.ui.form.MenuButton().set({
110117
maxWidth: 24,
@@ -123,7 +130,7 @@ qx.Class.define("osparc.support.ConversationPage", {
123130
});
124131
renameButton.addListener("execute", () => this.__renameConversation());
125132
menu.add(renameButton);
126-
this.getChildControl("conversation-header-layout").addAt(control, 3);
133+
this.getChildControl("conversation-header-layout").addAt(control, 4);
127134
break;
128135
}
129136
case "conversation-content":
@@ -146,28 +153,65 @@ qx.Class.define("osparc.support.ConversationPage", {
146153
title.setValue(this.tr("Ask a Question"));
147154
}
148155

149-
const extraContextLabel = this.getChildControl("conversation-extra-content");
156+
const extraContextLayout = this.getChildControl("conversation-extra-layout");
150157
const amISupporter = osparc.store.Products.getInstance().amIASupportUser();
151-
if (conversation && amISupporter) {
152-
const extraContext = conversation.getExtraContext();
153-
if (extraContext && Object.keys(extraContext).length) {
154-
let extraContextText = `Ticket ID: ${conversation.getConversationId()}`;
155-
const contextProjectId = conversation.getContextProjectId();
156-
if (contextProjectId) {
157-
extraContextText += `<br>Project ID: ${contextProjectId}`;
158+
if (conversation) {
159+
const createExtraContextLabel = text => {
160+
return new qx.ui.basic.Label(text).set({
161+
font: "text-12",
162+
textColor: "text-disabled",
163+
rich: true,
164+
allowGrowX: true,
165+
selectable: true,
166+
});
167+
};
168+
const updateExtraContext = () => {
169+
extraContextLayout.removeAll();
170+
const extraContext = conversation.getExtraContext();
171+
if (extraContext && Object.keys(extraContext).length) {
172+
const ticketIdLabel = createExtraContextLabel(`Ticket ID: ${conversation.getConversationId()}`);
173+
extraContextLayout.add(ticketIdLabel);
174+
const contextProjectId = conversation.getContextProjectId();
175+
if (contextProjectId && amISupporter) {
176+
const projectIdLabel = createExtraContextLabel(`Project ID: ${contextProjectId}`);
177+
extraContextLayout.add(projectIdLabel);
178+
}
179+
const appointment = conversation.getAppointment();
180+
if (appointment) {
181+
const appointmentLabel = createExtraContextLabel();
182+
let appointmentText = "Appointment: ";
183+
if (appointment === "requested") {
184+
// still pending
185+
appointmentText += appointment;
186+
} else {
187+
// already set
188+
appointmentText += osparc.utils.Utils.formatDateAndTime(new Date(appointment));
189+
appointmentLabel.set({
190+
cursor: "pointer",
191+
toolTipText: osparc.utils.Utils.formatDateWithCityAndTZ(new Date(appointment)),
192+
});
193+
}
194+
appointmentLabel.setValue(appointmentText);
195+
extraContextLayout.add(appointmentLabel);
196+
}
158197
}
159-
extraContextLabel.setValue(extraContextText);
160-
}
161-
extraContextLabel.show();
162-
} else {
163-
extraContextLabel.exclude();
198+
};
199+
updateExtraContext();
200+
conversation.addListener("changeExtraContext", () => updateExtraContext(), this);
164201
}
165202

166-
const openButton = this.getChildControl("open-project-button");
203+
const openProjectButton = this.getChildControl("open-project-button");
167204
if (conversation && conversation.getContextProjectId()) {
168-
openButton.show();
205+
openProjectButton.show();
169206
} else {
170-
openButton.exclude();
207+
openProjectButton.exclude();
208+
}
209+
210+
const setAppointmentButton = this.getChildControl("set-appointment-button");
211+
if (conversation && conversation.getAppointment() && amISupporter) {
212+
setAppointmentButton.show();
213+
} else {
214+
setAppointmentButton.exclude();
171215
}
172216

173217
const options = this.getChildControl("conversation-options");
@@ -193,6 +237,17 @@ qx.Class.define("osparc.support.ConversationPage", {
193237
}
194238
},
195239

240+
__openAppointmentDetails: function() {
241+
const win = new osparc.widget.DateTimeChooser();
242+
win.addListener("dateChanged", e => {
243+
const newValue = e.getData()["newValue"];
244+
this.getConversation().setAppointment(newValue)
245+
.catch(err => console.error(err));
246+
win.close();
247+
}, this);
248+
win.open();
249+
},
250+
196251
__renameConversation: function() {
197252
let oldName = this.getConversation().getName();
198253
if (oldName === "null") {
@@ -207,5 +262,19 @@ qx.Class.define("osparc.support.ConversationPage", {
207262
renamer.center();
208263
renamer.open();
209264
},
265+
266+
__getAddMessageField: function() {
267+
return this.getChildControl("conversation-content") &&
268+
this.getChildControl("conversation-content").getChildControl("add-message");
269+
},
270+
271+
postMessage: function(message) {
272+
const addMessage = this.__getAddMessageField();
273+
if (addMessage && addMessage.getChildControl("comment-field")) {
274+
addMessage.getChildControl("comment-field").setText(message);
275+
return addMessage.addComment();
276+
}
277+
return Promise.reject();
278+
},
210279
}
211280
});

services/static-webserver/client/source/class/osparc/support/SupportCenter.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ qx.Class.define("osparc.support.SupportCenter", {
3838
this.getChildControl("conversations-intro-text");
3939
this.getChildControl("conversations-list");
4040
this.getChildControl("ask-a-question-button");
41+
this.getChildControl("book-a-call-button");
4142
},
4243

4344
statics: {
4445
WINDOW_WIDTH: 430,
46+
REQUEST_CALL_MESSAGE: "Dear Support,\nI would like to make an appointment for a support call.",
4547

4648
getMaxHeight: function() {
4749
// height: max 80% of screen, or 600px
@@ -112,15 +114,29 @@ qx.Class.define("osparc.support.SupportCenter", {
112114
});
113115
break;
114116
}
117+
case "buttons-layout":
118+
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
119+
alignX: "center",
120+
}));
121+
this.getChildControl("conversations-layout").add(control);
122+
break;
115123
case "ask-a-question-button":
116124
control = new osparc.ui.form.FetchButton(this.tr("Ask a Question")).set({
117125
appearance: "strong-button",
118126
allowGrowX: false,
119127
center: true,
120-
alignX: "center",
121128
});
122129
control.addListener("execute", () => this.openConversation(null), this);
123-
this.getChildControl("conversations-layout").add(control);
130+
this.getChildControl("buttons-layout").add(control);
131+
break;
132+
case "book-a-call-button":
133+
control = new osparc.ui.form.FetchButton(this.tr("Book a Call")).set({
134+
appearance: "strong-button",
135+
allowGrowX: false,
136+
center: true,
137+
});
138+
control.addListener("execute", () => this.createConversationBookCall(null), this);
139+
this.getChildControl("buttons-layout").add(control);
124140
break;
125141
case "conversation-page":
126142
control = new osparc.support.ConversationPage();
@@ -152,5 +168,30 @@ qx.Class.define("osparc.support.SupportCenter", {
152168
this.__showConversation();
153169
}
154170
},
171+
172+
createConversationBookCall: function() {
173+
const conversationPage = this.getChildControl("conversation-page");
174+
conversationPage.setConversation(null);
175+
this.__showConversation();
176+
conversationPage.postMessage(osparc.support.SupportCenter.REQUEST_CALL_MESSAGE)
177+
.then(data => {
178+
const conversationId = data["conversationId"];
179+
osparc.store.ConversationsSupport.getInstance().getConversation(conversationId)
180+
.then(conversation => {
181+
// update conversation name and patch extra_context
182+
conversation.renameConversation("Book a call");
183+
conversation.patchExtraContext({
184+
...conversation.getExtraContext(),
185+
"appointment": "requested"
186+
});
187+
// This should be an automatic response in the chat
188+
const msg = this.tr("Your request has been sent.<br>Our support team will get back to you.");
189+
osparc.FlashMessenger.logAs(msg, "INFO");
190+
});
191+
})
192+
.catch(err => {
193+
console.error("Error sending request call message", err);
194+
});
195+
},
155196
}
156197
});

0 commit comments

Comments
 (0)