@tiledesk/tiledesk-server 2.4.52 → 2.4.54
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +3 -0
- package/app.js +5 -2
- package/docs/api-dev.md +42 -1
- package/models/faq.js +6 -0
- package/models/segment.js +67 -0
- package/package.json +2 -2
- package/routes/faq.js +66 -23
- package/routes/faq_kb.js +7 -3
- package/routes/kbsettings.js +76 -29
- package/routes/lead.js +22 -2
- package/routes/openai.js +112 -0
- package/routes/segment.js +157 -0
- package/routes/users.js +24 -0
- package/services/emailService.js +33 -0
- package/services/openaiService.js +110 -0
- package/template/email/redirectToDesktopEmail.html +304 -0
- package/template/email/redirectToDesktopEmail_new.html +303 -0
- package/test/faqRoute.js +116 -51
- package/test/openaiRoute.js +134 -0
- package/test/userRoute.js +63 -0
- package/utils/segment2mongoConverter.js +98 -0
- package/routes/openai_kbs.js +0 -58
- package/test/openaiKbsRoute.js +0 -75
@@ -0,0 +1,157 @@
|
|
1
|
+
var express = require('express');
|
2
|
+
var router = express.Router();
|
3
|
+
var winston = require('../config/winston');
|
4
|
+
var Segment = require("../models/segment");
|
5
|
+
|
6
|
+
|
7
|
+
router.post('/', function (req, res) {
|
8
|
+
|
9
|
+
winston.debug(req.body);
|
10
|
+
winston.debug("req.user", req.user);
|
11
|
+
|
12
|
+
var newSegment = new Segment({
|
13
|
+
name: req.body.name,
|
14
|
+
match: req.body.match,
|
15
|
+
filters: req.body.filters,
|
16
|
+
id_project: req.projectid,
|
17
|
+
createdBy: req.user.id
|
18
|
+
});
|
19
|
+
|
20
|
+
newSegment.save(function(err, segment) {
|
21
|
+
if (err) {
|
22
|
+
winston.error('Error saving the segment '+ JSON.stringify(segment), err);
|
23
|
+
return res.status(500).send({ success: false, msg: 'Error updating object.' });
|
24
|
+
|
25
|
+
}
|
26
|
+
winston.verbose('segment created ', segment.toJSON());
|
27
|
+
|
28
|
+
|
29
|
+
res.json(segment);
|
30
|
+
})
|
31
|
+
|
32
|
+
});
|
33
|
+
|
34
|
+
router.put('/:segmentid', function (req, res) {
|
35
|
+
winston.debug(req.body);
|
36
|
+
var update = {};
|
37
|
+
|
38
|
+
if (req.body.name!=undefined) {
|
39
|
+
update.name = req.body.name;
|
40
|
+
}
|
41
|
+
|
42
|
+
if (req.body.match!=undefined) {
|
43
|
+
update.match = req.body.match;
|
44
|
+
}
|
45
|
+
if (req.body.filters!=undefined) {
|
46
|
+
update.filters = req.body.filters;
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
Segment.findByIdAndUpdate(req.params.segmentid, update, { new: true, upsert: true }, function (err, updatedSegment) {
|
52
|
+
if (err) {
|
53
|
+
winston.error('--- > ERROR ', err);
|
54
|
+
return res.status(500).send({ success: false, msg: 'Error updating object.' });
|
55
|
+
}
|
56
|
+
|
57
|
+
res.json(updatedSegment);
|
58
|
+
});
|
59
|
+
});
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
router.delete('/:segmentid', function (req, res) {
|
64
|
+
winston.debug(req.body);
|
65
|
+
|
66
|
+
Segment.findByIdAndUpdate(req.params.segmentid, {status:0}, { new: true, upsert: true }, function (err, updatedSegment) {
|
67
|
+
if (err) {
|
68
|
+
winston.error('--- > ERROR ', err);
|
69
|
+
return res.status(500).send({ success: false, msg: 'Error updating object.' });
|
70
|
+
}
|
71
|
+
|
72
|
+
res.json(updatedSegment);
|
73
|
+
});
|
74
|
+
});
|
75
|
+
|
76
|
+
|
77
|
+
router.get('/:segmentid', function (req, res) {
|
78
|
+
winston.debug(req.body);
|
79
|
+
|
80
|
+
Segment.findById(req.params.segmentid, function (err, segment) {
|
81
|
+
if (err) {
|
82
|
+
return res.status(500).send({ success: false, msg: 'Error getting object.' });
|
83
|
+
}
|
84
|
+
if (!segment) {
|
85
|
+
return res.status(404).send({ success: false, msg: 'Object not found.' });
|
86
|
+
}
|
87
|
+
res.json(segment);
|
88
|
+
});
|
89
|
+
});
|
90
|
+
|
91
|
+
|
92
|
+
router.get('/', function (req, res) {
|
93
|
+
|
94
|
+
var limit = 40; // Number of request per page
|
95
|
+
|
96
|
+
if (req.query.limit) {
|
97
|
+
limit = parseInt(req.query.limit);
|
98
|
+
winston.debug('LEAD ROUTE - limit: '+limit);
|
99
|
+
}
|
100
|
+
|
101
|
+
var page = 0;
|
102
|
+
|
103
|
+
if (req.query.page) {
|
104
|
+
page = req.query.page;
|
105
|
+
}
|
106
|
+
|
107
|
+
var skip = page * limit;
|
108
|
+
winston.debug('LEAD ROUTE - SKIP PAGE ', skip);
|
109
|
+
|
110
|
+
|
111
|
+
var query = { "id_project": req.projectid, "status": 100};
|
112
|
+
|
113
|
+
var direction = -1; //-1 descending , 1 ascending
|
114
|
+
if (req.query.direction) {
|
115
|
+
direction = req.query.direction;
|
116
|
+
}
|
117
|
+
|
118
|
+
var sortField = "createdAt";
|
119
|
+
if (req.query.sort) {
|
120
|
+
sortField = req.query.sort;
|
121
|
+
}
|
122
|
+
|
123
|
+
var sortQuery = {};
|
124
|
+
sortQuery[sortField] = direction;
|
125
|
+
|
126
|
+
winston.debug("sort query", sortQuery);
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
return Segment.find(query).
|
131
|
+
skip(skip).limit(limit).
|
132
|
+
sort(sortQuery).
|
133
|
+
exec(function (err, segments) {
|
134
|
+
if (err) {
|
135
|
+
winston.error('segments ROUTE - REQUEST FIND ERR ', err)
|
136
|
+
return (err);
|
137
|
+
}
|
138
|
+
|
139
|
+
// blocked to 1000 TODO increases it
|
140
|
+
// collection.count is deprecated, and will be removed in a future version. Use Collection.countDocuments or Collection.estimatedDocumentCount instead
|
141
|
+
return Segment.countDocuments(query, function (err, totalRowCount) {
|
142
|
+
|
143
|
+
var objectToReturn = {
|
144
|
+
perPage: limit,
|
145
|
+
count: totalRowCount,
|
146
|
+
segments: segments
|
147
|
+
};
|
148
|
+
|
149
|
+
return res.json(objectToReturn);
|
150
|
+
});
|
151
|
+
});
|
152
|
+
});
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
module.exports = router;
|
package/routes/users.js
CHANGED
@@ -185,5 +185,29 @@ router.get('/', function (req, res) {
|
|
185
185
|
});
|
186
186
|
});
|
187
187
|
|
188
|
+
router.post('/loginemail', function (req, res) {
|
189
|
+
|
190
|
+
winston.debug("/loginemail... req.body: ", req.body);
|
191
|
+
let user_id = req.user._id;
|
192
|
+
let token = req.headers.authorization;
|
193
|
+
|
194
|
+
let project_id = req.body.id_project;
|
195
|
+
if (!project_id) {
|
196
|
+
res.status(500).send({ success: false, error: "missing 'id_project' field" })
|
197
|
+
}
|
198
|
+
|
199
|
+
User.findById(user_id, (err, user) => {
|
200
|
+
if (err) {
|
201
|
+
return res.status(404).send({ success: false, message: "No user found" });
|
202
|
+
}
|
203
|
+
winston.debug("user found: ", user);
|
204
|
+
|
205
|
+
emailService.sendEmailRedirectOnDesktop(user.email, token, project_id, user.email)
|
206
|
+
return res.status(200).send({ success: true, message: "Sending email..."})
|
207
|
+
})
|
208
|
+
|
209
|
+
|
210
|
+
})
|
211
|
+
|
188
212
|
|
189
213
|
module.exports = router;
|
package/services/emailService.js
CHANGED
@@ -1851,6 +1851,39 @@ async sendRequestTranscript(to, messages, request, project) {
|
|
1851
1851
|
|
1852
1852
|
}
|
1853
1853
|
|
1854
|
+
async sendEmailRedirectOnDesktop(to, token, project_id) {
|
1855
|
+
winston.debug("sendEmailRedirectOnDesktop: " + to);
|
1856
|
+
|
1857
|
+
var that = this;
|
1858
|
+
|
1859
|
+
let html = await this.readTemplate('redirectToDesktopEmail.html');
|
1860
|
+
|
1861
|
+
let envTemplate = process.env.EMAIL_REDIRECT_TO_DESKTOP_TEMPLATE
|
1862
|
+
winston.debug("envTemplate: " + envTemplate);
|
1863
|
+
|
1864
|
+
if (envTemplate) {
|
1865
|
+
html = envTemplate;
|
1866
|
+
}
|
1867
|
+
|
1868
|
+
winston.debug("html: " + html);
|
1869
|
+
|
1870
|
+
let template = handlebars.compile(html);
|
1871
|
+
|
1872
|
+
let baseScope = JSON.parse(JSON.stringify(that));
|
1873
|
+
delete baseScope.pass;
|
1874
|
+
|
1875
|
+
let replacements = {
|
1876
|
+
baseScope: baseScope,
|
1877
|
+
token: token,
|
1878
|
+
project_id: project_id
|
1879
|
+
}
|
1880
|
+
|
1881
|
+
html = template(replacements);
|
1882
|
+
|
1883
|
+
that.send({ to: to, subject: "Join Tiledesk from Desktop", html: html });
|
1884
|
+
|
1885
|
+
}
|
1886
|
+
|
1854
1887
|
parseText(text, payload) {
|
1855
1888
|
|
1856
1889
|
|
@@ -0,0 +1,110 @@
|
|
1
|
+
var winston = require('../config/winston');
|
2
|
+
const axios = require("axios").default;
|
3
|
+
var configGlobal = require('../config/global');
|
4
|
+
require('dotenv').config();
|
5
|
+
|
6
|
+
let openai_endpoint = process.env.OPENAI_ENDPOINT;
|
7
|
+
let kb_endpoint = process.env.KB_ENDPOINT;
|
8
|
+
|
9
|
+
class OpenaiService {
|
10
|
+
|
11
|
+
// OPEN AI
|
12
|
+
completions(data, gptkey) {
|
13
|
+
|
14
|
+
winston.debug("[OPENAI SERVICE] openai endpoint: ", openai_endpoint);
|
15
|
+
|
16
|
+
return new Promise((resolve, reject) => {
|
17
|
+
|
18
|
+
axios({
|
19
|
+
url: openai_endpoint + "/chat/completions",
|
20
|
+
headers: {
|
21
|
+
'Content-Type': 'application/json',
|
22
|
+
'Authorization': "Bearer " + gptkey
|
23
|
+
},
|
24
|
+
data: data,
|
25
|
+
method: 'POST'
|
26
|
+
}).then((resbody) => {
|
27
|
+
//winston.debug("[Openai] completions resbody: ", resbody.data);
|
28
|
+
resolve(resbody);
|
29
|
+
}).catch((err) => {
|
30
|
+
console.log("err: ", err);
|
31
|
+
// winston.error("[Openai] completions error: ", err);
|
32
|
+
reject(err);
|
33
|
+
})
|
34
|
+
|
35
|
+
})
|
36
|
+
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
// PUGLIA AI
|
41
|
+
checkStatus(data) {
|
42
|
+
winston.debug("[OPENAI SERVICE] kb endpoint: ", kb_endpoint);
|
43
|
+
|
44
|
+
return new Promise((resolve, reject) => {
|
45
|
+
|
46
|
+
axios({
|
47
|
+
url: kb_endpoint + "/scrape/status",
|
48
|
+
headers: {
|
49
|
+
'Content-Type': 'application/json'
|
50
|
+
},
|
51
|
+
data: data,
|
52
|
+
method: 'POST'
|
53
|
+
}).then((resbody) => {
|
54
|
+
resolve(resbody);
|
55
|
+
}).catch((err) => {
|
56
|
+
console.log("err: ", err);
|
57
|
+
reject(err);
|
58
|
+
})
|
59
|
+
|
60
|
+
})
|
61
|
+
}
|
62
|
+
|
63
|
+
startScrape(data) {
|
64
|
+
winston.debug("[OPENAI SERVICE] kb endpoint: ", kb_endpoint);
|
65
|
+
|
66
|
+
return new Promise((resolve, reject) => {
|
67
|
+
|
68
|
+
axios({
|
69
|
+
url: kb_endpoint + "/scrape/",
|
70
|
+
headers: {
|
71
|
+
'Content-Type': 'application/json'
|
72
|
+
},
|
73
|
+
data: data,
|
74
|
+
method: 'POST'
|
75
|
+
}).then((resbody) => {
|
76
|
+
resolve(resbody);
|
77
|
+
}).catch((err) => {
|
78
|
+
console.log("err: ", err);
|
79
|
+
reject(err);
|
80
|
+
})
|
81
|
+
|
82
|
+
})
|
83
|
+
}
|
84
|
+
|
85
|
+
ask(data) {
|
86
|
+
winston.debug("[OPENAI SERVICE] kb endpoint: ", kb_endpoint);
|
87
|
+
|
88
|
+
return new Promise((resolve, reject) => {
|
89
|
+
|
90
|
+
axios({
|
91
|
+
url: kb_endpoint + "/qa/",
|
92
|
+
headers: {
|
93
|
+
'Content-Type': 'application/json'
|
94
|
+
},
|
95
|
+
data: data,
|
96
|
+
method: 'POST'
|
97
|
+
}).then((resbody) => {
|
98
|
+
resolve(resbody);
|
99
|
+
}).catch((err) => {
|
100
|
+
console.log("err: ", err);
|
101
|
+
reject(err);
|
102
|
+
})
|
103
|
+
|
104
|
+
})
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
var openaiService = new OpenaiService();
|
109
|
+
|
110
|
+
module.exports = openaiService;
|
@@ -0,0 +1,304 @@
|
|
1
|
+
<!DOCTYPE html
|
2
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
|
4
|
+
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
|
5
|
+
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
6
|
+
|
7
|
+
<head>
|
8
|
+
<meta name="viewport" content="width=device-width" />
|
9
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
10
|
+
<title>Join Tiledesk from Desktop</title>
|
11
|
+
|
12
|
+
<style type="text/css">
|
13
|
+
img {
|
14
|
+
max-width: 100%;
|
15
|
+
text-align: center !important;
|
16
|
+
}
|
17
|
+
|
18
|
+
img.CToWUd {
|
19
|
+
margin-bottom: 16px;
|
20
|
+
max-width: 200px !important;
|
21
|
+
width: 200px !important;
|
22
|
+
min-width: 200px !important;
|
23
|
+
outline: none;
|
24
|
+
text-decoration: none;
|
25
|
+
border: none;
|
26
|
+
height: auto;
|
27
|
+
margin-left: 0px;
|
28
|
+
}
|
29
|
+
|
30
|
+
body {
|
31
|
+
-webkit-font-smoothing: antialiased;
|
32
|
+
-webkit-text-size-adjust: none;
|
33
|
+
width: 100% !important;
|
34
|
+
height: 100%;
|
35
|
+
line-height: 1.6em;
|
36
|
+
}
|
37
|
+
|
38
|
+
body {
|
39
|
+
background-color: #f6f6f6;
|
40
|
+
}
|
41
|
+
|
42
|
+
.header-image-container {
|
43
|
+
margin-top: 10px;
|
44
|
+
height: 40px;
|
45
|
+
background-image: url("https://tiledesk.com/wp-content/uploads/2022/08/tiledesk_v1-1.png");
|
46
|
+
background-size: contain;
|
47
|
+
background-position: center;
|
48
|
+
background-repeat: no-repeat;
|
49
|
+
}
|
50
|
+
|
51
|
+
@media only screen and (max-width: 640px) {
|
52
|
+
body {
|
53
|
+
padding: 0 !important;
|
54
|
+
}
|
55
|
+
|
56
|
+
h1 {
|
57
|
+
font-weight: 800 !important;
|
58
|
+
margin: 20px 0 5px !important;
|
59
|
+
text-align: center !important;
|
60
|
+
}
|
61
|
+
|
62
|
+
h2 {
|
63
|
+
font-weight: 800 !important;
|
64
|
+
margin: 20px 0 5px !important;
|
65
|
+
}
|
66
|
+
|
67
|
+
h3 {
|
68
|
+
font-weight: 800 !important;
|
69
|
+
margin: 20px 0 5px !important;
|
70
|
+
}
|
71
|
+
|
72
|
+
h4 {
|
73
|
+
font-weight: 800 !important;
|
74
|
+
margin: 20px 0 5px !important;
|
75
|
+
}
|
76
|
+
|
77
|
+
h1 {
|
78
|
+
font-size: 22px !important;
|
79
|
+
}
|
80
|
+
|
81
|
+
h2 {
|
82
|
+
font-size: 18px !important;
|
83
|
+
}
|
84
|
+
|
85
|
+
h3 {
|
86
|
+
font-size: 16px !important;
|
87
|
+
}
|
88
|
+
|
89
|
+
.container {
|
90
|
+
padding: 0 !important;
|
91
|
+
width: 100% !important;
|
92
|
+
}
|
93
|
+
|
94
|
+
.content {
|
95
|
+
padding: 0 !important;
|
96
|
+
}
|
97
|
+
|
98
|
+
.content-wrap {
|
99
|
+
padding: 10px !important;
|
100
|
+
}
|
101
|
+
|
102
|
+
.invoice {
|
103
|
+
width: 100% !important;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
</style>
|
107
|
+
</head>
|
108
|
+
|
109
|
+
<body itemscope itemtype="http://schema.org/EmailMessage"
|
110
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;"
|
111
|
+
bgcolor="#f6f6f6">
|
112
|
+
|
113
|
+
<table class="body-wrap"
|
114
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"
|
115
|
+
bgcolor="#f6f6f6">
|
116
|
+
<tr
|
117
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
118
|
+
<td
|
119
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
|
120
|
+
valign="top"></td>
|
121
|
+
<td class="container" width="600"
|
122
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"
|
123
|
+
valign="top">
|
124
|
+
<div class="content"
|
125
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
|
126
|
+
|
127
|
+
<table class="main" width="100%" cellpadding="0" cellspacing="0"
|
128
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;"
|
129
|
+
bgcolor="#fff">
|
130
|
+
|
131
|
+
|
132
|
+
<tr
|
133
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
134
|
+
|
135
|
+
<td class="alert alert-warning"
|
136
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; margin: 0;"
|
137
|
+
align="center" valign="top">
|
138
|
+
<div>
|
139
|
+
<div class="header-image-container"></div>
|
140
|
+
<h2 style="margin-top: 40px;">Get Ready for a Full Tiledesk Experience!</h2>
|
141
|
+
</div>
|
142
|
+
|
143
|
+
</td>
|
144
|
+
</tr>
|
145
|
+
|
146
|
+
<tr
|
147
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
148
|
+
<td class="content-wrap"
|
149
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"
|
150
|
+
valign="top">
|
151
|
+
<table width="100%" cellpadding="0" cellspacing="0"
|
152
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
153
|
+
|
154
|
+
|
155
|
+
<tr
|
156
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
157
|
+
<td class="content-block">
|
158
|
+
<div style="text-align: center;">
|
159
|
+
For the optimal Tiledesk experience, we recommend using it on a desktop device. We've made
|
160
|
+
access easier for you with a personalized link.
|
161
|
+
|
162
|
+
<div style="margin-top: 20px; font-weight: 600;">Access Tiledesk on your desktop now 👇</div>
|
163
|
+
|
164
|
+
<div style="margin-top: 10px;">
|
165
|
+
<a href="{{baseScope.baseUrl}}/#/project/{{project_id}}/home?token={{token}}"
|
166
|
+
style=" background-color: #ff8574 !important; border: none; color: white; padding: 6px 18px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; font-weight: 600; letter-spacing: 1px; margin: 4px 2px; cursor: pointer; border-radius: 8px;">
|
167
|
+
Enjoy Tiledesk
|
168
|
+
</a>
|
169
|
+
</div>
|
170
|
+
|
171
|
+
</div>
|
172
|
+
|
173
|
+
<div style="margin-top: 40px;">
|
174
|
+
Alternatively, simply copy and paste this URL into your browser:
|
175
|
+
<div>
|
176
|
+
<a
|
177
|
+
href="{{baseScope.baseUrl}}/#/project/{{project_id}}/home?token={{token}}">
|
178
|
+
{{baseScope.baseUrl}}/#/project/{{project_id}}/home
|
179
|
+
</a>
|
180
|
+
</div>
|
181
|
+
</div>
|
182
|
+
|
183
|
+
</td>
|
184
|
+
</tr>
|
185
|
+
|
186
|
+
<tr
|
187
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
188
|
+
<td class="content-block"
|
189
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
|
190
|
+
valign="top">
|
191
|
+
</td>
|
192
|
+
</tr>
|
193
|
+
</table>
|
194
|
+
</td>
|
195
|
+
</tr>
|
196
|
+
|
197
|
+
<tr>
|
198
|
+
<td>
|
199
|
+
<hr style="width:94%;height:1px;border:none;background-color: #cacaca;">
|
200
|
+
|
201
|
+
<div style="display: flex; padding: 20px 18px; color: #888888; align-items: center;">
|
202
|
+
<span>Powered by </span>
|
203
|
+
<span style="display: flex;"><img
|
204
|
+
src="https://tiledesk.com/wp-content/uploads/2023/05/tiledesk-solo_logo_new_gray.png" width="15"
|
205
|
+
height="15" style="margin-left: 6px; margin-top: 2px;" /></span>
|
206
|
+
<span style="font-weight: bold; margin-left: 2px;">Tiledesk</span>
|
207
|
+
</div>
|
208
|
+
|
209
|
+
</td>
|
210
|
+
</tr>
|
211
|
+
|
212
|
+
|
213
|
+
</table>
|
214
|
+
<div class="footer"
|
215
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
|
216
|
+
<table width="100%"
|
217
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
218
|
+
|
219
|
+
<!-- <tr
|
220
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
221
|
+
<td class="aligncenter content-block"
|
222
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0;"
|
223
|
+
align="center" valign="top">
|
224
|
+
|
225
|
+
<div style="margin-bottom: 10px; font-style: italic;">Follow us on:</div>
|
226
|
+
<div style="display: flex; flex-direction: row;">
|
227
|
+
|
228
|
+
<a href="https://www.facebook.com/tiledesk" target="_blank">
|
229
|
+
<div
|
230
|
+
style="border-color: #3d6ad6; border: solid 1px; border-radius: 14px; padding: 4px; margin: 0px 6px; cursor: pointer;">
|
231
|
+
<img
|
232
|
+
src="https://github.com/Tiledesk/tiledesk-dashboard/assets/45603238/f762a3d7-541a-4b78-ae33-c35cdbe3b083"
|
233
|
+
width="20px" height="20px" />
|
234
|
+
</div>
|
235
|
+
</a>
|
236
|
+
|
237
|
+
<a href="https://www.youtube.com/@tiledesk" target="_blank">
|
238
|
+
<div
|
239
|
+
style="border-color: #e7332f; border: solid 1px; border-radius: 14px; padding: 4px; margin: 0px 6px; cursor: pointer;">
|
240
|
+
<img
|
241
|
+
src="https://github.com/Tiledesk/tiledesk-dashboard/assets/45603238/5dc5be2d-8bd3-43cf-b1ca-f12a36c01f1a"
|
242
|
+
width="20px" height="20px" />
|
243
|
+
</div>
|
244
|
+
</a>
|
245
|
+
|
246
|
+
<a href="https://www.linkedin.com/company/tiledesk/" target="_blank">
|
247
|
+
<div
|
248
|
+
style="border-color: #1f77b5; border: solid 1px; border-radius: 14px; padding: 4px; margin: 0px 6px; cursor: pointer;">
|
249
|
+
<img
|
250
|
+
src="https://github.com/Tiledesk/tiledesk-dashboard/assets/45603238/52d8909f-c847-4b44-8dfb-7cd041e481c3"
|
251
|
+
width="20px" height="20px" />
|
252
|
+
</div>
|
253
|
+
</a>
|
254
|
+
|
255
|
+
<a href="https://www.instagram.com/tiledesk/" target="_blank">
|
256
|
+
<div
|
257
|
+
style="border-color: #f78881; border: solid 1px; border-radius: 14px; padding: 4px; margin: 0px 6px; cursor: pointer;">
|
258
|
+
<img
|
259
|
+
src="https://github.com/Tiledesk/tiledesk-dashboard/assets/45603238/4c35afe2-277a-4fdd-8e50-0844148216c8"
|
260
|
+
width="20px" height="20px" />
|
261
|
+
</div>
|
262
|
+
</a>
|
263
|
+
|
264
|
+
<a href="https://twitter.com/tiledesk" target="_blank">
|
265
|
+
<div
|
266
|
+
style="border-color: #2ea1f2; border: solid 1px; border-radius: 14px; padding: 4px; margin: 0px 6px; cursor: pointer;">
|
267
|
+
<img
|
268
|
+
src="https://github.com/Tiledesk/tiledesk-dashboard/assets/45603238/3288635e-50b6-4b2d-bccc-0c9313dd11a5"
|
269
|
+
width="20px" height="20px" />
|
270
|
+
</div>
|
271
|
+
</a>
|
272
|
+
|
273
|
+
</div>
|
274
|
+
|
275
|
+
</td>
|
276
|
+
</tr> -->
|
277
|
+
|
278
|
+
<tr
|
279
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
280
|
+
<td class="aligncenter content-block"
|
281
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0;"
|
282
|
+
align="center" valign="top">
|
283
|
+
<span><a href="http://www.tiledesk.com"
|
284
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">
|
285
|
+
Tiledesk.com </a></span>
|
286
|
+
<br><span><a href="%unsubscribe_url%"
|
287
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">Unsubscribe</a></span>
|
288
|
+
</td>
|
289
|
+
</tr>
|
290
|
+
</table>
|
291
|
+
|
292
|
+
|
293
|
+
</div>
|
294
|
+
</div>
|
295
|
+
</td>
|
296
|
+
<td
|
297
|
+
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
|
298
|
+
valign="top"></td>
|
299
|
+
</tr>
|
300
|
+
</table>
|
301
|
+
|
302
|
+
</body>
|
303
|
+
|
304
|
+
</html>
|