adjustment

This commit is contained in:
2023-07-27 10:03:53 +08:00
parent 3e9ee59178
commit 29209a3a00
17 changed files with 456 additions and 173 deletions

View File

@ -1,7 +1,7 @@
import { Grid, Dropdown, createElement, createCheckbox, Popup, showAlert } from "../../ui";
import { isEmail, nullOrEmpty, r } from "../../utility";
class Contact {
export class Contact {
#option;
#refs;
@ -191,7 +191,7 @@ class Contact {
}
}
class CustomerRecordContact {
export class CustomerRecordContact {
#option;
#grid;
@ -252,6 +252,4 @@ class CustomerRecordContact {
this.#grid.source = contacts;
}
}
}
export { Contact, CustomerRecordContact };
}

View File

@ -1,6 +1,6 @@
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup, createPicture, createAudio, createVideo, createPdf, createSmilefile, createVcard, createVideofile, createFile } from "../../ui";
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup } from "../../ui";
import { r, nullOrEmpty, formatUrl, isEmail, isPhone } from "../../utility";
import { createBox } from "./lib";
import { createBox, appendMedia, fileSupported, insertFile } from "./lib";
import { Contact, CustomerRecordContact } from "./contact";
import Follower from "./follower";
@ -35,6 +35,8 @@ class CustomerCommunication {
#followers;
#buttonFollower;
#enter;
#fileControl;
#file;
#message;
#data = {};
#gridContact;
@ -118,6 +120,38 @@ class CustomerCommunication {
}
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
label.querySelector('.ui-tooltip-wrapper')?.remove();
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
}
if (label != null) {
label.style.display = '';
label.innerText = f.name;
setTooltip(label, f.name);
}
}
}
get customerName() { return this.#container.querySelector('.customer-name>.ui-input')?.value }
set customerName(name) {
const element = this.#container.querySelector('.customer-name>.ui-input');
@ -400,10 +434,20 @@ class CustomerCommunication {
// enter.disabled = true;
// }
enter.addEventListener('input', () => {
const val = this.#enter.value;
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
});
enter.addEventListener('paste', e => {
if (option.customerNameVisible === true) {
return;
}
const file = e.clipboardData.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file);
}
});
this.#enter = enter;
container.appendChild(
createElement('div', div => {
@ -411,6 +455,28 @@ class CustomerCommunication {
if (readonly === true) {
div.style.display = 'none';
}
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);
}
}
});
},
enter,
createElement('div', div => div.style.textAlign = 'right',
@ -426,6 +492,41 @@ class CustomerCommunication {
input.className = 'ui-input';
})
),
createElement('div', selector => {
selector.className = 'file-selector';
if (option.customerNameVisible === true) {
selector.style.display = 'none';
}
},
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]);
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', 'prompt-count'),
createElement('button', button => {
button.className = 'roundbtn button-send-message';
@ -436,7 +537,7 @@ class CustomerCommunication {
button.appendChild(createIcon('fa-solid', 'paper-plane'));
setTooltip(button, r('sendMessage', 'Send Message'));
button.addEventListener('click', () => {
const val = this.#enter.value;
const val = this.text;
if (nullOrEmpty(val?.trim())) {
const p = showAlert(r('error', 'Error'), r('messageRequired', 'Please input the message.'), 'warn');
if (typeof option.onMasking === 'function') {
@ -446,7 +547,7 @@ class CustomerCommunication {
return;
}
if (typeof option.onAddMessage === 'function') {
option.onAddMessage(this.#enter.value);
option.onAddMessage(this.text, this.file);
}
})
})
@ -1063,43 +1164,20 @@ class CustomerCommunication {
}
}));
const content = createElement('div', 'item-content');
const emoji = s => s.replace(/(=[A-Fa-f0-9]{2}){4}/, s => decodeURIComponent(s.replaceAll('=', '%')));
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => {
if (/https?:\/\//i.test(comm.Message)) {
span.innerHTML = formatUrl(comm.Message);
span.innerHTML = emoji(formatUrl(comm.Message));
} else {
span.innerText = comm.Message;
span.innerText = emoji(comm.Message);
}
span.appendChild(mmsParts);
}));
if (comm.IsMMS && comm.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comm.MMSParts) {
switch (kv.Key) {
case 'application/pdf':
content.appendChild(createPdf(kv.Value));
break;
case 'application/smil':
content.appendChild(createSmilefile(kv.Value));
break;
case 'audio/amr':
content.appendChild(createAudio(kv.Key));
break;
case 'image/gif':
case 'image/jpeg':
case 'image/png':
content.appendChild(createPicture(kv.Value));
break;
case 'text/x-vcard':
content.appendChild(createVcard(kv.Value));
break;
case 'video/3gpp':
content.appendChild(createVideofile(kv.Value));
break;
case 'video/mp4':
content.appendChild(createVideo(kv.Value));
break;
default:
content.appendChild(createFile(kv.Value));
break;
}
appendMedia(mmsParts, kv.Key, kv.Value);
}
}
if (comm.IsReply) {

View File

@ -1,11 +1,13 @@
import { createElement, setTooltip, createIcon } from "../../ui";
import { createElement, setTooltip, createIcon, showAlert } from "../../ui";
import { r, nullOrEmpty, escapeHtml } from "../../utility";
import { createBox } from "./lib";
import { createBox, appendMedia, fileSupported, insertFile } from "./lib";
class InternalComment {
#container;
#option;
#enter;
#fileControl;
#file;
#message;
constructor(opt) {
@ -22,6 +24,50 @@ class InternalComment {
}
}
/**
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-post-note').disabled = flag;
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
label.querySelector('.ui-tooltip-wrapper')?.remove();
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
}
if (label != null) {
label.style.display = '';
label.innerText = f.name;
setTooltip(label, f.name);
}
}
}
/**
* @param {boolean} flag
*/
@ -50,18 +96,85 @@ class InternalComment {
enter.placeholder = r('typeComment', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.#enter.value;
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;
});
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);
// }
// });
this.#enter = enter;
container.appendChild(
createElement('div', 'message-bar',
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);
// }
// }
// });
},
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]);
// 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', 'prompt-count'),
createElement('button', button => {
button.className = 'roundbtn button-send-message';
@ -73,7 +186,7 @@ class InternalComment {
setTooltip(button, r('sendMessage', 'Send Message'));
button.addEventListener('click', () => {
if (typeof this.#option.onAddMessage === 'function') {
this.#option.onAddMessage(this.#enter.value);
this.#option.onAddMessage(this.text);
}
})
}),
@ -88,7 +201,7 @@ class InternalComment {
setTooltip(button, r('postNote', 'Post Note'));
button.addEventListener('click', () => {
if (typeof this.#option.onAddComment === 'function') {
this.#option.onAddComment(this.#enter.value);
this.#option.onAddComment(this.text, this.file);
}
})
})
@ -115,7 +228,15 @@ class InternalComment {
div.innerText = comment.UserName;
}));
const content = createElement('div', 'item-content');
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(comment.Comment)));
const emoji = s => s.replace(/(=[A-Fa-f0-9]{2}){4}/, s => decodeURIComponent(s.replaceAll('=', '%')));
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = emoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && 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('sendToColon', 'Send To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');

