This commit is contained in:
2024-01-17 17:31:41 +08:00
parent 84190ed9f1
commit fb9e920c15
35 changed files with 3003 additions and 1304 deletions

View File

@ -5,26 +5,23 @@ import { createBox, appendMedia } from "./lib";
let r = lang;
export default class CustomerRecordComment {
#container;
#option;
#enter;
#message;
_var = {};
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
@ -32,28 +29,28 @@ export default class CustomerRecordComment {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
}
/**
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag === true;
this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : '';
this._var.enter.disabled = flag === true;
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : '';
}
create() {
const readonly = this.#option.readonly;
const readonly = this._var.option.readonly;
const container = createBox(
createElement('div', null,
createElement('div', div => {
@ -65,7 +62,7 @@ export default class CustomerRecordComment {
createElement('button', button => {
button.className = 'roundbtn button-close';
button.style.backgroundColor = 'transparent';
if (this.#option.hasClose !== true) {
if (this._var.option.hasClose !== true) {
button.style.display = 'none';
return;
}
@ -73,8 +70,8 @@ export default class CustomerRecordComment {
fill: '#000'
}));
button.addEventListener('click', () => {
if (typeof this.#option.onClose === 'function') {
this.#option.onClose();
if (typeof this._var.option.onClose === 'function') {
this._var.option.onClose();
}
})
})
@ -83,16 +80,16 @@ export default class CustomerRecordComment {
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.maxLength = this._var.option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
if (readonly === true) {
enter.disabled = true;
}
this.#enter = enter;
this._var.enter = enter;
container.appendChild(
createElement('div', 'message-bar',
enter,
@ -108,8 +105,8 @@ export default class CustomerRecordComment {
// setTooltip(button, r('P_M3_SENDMESSAGE', 'Send Message'));
setTooltip(button, r('P_CU_POSTNOTE', 'Post Note'));
button.addEventListener('click', () => {
if (typeof this.#option.onAddComment === 'function') {
this.#option.onAddComment(this.text);
if (typeof this._var.option.onAddComment === 'function') {
this._var.option.onAddComment(this.text);
}
})
})
@ -118,9 +115,9 @@ export default class CustomerRecordComment {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
load(data) {
@ -129,7 +126,7 @@ export default class CustomerRecordComment {
for (let comment of data) {
const div = createElement('div', 'item-div');
// if (sendto !== '') {
// sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
// sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + `\n${sendto}`;
// }
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
@ -137,8 +134,8 @@ export default class CustomerRecordComment {
}));
const content = createElement('div', 'item-content');
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && comment.MMSParts?.length > 0) {
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Comment)), mmsParts));
if (comment.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comment.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
@ -155,8 +152,8 @@ export default class CustomerRecordComment {
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@ -4,11 +4,10 @@ import { isEmail, nullOrEmpty, r as lang } from "../../utility";
let r = lang;
export class Contact {
#option;
#refs;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@ -18,7 +17,7 @@ export class Contact {
async show(parent = document.body) {
const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3;
const c = this.#option.contact;
const c = this._var.option.contact;
const contactName = createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
@ -54,7 +53,7 @@ export class Contact {
txt.style.height = '100px';
});
const buttons = [];
if (this.#option.company) {
if (this._var.option.company) {
buttons.push({
text: c == null ? r('P_WO_ADDCONTACTRECORD', 'Add Contact Record') : r('P_WO_EDITCONTACTRECORD', 'Edit Contact Record'),
// tabIndex: tabIndex + 7,
@ -64,8 +63,8 @@ export class Contact {
return false;
}
item.SaveToCustomer = 1;
if (typeof this.#option.onSave === 'function') {
return this.#option.onSave.call(this, item, 'customerrecord');
if (typeof this._var.option.onSave === 'function') {
return this._var.option.onSave.call(this, item, 'customerrecord');
}
}
});
@ -81,8 +80,8 @@ export class Contact {
}
//item.Id = -1;
item.SaveToCustomer = 0;
if (typeof this.#option.onSave === 'function') {
return this.#option.onSave.call(this, item, 'workorder');
if (typeof this._var.option.onSave === 'function') {
return this._var.option.onSave.call(this, item, 'workorder');
}
}
},
@ -92,7 +91,7 @@ export class Contact {
}
);
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title: c == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'),
content: createElement('div', wrapper => {
wrapper.className = 'setting-wrapper';
@ -135,7 +134,7 @@ export class Contact {
} else {
preferences.select('0');
}
this.#refs = {
this._var.refs = {
contactName,
preferences,
contactEmail,
@ -149,35 +148,35 @@ export class Contact {
}
prepare() {
const name = this.#refs.contactName.value;
const pref = this.#refs.preferences.selected.value;
const email = this.#refs.contactEmail.value;
const phone = this.#refs.contactMobile.value;
const opt = this.#refs.checkOpt.querySelector('input').checked;
const notes = this.#refs.contactNotes.value;
const title = this.#option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
const name = this._var.refs.contactName.value;
const pref = this._var.refs.preferences.selected.value;
const email = this._var.refs.contactEmail.value;
const phone = this._var.refs.contactMobile.value;
const opt = this._var.refs.checkOpt.querySelector('input').checked;
const notes = this._var.refs.contactNotes.value;
const title = this._var.option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
if (nullOrEmpty(name)) {
showAlert(title, r('P_CR_CONTACTNAMECANNOTBEEMPTY', 'Contact Name cannot be empty.'), 'warn')
.then(() => this.#refs.contactName.focus());
.then(() => this._var.refs.contactName.focus());
return null;
}
if ((pref == 0 || pref == 2) && nullOrEmpty(phone)) {
showAlert(title, r('P_CR_MOBILECANNOTBEEMPTY', 'Mobile cannot be empty.'), 'warn')
.then(() => this.#refs.contactMobile.focus());
.then(() => this._var.refs.contactMobile.focus());
return null;
}
if (pref == 1 && nullOrEmpty(email)) {
showAlert(title, r('P_CU_EMAILCANNOTBEEMPTY', 'Email cannot be empty.'), 'warn')
.then(() => this.#refs.contactEmail.focus());
.then(() => this._var.refs.contactEmail.focus());
return null;
}
if (!nullOrEmpty(email) && !isEmail(email)) {
showAlert(title, r('P_CR_EMAILISNOTAVALIDEMAILADDRESS', 'The email address is invalid.'), 'warn')
.then(() => this.#refs.contactEmail.focus());
.then(() => this._var.refs.contactEmail.focus());
return null;
}
let contact = this.#option.contact;
let contact = this._var.option.contact;
if (contact == null) {
contact = {};
} else if (contact.OptOut !== opt) {
@ -198,11 +197,10 @@ export class Contact {
}
export class CustomerRecordContact {
#option;
#grid;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@ -214,7 +212,7 @@ export class CustomerRecordContact {
const gridContainer = createElement('div', 'selcontact-grid');
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title,
content: createElement('div', 'selcontact-wrapper',
gridContainer
@ -224,8 +222,8 @@ export class CustomerRecordContact {
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
if (typeof this.#option.onOk === 'function') {
return this.#option.onOk.call(this, this.#grid.source.filter(f => f.selected));
if (typeof this._var.option.onOk === 'function') {
return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.selected));
}
}
},
@ -250,16 +248,16 @@ export class CustomerRecordContact {
{ key: 'Notes', caption: r("P_CR_NOTES", "Notes"), width: 120 }
];
grid.init();
grid.source = this.#option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this.#grid = grid;
grid.source = this._var.option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid = grid;
return result;
}
set source(contacts) {
this.#option.contacts = contacts;
const grid = this.#grid;
this._var.option.contacts = contacts;
const grid = this._var.grid;
if (grid != null) {
this.#grid.source = contacts;
this._var.grid.source = contacts;
}
}
}

View File

@ -1,6 +1,6 @@
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup } from "../../ui";
import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../../utility";
import { createBox, appendMedia, fileSupported, insertFile } from "./lib";
import { createBox, appendMedia, fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
import { Contact, CustomerRecordContact } from "./contact";
import Follower from "./follower";
@ -31,31 +31,22 @@ class NoteCol extends GridColumn {
let r = lang;
export default class CustomerCommunication {
#container;
#option;
#contacts;
#followers;
#buttonFollower;
#enter;
#fileControl;
#file;
#message;
#data = {};
#gridContact;
#gridWo;
_var = {
data: {}
};
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get #autoUpdates() { return this.#container.querySelector('.check-auto-update>input') }
get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true }
get _autoUpdates() { return this._var.container.querySelector('.check-auto-update>input') }
get autoUpdatesEnabled() { return this._autoUpdates?.disabled !== true }
set autoUpdatesEnabled(flag) {
const element = this.#autoUpdates;
const element = this._autoUpdates;
if (element == null) {
return;
}
@ -67,9 +58,9 @@ export default class CustomerCommunication {
element.parentElement?.classList?.remove('disabled');
}
}
get autoUpdates() { return this.#autoUpdates?.checked }
get autoUpdates() { return this._autoUpdates?.checked }
set autoUpdates(flag) {
const element = this.#autoUpdates;
const element = this._autoUpdates;
if (element == null) {
return;
}
@ -77,10 +68,10 @@ export default class CustomerCommunication {
element.dispatchEvent(new Event('change'));
}
get #statusLink() { return this.#container.querySelector('.check-status-link>input') }
get statusLinkEnabled() { return this.#statusLink?.disabled !== true }
get _statusLink() { return this._var.container.querySelector('.check-status-link>input') }
get statusLinkEnabled() { return this._statusLink?.disabled !== true }
set statusLinkEnabled(flag) {
const element = this.#statusLink;
const element = this._statusLink;
if (element == null) {
return;
}
@ -92,9 +83,9 @@ export default class CustomerCommunication {
element.parentElement?.classList?.remove('disabled');
}
}
get statusLink() { return this.#statusLink?.checked }
get statusLink() { return this._statusLink?.checked }
set statusLink(flag) {
const element = this.#statusLink;
const element = this._statusLink;
if (element == null) {
return;
}
@ -106,33 +97,33 @@ export default class CustomerCommunication {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.customer-name>.ui-input').disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-edit-contacts').disabled = flag;
this.#container.querySelector('.button-edit-followers').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.customer-name>.ui-input').disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
this._var.container.querySelector('.button-edit-contacts').disabled = flag;
this._var.container.querySelector('.button-edit-followers').disabled = flag;
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
get file() { return this._var.file || this._var.fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
this._var.fileControl?.remove();
const label = this._var.container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
this._var.fileControl = null;
this._var.file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
@ -140,15 +131,15 @@ export default class CustomerCommunication {
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
this._var.fileControl = f;
this._var.file = f.files[0];
const link = this._var.container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
this._var.fileControl = null;
this._var.file = f;
}
if (label != null) {
label.style.display = '';
@ -158,9 +149,9 @@ export default class CustomerCommunication {
}
}
get customerName() { return this.#container.querySelector('.customer-name>.ui-input')?.value }
get customerName() { return this._var.container.querySelector('.customer-name>.ui-input')?.value }
set customerName(name) {
const element = this.#container.querySelector('.customer-name>.ui-input');
const element = this._var.container.querySelector('.customer-name>.ui-input');
if (element == null) {
return;
}
@ -168,7 +159,7 @@ export default class CustomerCommunication {
}
get contacts() {
return [...this.#contacts.children].filter(el => {
return [...this._var.contacts.children].filter(el => {
return el.querySelector('span').dataset.notsend == "false";
}).map(el => {
const span = el.querySelector('span');
@ -176,11 +167,20 @@ export default class CustomerCommunication {
});
}
set contacts(contacts) {
this.#contacts.replaceChildren();
this.setData('contacts', contacts);
this._var.contacts.replaceChildren();
if (contacts?.length > 0) {
var cs = contacts.sort(function (a, b) {
if (a.Name == b.Name) return 0; return a.Name > b.Name ? 1 : -1;
if (a.Name == b.Name) {
return 0;
}
return a.Name > b.Name ? 1 : -1;
});
const messages = this._var.data.messages;
if (this._var.contactsUpdated !== true && messages?.length > 0) {
updateCustomerName(messages, contacts);
this._var.contactsUpdated = true;
}
for (let c of cs) {
//if (c.OptOut || c.OptOut_BC || c.selected === false) {
// continue;
@ -195,19 +195,28 @@ export default class CustomerCommunication {
const to = pref === '1' ? email : mp;
let icon;
let method;
let tipstr;
if (c.OptOut || c.OptOut_BC || c.selected === false) {
icon = 'times';
method = r('P_CU_OPTEDOUT_COLON', 'Opted Out:');
tipstr = r('P_CU_OPTEDOUT_PROMPT', 'User has opted out of messages');
}
else {
switch (pref) {
case '0':
icon = 'comment-lines';
if (c.MobilePhoneStatus !== 0) {
icon = 'times';
if (c.MobilePhoneStatus === 412) {
// landline
tipstr = r('P_CU_LANDLINE', 'Landline');
}
} else {
icon = 'comment-lines';
}
method = r('P_CU_TEXTSTO_COLON', 'Texts to:');
break;
case '2':
icon = 'mobile';
method = r('P_CU_CALLSTO_COLON', 'Calls to:');
icon = 'phone';
tipstr = r('P_CU_NOMESSAGE', 'No Messages Sent');
break;
default:
icon = 'envelope';
@ -225,14 +234,14 @@ export default class CustomerCommunication {
createIcon('fa-light', icon, { 'fill': (c.OptOut || c.OptOut_BC || c.selected === false) ? 'red' : '' }),
span
);
this.#contacts.appendChild(item);
let tip = `${method} ${to}`;
this._var.contacts.appendChild(item);
let tip = tipstr || `${method} ${to}`;
if (span.scrollWidth > span.offsetWidth) {
tip = r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}\n${tip}`;
}
setTooltip(span, tip);
}
this.#message.scrollTop = this.#message.scrollHeight
this._var.message.scrollTop = this._var.message.scrollHeight
}
}
@ -240,11 +249,11 @@ export default class CustomerCommunication {
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
const link = this.#container.querySelector('.check-status-link');
const link = this._var.container.querySelector('.check-status-link');
if (flag === true) {
link.classList.add('disabled');
} else {
@ -252,31 +261,31 @@ export default class CustomerCommunication {
}
link.querySelector('input').disabled = flag;
const display = flag === true ? 'none' : '';
this.#container.querySelector('.button-edit-contacts').style.display = display;
this.#container.querySelector('.button-edit-followers').style.display = display;
// this.#enter.disabled = flag === true;
this.#container.querySelector('.message-bar').style.display = display;
// this.#container.querySelector('.button-send-message').style.display = display;
this._var.container.querySelector('.button-edit-contacts').style.display = display;
this._var.container.querySelector('.button-edit-followers').style.display = display;
// this._var.enter.disabled = flag === true;
this._var.container.querySelector('.message-bar').style.display = display;
// this._var.container.querySelector('.button-send-message').style.display = display;
}
/**
* @param {boolean} flag
*/
set recordReadonly(flag) {
this.#option.recordReadonly = flag;
if (this.#container == null) {
this._var.option.recordReadonly = flag;
if (this._var.container == null) {
return;
}
this.#container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
}
/**
* @param {String} name
*/
set companyName(name) {
this.#option.companyName = name;
const div = this.#container.querySelector('.title-company');
this._var.option.companyName = name;
const div = this._var.container.querySelector('.title-company');
if (nullOrEmpty(name)) {
div.style.display = 'none';
} else {
@ -288,9 +297,9 @@ export default class CustomerCommunication {
* @param {String} code
*/
set companyCode(code) {
const option = this.#option;
const option = this._var.option;
option.companyCode = code;
const div = this.#container.querySelector('.title-company');
const div = this._var.container.querySelector('.title-company');
if (nullOrEmpty(option.companyName)) {
div.style.display = 'none';
} else {
@ -302,18 +311,18 @@ export default class CustomerCommunication {
}
get followers() {
return [...this.#followers.children].map(el => {
return [...this._var.followers.children].map(el => {
const span = el.querySelector('span');
return { 'Email': span.dataset.email, 'MobilePhone': span.dataset.mp, 'Value': span.dataset.name };
});
}
set followers(followers) {
this.#data.followers = followers;
this.#followers.replaceChildren();
this._var.data.followers = followers;
this._var.followers.replaceChildren();
if (followers?.length > 0) {
this.#container.querySelector('.follower-bar').style.display = '';
setTooltip(this.#buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
this.#container.querySelector('.follower-bar>.bar-list').appendChild(this.#buttonFollower);
this._var.container.querySelector('.follower-bar').style.display = '';
setTooltip(this._var.buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
this._var.container.querySelector('.follower-bar>.bar-list').appendChild(this._var.buttonFollower);
for (let f of followers) {
if (f.OptOut) {
continue;
@ -326,7 +335,7 @@ export default class CustomerCommunication {
tips.push(r('P_CU_EMAILSTO_COLON', 'Emails to:') + ` ${email}`);
}
if (f.SendText) {
tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:' + ` ${mpDisplay}`));
tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:') + ` ${mpDisplay}`);
}
let icon;
if (f.SendText && f.SendEmail) {
@ -348,26 +357,26 @@ export default class CustomerCommunication {
createIcon('fa-light', icon),
span
);
this.#followers.appendChild(item);
this._var.followers.appendChild(item);
if (span.scrollWidth > span.offsetWidth) {
tips.splice(0, 0, r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}`);
}
setTooltip(span, tips.join('\n'));
}
} else {
this.#container.querySelector('.follower-bar').style.display = 'none';
setTooltip(this.#buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers'));
this.#container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this.#buttonFollower)
this._var.container.querySelector('.follower-bar').style.display = 'none';
setTooltip(this._var.buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers'));
this._var.container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this._var.buttonFollower)
}
this.#message.scrollTop = this.#message.scrollHeight
this._var.message.scrollTop = this._var.message.scrollHeight
}
setData(key, data) {
this.#data[key] = data;
this._var.data[key] = data;
}
create() {
const option = this.#option;
const option = this._var.option;
const readonly = option.readonly;
// functions
const checkAutoUpdate = createCheckbox({
@ -428,9 +437,9 @@ export default class CustomerCommunication {
]
);
// contacts
this.#contacts = this.#createContacts(container, option);
this._var.contacts = this._createContacts(container, option);
// followers
this.#followers = this.#createFollowers(container, option);
this._var.followers = this._createFollowers(container, option);
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERMESSAGEHERE', 'Enter Message Here');
@ -442,7 +451,7 @@ export default class CustomerCommunication {
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
enter.addEventListener('paste', e => {
if (option.customerNameVisible === true) {
@ -466,7 +475,7 @@ export default class CustomerCommunication {
// this.file = insertFile(container, file, r);
// }
});
this.#enter = enter;
this._var.enter = enter;
container.appendChild(
createElement('div', div => {
div.className = 'message-bar';
@ -498,52 +507,49 @@ export default class CustomerCommunication {
},
enter,
createElement('div', div => div.style.textAlign = 'right',
createElement('div', div => {
div.className = 'customer-name';
if (option.customerNameVisible !== true) {
div.style.display = 'none';
}
},
createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')),
createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
})
),
createElement('div', selector => {
selector.className = 'file-selector';
if (option.customerNameVisible === true) {
selector.style.display = 'none';
}
},
createElement('div', 'customer-left',
createElement('div', div => {
div.className = 'selector-link';
div.addEventListener('click', () => {
this.#fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this.#fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
div.className = 'customer-name';
if (option.customerNameVisible !== true) {
div.style.display = 'none';
}
},
createIcon('fa-regular', 'link')
createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')),
createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
})
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
createElement('div', 'file-selector',
createElement('div', div => {
div.className = 'selector-link';
div.addEventListener('click', () => {
this._var.fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this._var.fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
)
),
createElement('div', 'prompt-count'),
createElement('button', button => {
@ -575,12 +581,12 @@ export default class CustomerCommunication {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
#createContacts(container, option) {
_createContacts(container, option) {
const readonly = option.readonly;
const recordReadonly = option.recordReadonly;
const contacts = createElement('div', 'bar-list-container');
@ -616,6 +622,7 @@ export default class CustomerCommunication {
createElement('div', div => div.innerText = r('P_CU_EDITCONTACTS', 'Edit Contacts')),
createElement('div', div => {
div.className = 'title-company';
div.style.maxWidth = '540px';
if (nullOrEmpty(option.companyName)) {
div.style.display = 'none';
} else {
@ -652,8 +659,8 @@ export default class CustomerCommunication {
});
const result = option.onSelectCRContacts(list);
}
const r = this.#data.contacts;
this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
const r = this._var.data.contacts;
this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -662,7 +669,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -681,7 +688,7 @@ export default class CustomerCommunication {
if (typeof result?.then === 'function') {
return result.then(r => {
r.map(c => {
for (let cc of this.#data.contacts) {
for (let cc of this._var.data.contacts) {
if (c.Id === cc.Id) {
c.selected = true;
break;
@ -692,7 +699,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#data.contacts.filter(c => c.Id >= 0).map(c => {
this._var.data.contacts.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -729,7 +736,7 @@ export default class CustomerCommunication {
// onMasking: option.onMasking,
company: !nullOrEmpty(option.companyName),
onSave: item => {
const exists = this.#gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone);
const exists = this._var.gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone);
if (exists) {
showAlert(r('P_CR_ADDCONTACT', 'Add Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn');
return false;
@ -738,7 +745,7 @@ export default class CustomerCommunication {
const result = option.onSave(item, true);
if (typeof result?.then === 'function') {
return result.then(r => {
this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -747,7 +754,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -807,7 +814,7 @@ export default class CustomerCommunication {
enabled: item => !item.OptOut && !item.OptOut_BC,
onchanged: function () {
if (typeof option.onChanged === 'function') {
option.onChanged([...This.#gridContact.source, ...This.#gridWo.source]);
option.onChanged([...This._var.gridContact.source, ...This._var.gridWo.source]);
}
},
tooltip: item => item.selected ? r('P_CU_OPTEDIN', 'Opted In') : r('P_CU_OPTEDOUT', 'Opted Out')
@ -850,8 +857,8 @@ export default class CustomerCommunication {
company: !nullOrEmpty(option.companyName),
onSave: (item, _op) => {
const exists =
This.#gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) ||
This.#gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone);
This._var.gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) ||
This._var.gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone);
if (exists) {
showAlert(r('P_CR_EDITCONTACT', 'Edit Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn');
return false;
@ -860,7 +867,7 @@ export default class CustomerCommunication {
const result = option.onSave(item);
if (typeof result?.then === 'function') {
return result.then(r => {
This.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
This._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -869,7 +876,7 @@ export default class CustomerCommunication {
}
return c;
});
This.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
This._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -957,7 +964,7 @@ export default class CustomerCommunication {
}
];
grid.init(pop.container.querySelector('.contacts-record'));
const customerRecords = this.#data.contacts.filter(c => c.Id >= 0).map(c => {
const customerRecords = this._var.data.contacts.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -969,11 +976,11 @@ export default class CustomerCommunication {
grid.extraRows = customerRecords.filter(c => !nullOrEmpty(c.Notes)).length;
grid.source = customerRecords;
grid.selectedRowChanged = index => {
if (index >= 0 && this.#gridWo.selectedIndexes?.length > 0) {
this.#gridWo.selectedIndexes = [];
if (index >= 0 && this._var.gridWo.selectedIndexes?.length > 0) {
this._var.gridWo.selectedIndexes = [];
}
};
this.#gridContact = grid;
this._var.gridContact = grid;
// contacts from work order only
const gridWo = new Grid();
@ -1016,7 +1023,7 @@ export default class CustomerCommunication {
}
];
gridWo.init(pop.container.querySelector('.contacts-wo'));
const workOrderOnly = this.#data.contacts.filter(c => c.Id < 0).map(c => {
const workOrderOnly = this._var.data.contacts.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -1028,11 +1035,11 @@ export default class CustomerCommunication {
gridWo.extraRows = workOrderOnly.filter(c => !nullOrEmpty(c.Notes)).length;
gridWo.source = workOrderOnly;
gridWo.selectedRowChanged = index => {
if (index >= 0 && this.#gridContact.selectedIndexes?.length > 0) {
this.#gridContact.selectedIndexes = [];
if (index >= 0 && this._var.gridContact.selectedIndexes?.length > 0) {
this._var.gridContact.selectedIndexes = [];
}
};
this.#gridWo = gridWo;
this._var.gridWo = gridWo;
});
});
})
@ -1072,7 +1079,7 @@ export default class CustomerCommunication {
return contacts;
}
#createFollowers(container, option) {
_createFollowers(container, option) {
const readonly = option.readonly;
const recordReadonly = option.recordReadonly;
const followers = createElement('div', 'bar-list-container');
@ -1086,7 +1093,7 @@ export default class CustomerCommunication {
setTooltip(button, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
button.addEventListener('click', () => {
if (typeof option.onInitFollower === 'function') {
option.onInitFollower(this.#data.followers).then(data => {
option.onInitFollower(this._var.data.followers).then(data => {
if (typeof data === 'string') {
showAlert(r('P_CUSTOMERRECORD', 'Customer Record'), data, 'warn');
return;
@ -1108,13 +1115,13 @@ export default class CustomerCommunication {
}
}
});
var title = this.#data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers');
var title = this._var.data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers');
add.show(title, container);
});
}
});
});
this.#buttonFollower = buttonEditFollower;
this._var.buttonFollower = buttonEditFollower;
container.append(
createElement('div', div => {
div.className = 'contact-bar follower-bar';
@ -1141,8 +1148,16 @@ export default class CustomerCommunication {
load(data, contacts, followers) {
const children = [];
if (data?.length > 0) {
contacts ??= this.#data.contacts;
followers ??= this.#data.allfollowers;
contacts ??= this._var.data.contacts;
followers ??= this._var.data.allfollowers;
this.setData('messages', data);
if (this._var.contactsUpdated !== true) {
const contacts = this._var.data.contacts;
if (contacts?.length > 0) {
updateCustomerName(data, contacts);
this._var.contactsUpdated = true;
}
}
for (let comm of data) {
const div = createElement('div', 'item-div');
let name;
@ -1153,32 +1168,7 @@ export default class CustomerCommunication {
name = c?.Name;
}
name ??= comm.IsReply && String(comm.FormatSender) !== '' ? comm.FormatSender : comm.Sender;
let sendto = '';
if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) {
for (let oriph of comm.OriPhoneNumbers) {
let cname;
const email = isEmail(oriph);
if (contacts?.length > 0) {
let c = email ?
contacts.find(c => c.Email === oriph) :
contacts.find(c => c.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
} else if (followers?.length > 0) {
c = email ?
followers.find(f => f.Email === oriph) :
followers.find(f => f.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
}
}
}
sendto += (cname ?? oriph) + '\n';
}
}
if (sendto !== '') {
sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
}
const sendto = getMessageSendTo(comm, contacts, followers, r)
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
div.innerText = name;
@ -1190,13 +1180,13 @@ export default class CustomerCommunication {
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => {
if (/https?:\/\//i.test(comm.Message)) {
span.innerHTML = escapeEmoji(formatUrl(comm.Message));
span.innerHTML = formatUrl(escapeEmoji(comm.Message));
} else {
span.innerText = escapeEmoji(comm.Message);
}
span.appendChild(mmsParts);
}));
if (comm.IsMMS && comm.MMSParts?.length > 0) {
if (comm.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comm.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
@ -1206,39 +1196,16 @@ export default class CustomerCommunication {
div.classList.add('item-other');
} else {
div.classList.add('item-self');
const [status, statusmsg] = this.#getMessageStatus(comm);
const [status, text, color, tips] = getMessageStatus(comm, r, this._var);
if (status !== -100) {
let statustext;
switch (status) {
case 0:
statustext = r('P_CU_PENDING', 'Pending');
content.style.backgroundColor = '#ffc107';
break;
case 1:
statustext = r('P_WO_SENT', 'Sent');
break;
case 9:
statustext = r('P_MA_FAILED', 'Failed');
content.style.backgroundColor = '#ffc107';
break;
case 10:
statustext = r('P_CU_OPTOUT', 'Opt-Out');
content.style.backgroundColor = '#ffc107';
break;
case 412:
statustext = r('P_CU_LANDLINE', 'Landline');
content.style.backgroundColor = '#ffc107';
break;
default:
statustext = r('P_CU_UNDELIVERED', 'Undelivered');
content.style.backgroundColor = '#ffc107';
break;
if (color != null) {
content.style.backgroundColor = color;
}
const divstatus = createElement('div', div => {
div.className = 'item-status';
div.innerText = statustext;
if (status == -10) {
setTooltip(div, statusmsg);
div.innerText = text;
if (tips != null) {
setTooltip(div, tips);
}
});
content.appendChild(divstatus);
@ -1255,43 +1222,8 @@ export default class CustomerCommunication {
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
}
#getMessageStatus(comm) {
let status = -100; // 没有状态,页面上不显示
const ls = [];
let statusmsg = '';
if (!comm.StatusIncorrect && comm.Participator?.length > 0) {
for (let p of comm.Participator) {
if (!isEmail(p.CustomerNumber)) {
if (ls.indexOf(p.Status) < 0) {
ls.push(p.Status);
}
if (statusmsg.length > 0) {
statusmsg += '\n';
}
statusmsg += `${p.CustomerNumber}: `;
const st = ({
0: r('P_CU_UNDELIVERED', 'Undelivered'),
1: r('P_WO_SENT', 'Sent'),
9: r('P_MA_FAILED', 'Failed')
})[p.Status];
if (st != null) {
statusmsg += st;
}
else
statusmsg += r('P_MA_XXXXXX', 'Unknown');
}
}
}
if (ls.length === 1) {
status = ls[0];
} else if (ls.length > 1) {
status = -10; // 多种状态
}
return [status, statusmsg];
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@ -4,11 +4,10 @@ import { nullOrEmpty, r as lang, contains } from "../../utility";
let r = lang;
export default class Follower {
#option;
#grid;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@ -20,7 +19,7 @@ export default class Follower {
const gridContainer = createElement('div', 'follower-grid');
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title,
content: createElement('div', 'follower-wrapper',
createElement('div', div => div.innerText = r('P_CR_WHODOYOUWANTTORECEIVECUSTOMERNOTIFICATIONS', 'Who do you want to receive customer notifications?')),
@ -31,9 +30,9 @@ export default class Follower {
search.addEventListener('input', () => {
const key = search.value;
if (nullOrEmpty(key)) {
this.#grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
} else {
this.#grid.source = this.#option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true))
this._var.grid.source = this._var.option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true))
.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
}
});
@ -45,8 +44,8 @@ export default class Follower {
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
if (typeof this.#option.onOk === 'function') {
return this.#option.onOk.call(this, this.#grid.source.filter(f => f.Email || f.Text));
if (typeof this._var.option.onOk === 'function') {
return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.Email || f.Text));
}
}
},
@ -76,8 +75,8 @@ export default class Follower {
}
];
grid.init();
grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this.#grid = grid;
grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid = grid;
return result;
}
}

View File

@ -1,33 +1,48 @@
import { createElement, setTooltip, createIcon } from "../../ui";
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
import { createBox, appendMedia } from "./lib";
// import { fileSupported, insertFile } from "./lib";
import { fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
let r = lang;
export default class InternalComment {
#container;
#option;
#enter;
#fileControl;
#file;
#message;
_var = {};
// _var.container;
// _var.option;
// _var.enter;
// _var.fileControl;
// _var.file;
// _var.message;
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
/**
* @param {any} contacts
*/
set contacts(contacts) {
this._var.contacts = contacts;
if (this._var.contactsUpdated !== true && contacts?.length > 0) {
const comments = this._var.comments;
if (comments?.length > 0) {
updateCustomerName(this._var.comments, contacts);
this._var.contactsUpdated = true;
}
}
}
@ -35,21 +50,21 @@ export default class InternalComment {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-post-note').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
this._var.container.querySelector('.button-post-note').disabled = flag;
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
get file() { return this._var.file || this._var.fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
this._var.fileControl?.remove();
const label = this._var.container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
this._var.fileControl = null;
this._var.file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
@ -57,15 +72,15 @@ export default class InternalComment {
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
this._var.fileControl = f;
this._var.file = f.files[0];
const link = this._var.container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
this._var.fileControl = null;
this._var.file = f;
}
if (label != null) {
label.style.display = '';
@ -79,16 +94,17 @@ export default class InternalComment {
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag === true;
this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
this._var.enter.disabled = flag === true;
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
}
create() {
const option = this._var.option;
const container = createBox(
createElement('div', null,
createElement('div', div => {
@ -97,96 +113,93 @@ export default class InternalComment {
})
), []
);
const readonly = this.#option.readonly;
const readonly = option.readonly;
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.maxLength = option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
if (readonly === true) {
enter.disabled = true;
}
// enter.addEventListener('paste', e => {
// const file = e.clipboardData.files[0];
// if (file != null) {
// e.preventDefault();
// this.file = insertFile(container, file, r);
// }
// });
this.#enter = enter;
enter.addEventListener('paste', e => {
const file = e.clipboardData.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file, r);
}
});
this._var.enter = enter;
container.appendChild(
createElement('div', div => {
div.className = 'message-bar';
// div.addEventListener('dragover', e => {
// if (option.readonly !== true) {
// const item = e.dataTransfer.items[0];
// if (item?.kind === 'file') {
// e.preventDefault();
// if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) {
// e.dataTransfer.dropEffect = 'none';
// } else {
// e.dataTransfer.dropEffect = 'link';
// }
// }
// }
// });
// div.addEventListener('drop', e => {
// if (option.readonly !== true) {
// const file = e.dataTransfer.files[0];
// if (file != null) {
// e.preventDefault();
// this.file = insertFile(container, file, r);
// }
// }
// });
div.addEventListener('dragover', e => {
if (option.readonly !== true) {
const item = e.dataTransfer.items[0];
if (item?.kind === 'file') {
e.preventDefault();
if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) {
e.dataTransfer.dropEffect = 'none';
} else {
e.dataTransfer.dropEffect = 'link';
}
}
}
});
div.addEventListener('drop', e => {
if (option.readonly !== true) {
const file = e.dataTransfer.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file, r);
}
}
});
},
enter,
createElement('div', div => div.style.textAlign = 'right',
createElement('div', selector => {
selector.className = 'file-selector';
if (this.#option.noMessage === true) {
selector.style.display = 'none';
}
},
createElement('div', div => {
div.className = 'selector-link';
div.style.display = 'none';
// div.addEventListener('click', () => {
// this.#fileControl?.remove();
// const file = createElement('input', input => {
// input.type = 'file';
// input.accept = fileSupported.join(',');
// input.addEventListener('change', () => {
// const file = insertFile(container, input.files?.[0], r);
// if (file != null) {
// this.file = file;
// }
// });
// });
// div.appendChild(this.#fileControl = file);
// file.dispatchEvent(new MouseEvent('click'));
// });
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
createElement('div', 'customer-left',
createElement('div', 'file-selector',
createElement('div', div => {
div.className = 'selector-link';
// div.style.display = 'none';
div.addEventListener('click', () => {
this._var.fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this._var.fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
)
),
createElement('div', 'prompt-count'),
createElement('button', button => {
button.className = 'roundbtn button-send-message';
button.style.backgroundColor = 'rgb(19, 150, 204)';
if (readonly === true || this.#option.noMessage === true) {
if (readonly === true || option.noMessage === true) {
button.style.display = 'none';
}
button.appendChild(createIcon('fa-solid', 'paper-plane'));
@ -196,9 +209,9 @@ export default class InternalComment {
if (nullOrEmpty(val?.trim())) {
return;
}
if (typeof this.#option.onAddMessage === 'function') {
if (typeof option.onAddMessage === 'function') {
this.loading = true;
this.#option.onAddMessage(this.text);
option.onAddMessage(this.text);
}
})
}),
@ -216,9 +229,9 @@ export default class InternalComment {
if (nullOrEmpty(val?.trim())) {
return;
}
if (typeof this.#option.onAddComment === 'function') {
if (typeof option.onAddComment === 'function') {
this.loading = true;
this.#option.onAddComment(this.text, this.file);
option.onAddComment(this.text, this.file);
}
})
})
@ -227,54 +240,77 @@ export default class InternalComment {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
load(data) {
const children = [];
if (data?.length > 0) {
this._var.comments = data;
if (this._var.contactsUpdated !== true) {
const contacts = this._var.contacts;
if (contacts?.length > 0) {
updateCustomerName(data, contacts);
this._var.contactsUpdated = true;
}
}
for (let comment of data) {
const div = createElement('div', 'item-div');
// if (sendto !== '') {
// sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
// }
const sendto = getMessageSendTo(comment, null, null, r)
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
div.innerText = comment.UserName;
div.innerText = comment.Sender;
if (sendto?.length > 0) {
setTooltip(div, sendto);
}
}));
const content = createElement('div', 'item-content');
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && comment.MMSParts?.length > 0) {
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Message)), mmsParts));
if (comment.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comment.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
}
}
if (comment.FollowUp?.length > 0) {
div.classList.add('item-sent');
const sendto = r('P_CU_SENDTO_COLON', 'Send To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
content.appendChild(createElement('div', div => {
// if (comment.FollowUp?.length > 0) {
// div.classList.add('item-sent');
// const sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
// content.appendChild(createElement('div', div => {
// div.className = 'item-status';
// div.innerText = r('P_WO_SENT', 'Sent');
// setTooltip(div, sendto);
// }));
// }
const [status, text, color, tips] = getMessageStatus(comment, r, this._var);
if (status !== -100) {
if (color != null) {
content.style.backgroundColor = color;
}
const divstatus = createElement('div', div => {
div.className = 'item-status';
div.innerText = r('P_WO_SENT', 'Sent');
setTooltip(div, sendto);
}));
div.innerText = text;
if (tips != null) {
setTooltip(div, tips);
}
});
content.appendChild(divstatus);
}
div.append(
content,
createElement('div', div => {
div.className = 'item-time';
div.innerText = comment.SubmitDateStr;
div.innerText = comment.TimeStr;
})
);
children.push(div);
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@ -1,4 +1,5 @@
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile } from "../../ui";
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile, createIcon, Popup, Grid, Dropdown } from "../../ui";
import { global, isEmail } from "../../utility";
export function createBox(title, functions) {
const container = createElement('div', 'comm');
@ -13,22 +14,30 @@ export function createBox(title, functions) {
export function appendMedia(container, mimeType, url) {
switch (mimeType) {
case 'application/pdf':
case '.pdf':
container.appendChild(createFile(url, 'file-pdf'));
break;
case 'application/msword':
case 'application/vnd.ms-word':
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case '.doc':
case '.docx':
container.appendChild(createFile(url, 'file-word'));
break;
case 'application/vnd.ms-excel':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case '.xls':
case '.xlsx':
container.appendChild(createFile(url, 'file-excel'));
break;
case 'application/vnd.ms-powerpoint':
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case '.ppt':
case '.pptx':
container.appendChild(createFile(url, 'file-powerpoint'));
break;
case 'application/smil':
case '.smil':
// TODO: ignore smil files
// container.appendChild(createFile(url, 'smile'));
break;
@ -41,10 +50,18 @@ export function appendMedia(container, mimeType, url) {
case 'audio/opus':
case 'audio/wav':
case 'audio/webm':
case '.aac':
case '.amr':
case '.mp3':
case '.ogg':
case '.opus':
case '.wav':
container.appendChild(createAudio(mimeType, url));
break;
case 'text/plain':
case 'text/x-vcard':
case '.txt':
case '.vcard':
container.appendChild(createFile(url, 'id-card'));
break;
case 'video/3gpp':
@ -54,8 +71,22 @@ export function appendMedia(container, mimeType, url) {
case 'video/x-mpeg':
case 'video/quicktime':
case 'video/webm':
case '.3gp':
case '.3gpp':
case '.mp4':
case '.mpg':
case '.mov':
case '.webm':
container.appendChild(createVideo(url));
break;
case '.jpg':
case '.jpeg':
case '.jfif':
case '.png':
case '.gif':
case '.bmp':
container.appendChild(createPicture(url));
break;
default:
if (/^image\//.test(mimeType)) {
container.appendChild(createPicture(url));
@ -151,4 +182,295 @@ export function insertFile(container, file, r) {
}
return file;
}
};
};
function getStatusText(status, dict) {
switch (status) {
case 0:
case 1:
case 5:
case 6:
return dict[status];
case 9:
case 10:
case 412:
return dict[9];
default:
return dict[9999];
}
}
const SymbolDropdown = Symbol.for('ui-dropdown');
class DropdownColumn {
static create(col, trigger, parent) {
const drop = new Dropdown({ ...col.dropOptions, parent });
drop.onselected = trigger;
return drop.create();
}
static _getDrop(element) {
const dropGlobal = global[SymbolDropdown];
if (dropGlobal == null) {
return null;
}
const dropId = element.dataset.dropId;
const drop = dropGlobal[dropId];
if (drop == null) {
return null;
}
return drop;
}
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
if (data != null) {
val = data.text;
}
super.setValue(element, val);
}
static setValue(element, val, _item, col) {
if (element.tagName !== 'DIV') {
this._setValue(col.source, element, val);
return;
}
const drop = this._getDrop(element);
if (drop == null) {
return;
}
if (drop.source == null || drop.source.length === 0) {
let source = col.source;
if (source != null) {
drop.source = source;
}
}
drop.select(val, true);
}
static getValue(e) {
return e.value;
}
}
export function getMessageStatus(comm, r, _var) {
const messageStatus = {
0: r('P_CU_PENDING', 'Pending'),
1: r('P_WO_SENT', 'Sent'),
5: r('P_CU_DELIVERYCONFIRMED', 'Delivery Confirmed'),
6: r('P_CU_RESENT', 'Resent'),
9: r('P_MA_FAILED', 'Failed'),
9999: r('P_CU_UNKNOWN', 'Unknown')
};
const knownStatus = [0, 1, 5, 6, 9, 10, 412];
const okStatus = [1, 5, 6];
const failedStatus = [9, 10, 412];
let status = -100; // 没有状态,页面上不显示
const ls = [];
const msgs = [];
if (!comm.StatusIncorrect && comm.Participator?.length > 0) {
// if (comm.Id === 433339) {
// comm.Participator[4].Status = 6;
// }
for (let p of comm.Participator) {
if (!isEmail(p.CustomerNumber)) {
if (ls.indexOf(p.Status) < 0) {
ls.push(p.Status);
}
p.statusText = getStatusText(p.Status, messageStatus);
msgs.push(p);
}
}
}
if (ls.length === 1) {
status = ls[0];
} else if (ls.length > 1) {
// status = -10; // 多种状态
status = ls
.filter(s => okStatus.indexOf(s) < 0) // ok status
.sort((a, b) => b - a)[0] ?? 1;
}
const statusText = messageStatus[failedStatus.includes(status) ? 9 : status] ?? messageStatus[9999];
const statusColor = okStatus.includes(status) ? null : '#ffc107';
const statusUpdatable = _var.option.statusUpdatable;
let statusTips;
if (statusUpdatable !== false || ls.length > 1) {
statusTips = createElement('div', tip => {
for (let i = 0; i < msgs.length; i++) {
tip.appendChild(createElement('div', t => {
const p = msgs[i];
if (statusUpdatable !== false && p.StatusChanged) {
t.append(
createElement('span', s => s.innerText = `${p.CustomerNumber}: `),
createElement('span', s => {
s.style.color = '#2140fb';
s.style.cursor = 'pointer';
s.innerText = p.statusText;
s.addEventListener('click', () => {
if (typeof _var.option.onMessageStatusClicked === 'function') {
_var.option.onMessageStatusClicked(p);
}
});
})
)
} else {
t.innerText = `${p.CustomerNumber}: ${p.statusText}`;
}
}));
}
if (statusUpdatable !== false) {
tip.appendChild(createElement('div', b => {
b.className = 'tip-function-button';
// setTooltip(b, r('P_CU_UPDATESTATUS', 'Update Status'));
b.addEventListener('click', async () => {
for (let p of comm.Participator) {
switch (p.Status) {
case 0:
case 1:
case 5:
case 6:
p.statusChanged = String(p.Status);
break;
case 9:
case 10:
case 412:
p.statusChanged = '9';
break;
default:
p.statusChanged = '-1';
break;
}
}
const gridContainer = createElement('div', 'status-grid');
const popup = new Popup({
onMasking: _var.option.onMasking,
title: r('P_CU_UPDATESTATUS', 'Update Status'),
content: createElement('div', wrapper => {
wrapper.className = 'update-status-wrapper';
wrapper.style.width = '500px';
},
gridContainer
),
buttons: [
{
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
const changed = msgs.filter(m => {
switch (m.statusChanged) {
case '-1':
return knownStatus.includes(m.Status);
case '9':
return failedStatus.indexOf(m.Status) < 0;
default:
return String(m.Status) !== m.statusChanged;
}
}).map(m => {
let status = Number(m.statusChanged);
if (isNaN(status) || status < 0) {
status = 9999;
}
return {
Id: m.Id,
Status: status
};
});
if (typeof _var.option.onUpdateMessageStatus === 'function') {
_var.option.onUpdateMessageStatus(changed);
}
}
},
{
text: r('P_WO_CANCEL', 'Cancel'),
key: 'cancel'
}
]
});
await popup.show();
const grid = new Grid(gridContainer);
// grid.headerVisible = false;
grid.allowHtml = true;
grid.columns = [
{
key: 'CustomerNumber',
caption: r('P_JS_NUMBER', 'Number'),
width: 150
},
/*{
key: 'customerName',
caption: r('P_WOS_CUSTOMERNAME', 'Customer Name'),
width: 120
},*/
{
key: 'statusText',
caption: r('P_CU_CURRENTSTATUS', 'Current Status'),
width: 155
},
{
key: 'statusChanged',
caption: r('P_CU_REVISEDSTATUS', 'Revised Status'),
width: 155,
type: DropdownColumn,
source: [
{ value: '-1', text: messageStatus[9999] },
{ value: '0', text: messageStatus[0] },
{ value: '1', text: messageStatus[1] },
{ value: '5', text: messageStatus[5] },
{ value: '6', text: messageStatus[6] },
{ value: '9', text: messageStatus[9] }
]
}
];
grid.init();
grid.source = msgs;
});
}, createIcon('fa-light', 'wave-sine')));
}
});
}
return [status, statusText, statusColor, statusTips];
};
export function getMessageSendTo(comm, contacts, followers, r) {
let sendto = '';
if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) {
for (let oriph of comm.OriPhoneNumbers) {
let cname;
const email = isEmail(oriph);
if (contacts?.length > 0) {
let c = email ?
contacts.find(c => c.Email === oriph) :
contacts.find(c => c.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
} else if (followers?.length > 0) {
c = email ?
followers.find(f => f.Email === oriph) :
followers.find(f => f.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
}
}
}
sendto += (cname ?? oriph) + '\n';
}
}
if (sendto !== '') {
sendto = r('P_CU_SENDTO_COLON', 'Sent to :') + `\n${sendto}`;
}
return sendto;
}
export function updateCustomerName(messages, contacts) {
if (messages?.length > 0 && contacts?.length > 0) {
for (let m of messages) {
if (m.Participator?.length > 0) {
for (let p of m.Participator) {
const contact = contacts.filter(c => c.MobilePhoneDisplayText === p.CustomerNumber)[0];
p.customerName = contact?.Name;
}
}
}
}
}

View File

@ -1,10 +1,24 @@
@import "../../ui/css/functions/func.scss";
.ui-popup-mask .wrapper-edit-method {
width: 100%;
.ui-popup-mask {
.wrapper-edit-method {
width: 100%;
.ui-check-wrapper {
padding: 0 28px;
.ui-check-wrapper {
padding: 0 28px;
}
}
.status-grid,
.contacts-record,
.contacts-wo {
>.ui-grid {
overflow-x: visible;
>.ui-grid-body {
overflow: visible;
}
}
}
}
@ -13,7 +27,7 @@
flex-direction: column;
width: 320px;
background-color: var(--dark-fore-color);
border: 1px solid var(--title-bg-color);
border: 1px solid var(--title-ctrlbg-color);
margin-left: 12px;
& {
@ -69,7 +83,7 @@
flex: 0 0 auto;
padding: 5px 0 5px 10px;
color: var(--title-color);
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
line-height: 24px;
display: flex;
align-items: center;
@ -132,7 +146,7 @@
flex: 0 0 auto;
padding: 4px 0;
display: flex;
border-bottom: 1px solid var(--title-bg-color);
border-bottom: 1px solid var(--title-ctrlbg-color);
position: relative;
>.bar-icon {
@ -253,13 +267,13 @@
.message-bar {
flex: 0 0 auto;
border-bottom: 1px solid var(--title-bg-color);
border-bottom: 1px solid var(--title-ctrlbg-color);
display: flex;
flex-direction: column;
>textarea {
padding: 10px 10px 0;
border: 1px solid var(--title-bg-color);
border: 1px solid var(--title-ctrlbg-color);
border-radius: 5px;
height: 70px;
resize: none;
@ -274,73 +288,75 @@
>div {
padding: 0 10px 10px;
>.customer-name {
>.customer-left {
float: left;
text-align: left;
>span {
font-size: var(--font-smaller-size);
}
>.ui-input {
margin-left: 4px;
width: 150px;
border-top: none;
border-left: none;
border-right: none;
}
}
>.file-selector {
float: left;
display: inline-flex;
align-items: center;
height: 30px;
>.selector-link {
cursor: pointer;
display: flex;
>svg {
width: 16px;
height: 16px;
fill: var(--secondary-link-color);
>.customer-name {
>span {
font-size: var(--font-smaller-size);
}
>input {
display: none;
>.ui-input {
margin-left: 4px;
width: 150px;
border-top: none;
border-left: none;
border-right: none;
}
}
>.selector-name {
max-width: 130px;
padding: 0 20px 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
>.file-selector {
display: inline-flex;
align-items: center;
height: 30px;
+layer {
display: none;
margin-left: -20px;
>.selector-link {
cursor: pointer;
display: flex;
>svg {
width: 16px;
height: 16px;
fill: var(--red-color);
fill: var(--secondary-link-color);
}
&:hover {
>input {
display: none;
}
}
>.selector-name {
max-width: 130px;
padding: 0 20px 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+layer {
display: none;
margin-left: -20px;
cursor: pointer;
>svg {
width: 16px;
height: 16px;
fill: var(--red-color);
}
&:hover {
display: flex;
}
}
&:hover+layer {
display: flex;
}
}
&:hover+layer {
display: flex;
}
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
}
}
}
}
@ -421,6 +437,25 @@
margin-right: -12px;
font-size: .625rem;
float: right;
.ui-tooltip-content .tip-function-button {
text-align: right;
>svg {
width: 20px;
height: 20px;
cursor: pointer;
border: 1px solid;
border-radius: 10px;
padding: 2px;
box-sizing: border-box;
transition: opacity .12s;
&:hover {
opacity: .5;
}
}
}
}
}