View File

@ -1 +1,3 @@
export function createBox(title: HTMLElement, functions: HTMLElement[]): HTMLElement
export function createBox(title: HTMLElement, functions: HTMLElement[]): HTMLElement
export function appendMedia(container: HTMLElement, mimeType: string, url: string): HTMLElement

View File

@ -1,6 +1,7 @@
import { createElement } from "../../ui";
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile } from "../../ui";
import { r } from "../../utility";
function createBox(title, functions) {
export function createBox(title, functions) {
const container = createElement('div', 'comm');
const header = createElement('div', 'title-bar',
title,
@ -8,8 +9,115 @@ function createBox(title, functions) {
);
container.appendChild(header);
return container;
}
};
export {
createBox
}
export function appendMedia(container, mimeType, url) {
switch (mimeType) {
case 'application/pdf':
container.appendChild(createFile(url, 'file-pdf'));
break;
case 'application/smil':
// TODO: ignore smil files
// container.appendChild(createFile(url, 'smile'));
break;
case 'audio/amr':
case 'audio/mp3':
case 'audio/mpeg':
case 'audio/x-mpeg':
case 'audio/aac':
case 'audio/ogg':
case 'audio/opus':
case 'audio/wav':
case 'audio/webm':
container.appendChild(createAudio(mimeType, url));
break;
case 'image/gif':
case 'image/jpeg':
case 'image/png':
container.appendChild(createPicture(url));
break;
case 'text/x-vcard':
container.appendChild(createFile(url, 'id-card'));
break;
case 'video/mp4':
case 'video/mpeg':
case 'video/x-mpeg':
case 'video/mp2t':
case 'video/webm':
case 'video/quicktime':
container.appendChild(createVideo(url));
break;
default:
if (/^audio\//.test(mimeType)) {
container.appendChild(createFile(url, 'music'));
} else if (/^video\//.test(mimeType)) {
container.appendChild(createFile(url, 'video'));
} else {
container.appendChild(createFile(url));
}
break;
}
return container;
};
const MaxAttachmentSize = {
limit: 2 * 1024 * 1024,
text: '2MB'
};
export const fileSupported = [
'application/pdf',
'audio/mpeg',
'audio/x-mpeg',
'audio/amr',
'.amr',
'image/bmp',
'image/gif',
'image/jpeg',
'image/png',
'image/tiff',
'text/plain',
'text/x-vcard',
'video/3gpp',
'video/mp4',
'video/webm',
'video/quicktime'
];
export function insertFile(container, file) {
const label = container.querySelector('.file-selector>.selector-name');
if (label != null && file != null) {
let type = file.type;
if (type == null || type.length === 0) {
type = file.name;
type = type.substring(type.lastIndexOf('.'));
}
if (fileSupported.indexOf(type) < 0) {
showAlert(r('error', 'Error'), r('notSupported', 'File type "{type}" is now not supported.').replace('{type}', type));
return;
}
const isImage = /^image\//.test(type);
if (!isImage && file.size > MaxAttachmentSize.limit) {
showAlert(r('error', 'Error'), r('fileTooLarge', `File is too large. (> ${MaxAttachmentSize.text})`), 'warn');
return;
}
const fn = file.name;
label.style.display = '';
label.innerText = fn;
if (isImage) {
const img = new Image();
const reader = new FileReader();
reader.onload = e => {
img.src = e.target.result;
setTooltip(label, img);
};
reader.onerror = () => setTooltip(label, fn);
reader.readAsDataURL(file);
// img.src = URL.createObjectURL(file);
// setTooltip(label, img);
} else {
setTooltip(label, fn);
}
return file;
}
};

View File

@ -290,6 +290,61 @@
}
}
>.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);
}
>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;
}
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
}
}
}
>.prompt-count {
display: inline-block;
color: var(--light-color);
@ -340,6 +395,10 @@
max-width: 240px;
background-color: rgb(244, 244, 244);
audio[controls] {
width: 220px;
}
a>svg {
width: 13px;
height: 13px;