web-mojo 2.1.458 → 2.1.498
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +29 -187
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +3 -3
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +2 -2
- package/dist/chat.css +392 -0
- package/dist/chunks/ChatView-D4ZiYBzL.js +2 -0
- package/dist/chunks/ChatView-D4ZiYBzL.js.map +1 -0
- package/dist/chunks/{FilePreviewView-CehLQVJa.js → ChatView-DkK2PBny.js} +620 -84
- package/dist/chunks/ChatView-DkK2PBny.js.map +1 -0
- package/dist/chunks/{ContextMenu-BjQymcpo.js → ContextMenu-A1PxR6Zt.js} +2 -2
- package/dist/chunks/{ContextMenu-BjQymcpo.js.map → ContextMenu-A1PxR6Zt.js.map} +1 -1
- package/dist/chunks/{ContextMenu-C0sdb9u5.js → ContextMenu-B4LTfsNs.js} +2 -2
- package/dist/chunks/{ContextMenu-C0sdb9u5.js.map → ContextMenu-B4LTfsNs.js.map} +1 -1
- package/dist/chunks/{DataView-DDe4NgiH.js → DataView-BIfnT2oG.js} +2 -2
- package/dist/chunks/{DataView-DDe4NgiH.js.map → DataView-BIfnT2oG.js.map} +1 -1
- package/dist/chunks/{DataView-Cl4kRP-W.js → DataView-DGEl3x72.js} +2 -2
- package/dist/chunks/{DataView-Cl4kRP-W.js.map → DataView-DGEl3x72.js.map} +1 -1
- package/dist/chunks/{Dialog-upGYwvdZ.js → Dialog-DtSluuiz.js} +5 -5
- package/dist/chunks/{Dialog-upGYwvdZ.js.map → Dialog-DtSluuiz.js.map} +1 -1
- package/dist/chunks/{Dialog-DAQdjRci.js → Dialog-pH-ua3tF.js} +2 -2
- package/dist/chunks/{Dialog-DAQdjRci.js.map → Dialog-pH-ua3tF.js.map} +1 -1
- package/dist/chunks/{FormView-B7pwdxqX.js → FormView-CDG8qMdU.js} +3 -3
- package/dist/chunks/FormView-CDG8qMdU.js.map +1 -0
- package/dist/chunks/{FormView-C9PgTIH2.js → FormView-DrvkHPA-.js} +99 -10
- package/dist/chunks/FormView-DrvkHPA-.js.map +1 -0
- package/dist/chunks/{MetricsChart-TwtfTNEA.js → MetricsChart-Bo0ky0UE.js} +2 -2
- package/dist/chunks/{MetricsChart-TwtfTNEA.js.map → MetricsChart-Bo0ky0UE.js.map} +1 -1
- package/dist/chunks/{MetricsChart-x15UvXRk.js → MetricsChart-DJU0UetE.js} +3 -3
- package/dist/chunks/{MetricsChart-x15UvXRk.js.map → MetricsChart-DJU0UetE.js.map} +1 -1
- package/dist/chunks/{PDFViewer-tK1fntDR.js → PDFViewer-8HA3oUBX.js} +2 -2
- package/dist/chunks/{PDFViewer-tK1fntDR.js.map → PDFViewer-8HA3oUBX.js.map} +1 -1
- package/dist/chunks/{PDFViewer---ebUwfT.js → PDFViewer-CydWqbUF.js} +3 -3
- package/dist/chunks/{PDFViewer---ebUwfT.js.map → PDFViewer-CydWqbUF.js.map} +1 -1
- package/dist/chunks/{Page-q5Gme_r0.js → Page-dP8b4UdB.js} +2 -2
- package/dist/chunks/{Page-q5Gme_r0.js.map → Page-dP8b4UdB.js.map} +1 -1
- package/dist/chunks/{Page-BUXs6-fN.js → Page-nrJqRb7x.js} +2 -2
- package/dist/chunks/{Page-BUXs6-fN.js.map → Page-nrJqRb7x.js.map} +1 -1
- package/dist/chunks/{TopNav-os-U9FQP.js → TopNav-DfikMNXj.js} +2 -2
- package/dist/chunks/{TopNav-os-U9FQP.js.map → TopNav-DfikMNXj.js.map} +1 -1
- package/dist/chunks/{TopNav-BOMPjms6.js → TopNav-jh5jqV3e.js} +2 -2
- package/dist/chunks/{TopNav-BOMPjms6.js.map → TopNav-jh5jqV3e.js.map} +1 -1
- package/dist/chunks/{WebApp-BONphh_h.js → WebApp-BdmNWAj2.js} +13 -17
- package/dist/chunks/{WebApp-BONphh_h.js.map → WebApp-BdmNWAj2.js.map} +1 -1
- package/dist/chunks/{WebApp-DUxotRc-.js → WebApp-D801tYpF.js} +2 -2
- package/dist/chunks/{WebApp-DUxotRc-.js.map → WebApp-D801tYpF.js.map} +1 -1
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +5 -5
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +86 -83
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +4 -4
- package/package.json +2 -2
- package/dist/chunks/FilePreviewView-7-RBir4g.js +0 -2
- package/dist/chunks/FilePreviewView-7-RBir4g.js.map +0 -1
- package/dist/chunks/FilePreviewView-CehLQVJa.js.map +0 -1
- package/dist/chunks/FormView-B7pwdxqX.js.map +0 -1
- package/dist/chunks/FormView-C9PgTIH2.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { M as Model, C as Collection, T as ToastService, G as GroupList, d as UserList } from "./ContextMenu-
|
|
2
|
-
import { r as rest, V as View, d as dataFormatter, M as Mustache } from "./WebApp-
|
|
3
|
-
import { P as Page } from "./Page-
|
|
4
|
-
import Dialog from "./Dialog-
|
|
5
|
-
import { F as FormView } from "./FormView-
|
|
1
|
+
import { M as Model, C as Collection, T as ToastService, G as GroupList, d as UserList } from "./ContextMenu-A1PxR6Zt.js";
|
|
2
|
+
import { r as rest, V as View, d as dataFormatter, M as Mustache } from "./WebApp-BdmNWAj2.js";
|
|
3
|
+
import { P as Page } from "./Page-dP8b4UdB.js";
|
|
4
|
+
import Dialog from "./Dialog-DtSluuiz.js";
|
|
5
|
+
import { F as FormView, a as applyFileDropMixin } from "./FormView-DrvkHPA-.js";
|
|
6
6
|
class S3Bucket extends Model {
|
|
7
7
|
constructor(data = {}) {
|
|
8
8
|
super(data, {
|
|
@@ -2566,13 +2566,55 @@ const TicketForms = {
|
|
|
2566
2566
|
create: {
|
|
2567
2567
|
title: "Create Ticket",
|
|
2568
2568
|
fields: [
|
|
2569
|
-
{
|
|
2569
|
+
{
|
|
2570
|
+
name: "title",
|
|
2571
|
+
type: "text",
|
|
2572
|
+
label: "Title",
|
|
2573
|
+
required: true,
|
|
2574
|
+
cols: 12
|
|
2575
|
+
},
|
|
2570
2576
|
{ name: "description", type: "textarea", label: "Description", required: false, cols: 12 },
|
|
2571
|
-
{
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2577
|
+
{
|
|
2578
|
+
name: "category",
|
|
2579
|
+
type: "select",
|
|
2580
|
+
label: "Category",
|
|
2581
|
+
options: TicketCategoriesOptions,
|
|
2582
|
+
cols: 12,
|
|
2583
|
+
value: "ticket"
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
name: "priority",
|
|
2587
|
+
type: "number",
|
|
2588
|
+
label: "Priority",
|
|
2589
|
+
value: 5,
|
|
2590
|
+
cols: 6
|
|
2591
|
+
},
|
|
2592
|
+
{
|
|
2593
|
+
name: "status",
|
|
2594
|
+
type: "select",
|
|
2595
|
+
label: "Status",
|
|
2596
|
+
options: ["new", "open", "paused", "resolved", "qa", "ignored"],
|
|
2597
|
+
cols: 6,
|
|
2598
|
+
value: "new"
|
|
2599
|
+
},
|
|
2600
|
+
{
|
|
2601
|
+
type: "collection",
|
|
2602
|
+
name: "assignee",
|
|
2603
|
+
label: "Assignee",
|
|
2604
|
+
Collection: UserList,
|
|
2605
|
+
labelField: "display_name",
|
|
2606
|
+
valueField: "id",
|
|
2607
|
+
cols: 12
|
|
2608
|
+
},
|
|
2609
|
+
{
|
|
2610
|
+
type: "collection",
|
|
2611
|
+
name: "incident",
|
|
2612
|
+
label: "Incident",
|
|
2613
|
+
Collection: IncidentList,
|
|
2614
|
+
labelField: "title",
|
|
2615
|
+
valueField: "id",
|
|
2616
|
+
cols: 12
|
|
2617
|
+
}
|
|
2576
2618
|
]
|
|
2577
2619
|
},
|
|
2578
2620
|
edit: {
|
|
@@ -6533,90 +6575,584 @@ class FilePreviewView extends View {
|
|
|
6533
6575
|
}
|
|
6534
6576
|
}
|
|
6535
6577
|
}
|
|
6578
|
+
class ChatMessageView extends View {
|
|
6579
|
+
constructor(options = {}) {
|
|
6580
|
+
super({
|
|
6581
|
+
className: "chat-message",
|
|
6582
|
+
...options
|
|
6583
|
+
});
|
|
6584
|
+
this.message = options.message || {};
|
|
6585
|
+
this.theme = options.theme || "compact";
|
|
6586
|
+
this.isCurrentUser = options.isCurrentUser || false;
|
|
6587
|
+
if (this.theme === "bubbles") {
|
|
6588
|
+
this.className += this.isCurrentUser ? " message-right" : " message-left";
|
|
6589
|
+
}
|
|
6590
|
+
}
|
|
6591
|
+
getTemplate() {
|
|
6592
|
+
if (this.message.type === "system_event") {
|
|
6593
|
+
return `
|
|
6594
|
+
<div class="chat-message-system text-center text-muted small py-2">
|
|
6595
|
+
<i class="bi bi-info-circle me-1"></i>
|
|
6596
|
+
{{message.content}} on {{message.timestamp|datetime}}
|
|
6597
|
+
</div>
|
|
6598
|
+
`;
|
|
6599
|
+
}
|
|
6600
|
+
if (this.theme === "bubbles") {
|
|
6601
|
+
return this.getBubblesTemplate();
|
|
6602
|
+
} else {
|
|
6603
|
+
return this.getCompactTemplate();
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
/**
|
|
6607
|
+
* Get compact theme template (Option 4 - Admin/Activity Feed Style)
|
|
6608
|
+
*/
|
|
6609
|
+
getCompactTemplate() {
|
|
6610
|
+
const userClass = this.isCurrentUser ? "bg-primary" : "bg-secondary";
|
|
6611
|
+
return `
|
|
6612
|
+
<div class="message-item">
|
|
6613
|
+
<div class="message-avatar ${userClass}">
|
|
6614
|
+
{{#message.author.avatarUrl}}
|
|
6615
|
+
<img src="{{message.author.avatarUrl}}" alt="{{message.author.name}}" class="w-100 h-100 rounded-circle">
|
|
6616
|
+
{{/message.author.avatarUrl}}
|
|
6617
|
+
{{^message.author.avatarUrl}}
|
|
6618
|
+
{{message.author.name|initials}}
|
|
6619
|
+
{{/message.author.avatarUrl}}
|
|
6620
|
+
</div>
|
|
6621
|
+
<div class="message-content">
|
|
6622
|
+
<div class="message-header">
|
|
6623
|
+
<div class="message-author">
|
|
6624
|
+
{{message.author.name}}
|
|
6625
|
+
{{#isCurrentUser}}
|
|
6626
|
+
<span class="badge bg-primary badge-sm ms-1">You</span>
|
|
6627
|
+
{{/isCurrentUser}}
|
|
6628
|
+
</div>
|
|
6629
|
+
<div class="message-time text-muted">{{message.timestamp|relative}}</div>
|
|
6630
|
+
</div>
|
|
6631
|
+
<div class="message-text">{{{message.content}}}</div>
|
|
6632
|
+
<div data-container="attachments"></div>
|
|
6633
|
+
</div>
|
|
6634
|
+
</div>
|
|
6635
|
+
`;
|
|
6636
|
+
}
|
|
6637
|
+
/**
|
|
6638
|
+
* Get bubbles theme template (Option 1 - Modern Chat Bubbles)
|
|
6639
|
+
*/
|
|
6640
|
+
getBubblesTemplate() {
|
|
6641
|
+
return `
|
|
6642
|
+
<div class="message-bubble-wrapper">
|
|
6643
|
+
<div class="message-meta">
|
|
6644
|
+
<strong>{{message.author.name}}</strong>
|
|
6645
|
+
<span class="text-muted">· {{message.timestamp|relative}}</span>
|
|
6646
|
+
</div>
|
|
6647
|
+
<div class="message-bubble">
|
|
6648
|
+
<div class="message-text">{{{message.content}}}</div>
|
|
6649
|
+
<div data-container="attachments"></div>
|
|
6650
|
+
</div>
|
|
6651
|
+
</div>
|
|
6652
|
+
`;
|
|
6653
|
+
}
|
|
6654
|
+
async onAfterRender() {
|
|
6655
|
+
if (this.message.attachments && this.message.attachments.length > 0) {
|
|
6656
|
+
const attachmentsContainer = this.element.querySelector('[data-container="attachments"]');
|
|
6657
|
+
if (attachmentsContainer) {
|
|
6658
|
+
this.message.attachments.forEach((file) => {
|
|
6659
|
+
const filePreview = new FilePreviewView({ file });
|
|
6660
|
+
this.addChild(filePreview);
|
|
6661
|
+
filePreview.render(true, attachmentsContainer);
|
|
6662
|
+
});
|
|
6663
|
+
}
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
}
|
|
6667
|
+
class ChatInputView extends View {
|
|
6668
|
+
constructor(options = {}) {
|
|
6669
|
+
super({
|
|
6670
|
+
className: "chat-input-view",
|
|
6671
|
+
...options
|
|
6672
|
+
});
|
|
6673
|
+
this.placeholder = options.placeholder || "Type a message...";
|
|
6674
|
+
this.buttonText = options.buttonText || "Send";
|
|
6675
|
+
this.attachments = [];
|
|
6676
|
+
this.pendingUploads = /* @__PURE__ */ new Map();
|
|
6677
|
+
}
|
|
6678
|
+
getTemplate() {
|
|
6679
|
+
return `
|
|
6680
|
+
<div class="chat-input-container">
|
|
6681
|
+
<div class="chat-input-attachments" data-container="attachments"></div>
|
|
6682
|
+
<div class="chat-input-wrapper">
|
|
6683
|
+
<textarea
|
|
6684
|
+
class="chat-input form-control"
|
|
6685
|
+
placeholder="${this.placeholder}"
|
|
6686
|
+
rows="1"></textarea>
|
|
6687
|
+
<button class="chat-send-btn btn btn-primary" data-action="send-message" type="button">
|
|
6688
|
+
<i class="bi bi-send-fill"></i>
|
|
6689
|
+
</button>
|
|
6690
|
+
</div>
|
|
6691
|
+
<div class="chat-input-footer">
|
|
6692
|
+
<small class="text-muted">
|
|
6693
|
+
<i class="bi bi-paperclip"></i>
|
|
6694
|
+
Drag & drop files to attach
|
|
6695
|
+
</small>
|
|
6696
|
+
</div>
|
|
6697
|
+
</div>
|
|
6698
|
+
`;
|
|
6699
|
+
}
|
|
6700
|
+
async onAfterRender() {
|
|
6701
|
+
this.enableFileDrop({
|
|
6702
|
+
dropZoneSelector: ".chat-input-container",
|
|
6703
|
+
multiple: true,
|
|
6704
|
+
acceptedTypes: ["*/*"],
|
|
6705
|
+
// Accept all file types
|
|
6706
|
+
visualFeedback: true,
|
|
6707
|
+
dragOverClass: "drag-over",
|
|
6708
|
+
dragActiveClass: "drag-active"
|
|
6709
|
+
});
|
|
6710
|
+
const textarea = this.element.querySelector(".chat-input");
|
|
6711
|
+
if (textarea) {
|
|
6712
|
+
textarea.addEventListener("input", () => this.autoResizeTextarea(textarea));
|
|
6713
|
+
textarea.addEventListener("keydown", (e) => this.handleKeydown(e));
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
/**
|
|
6717
|
+
* Handle textarea keydown (send on Enter without Shift)
|
|
6718
|
+
*/
|
|
6719
|
+
handleKeydown(event) {
|
|
6720
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
6721
|
+
event.preventDefault();
|
|
6722
|
+
this.onActionSendMessage(event, event.target);
|
|
6723
|
+
}
|
|
6724
|
+
}
|
|
6725
|
+
/**
|
|
6726
|
+
* Handle file drop
|
|
6727
|
+
* @param {File[]} files - Dropped files
|
|
6728
|
+
*/
|
|
6729
|
+
async onFileDrop(files) {
|
|
6730
|
+
for (const file of files) {
|
|
6731
|
+
await this.uploadFile(file);
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
/**
|
|
6735
|
+
* Upload a file
|
|
6736
|
+
* @param {File} file - File to upload
|
|
6737
|
+
*/
|
|
6738
|
+
async uploadFile(file) {
|
|
6739
|
+
const fileModel = new File$1();
|
|
6740
|
+
const uploadId = Date.now() + Math.random();
|
|
6741
|
+
this.addFilePreview(uploadId, file, 0);
|
|
6742
|
+
this.pendingUploads.set(uploadId, { file, fileModel });
|
|
6743
|
+
try {
|
|
6744
|
+
const result = await fileModel.upload({
|
|
6745
|
+
file,
|
|
6746
|
+
onProgress: (progress) => {
|
|
6747
|
+
this.updateFileProgress(uploadId, progress);
|
|
6748
|
+
},
|
|
6749
|
+
onComplete: (uploadResult) => {
|
|
6750
|
+
this.handleUploadComplete(uploadId, uploadResult);
|
|
6751
|
+
}
|
|
6752
|
+
});
|
|
6753
|
+
if (result && result.success) {
|
|
6754
|
+
this.handleUploadComplete(uploadId, result.data);
|
|
6755
|
+
}
|
|
6756
|
+
} catch (error) {
|
|
6757
|
+
console.error("File upload failed:", error);
|
|
6758
|
+
this.handleUploadError(uploadId, error);
|
|
6759
|
+
}
|
|
6760
|
+
}
|
|
6761
|
+
/**
|
|
6762
|
+
* Add file preview to UI
|
|
6763
|
+
* @param {string} uploadId - Unique upload ID
|
|
6764
|
+
* @param {File} file - File object
|
|
6765
|
+
* @param {number} progress - Upload progress (0-100)
|
|
6766
|
+
*/
|
|
6767
|
+
addFilePreview(uploadId, file, progress) {
|
|
6768
|
+
const container = this.element.querySelector('[data-container="attachments"]');
|
|
6769
|
+
if (!container) return;
|
|
6770
|
+
const preview = document.createElement("div");
|
|
6771
|
+
preview.className = "attachment-preview";
|
|
6772
|
+
preview.dataset.uploadId = uploadId;
|
|
6773
|
+
preview.innerHTML = `
|
|
6774
|
+
<div class="attachment-info">
|
|
6775
|
+
<i class="bi bi-file-earmark"></i>
|
|
6776
|
+
<span class="attachment-name">${this.escapeHtml(file.name)}</span>
|
|
6777
|
+
<span class="attachment-size">(${this.formatFileSize(file.size)})</span>
|
|
6778
|
+
</div>
|
|
6779
|
+
<div class="attachment-progress">
|
|
6780
|
+
<div class="progress" style="height: 4px;">
|
|
6781
|
+
<div class="progress-bar" role="progressbar" style="width: ${progress}%"></div>
|
|
6782
|
+
</div>
|
|
6783
|
+
</div>
|
|
6784
|
+
<button class="attachment-remove btn btn-sm btn-link text-danger" data-action="remove-attachment" data-upload-id="${uploadId}" type="button">
|
|
6785
|
+
<i class="bi bi-x"></i>
|
|
6786
|
+
</button>
|
|
6787
|
+
`;
|
|
6788
|
+
container.appendChild(preview);
|
|
6789
|
+
}
|
|
6790
|
+
/**
|
|
6791
|
+
* Update file upload progress
|
|
6792
|
+
* @param {string} uploadId - Upload ID
|
|
6793
|
+
* @param {number} progress - Progress (0-100)
|
|
6794
|
+
*/
|
|
6795
|
+
updateFileProgress(uploadId, progress) {
|
|
6796
|
+
const preview = this.element.querySelector(`[data-upload-id="${uploadId}"]`);
|
|
6797
|
+
if (preview) {
|
|
6798
|
+
const progressBar = preview.querySelector(".progress-bar");
|
|
6799
|
+
if (progressBar) {
|
|
6800
|
+
progressBar.style.width = `${progress}%`;
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
}
|
|
6804
|
+
/**
|
|
6805
|
+
* Handle upload completion
|
|
6806
|
+
* @param {string} uploadId - Upload ID
|
|
6807
|
+
* @param {Object} result - Upload result data
|
|
6808
|
+
*/
|
|
6809
|
+
handleUploadComplete(uploadId, result) {
|
|
6810
|
+
this.attachments.push(result);
|
|
6811
|
+
this.pendingUploads.delete(uploadId);
|
|
6812
|
+
const preview = this.element.querySelector(`[data-upload-id="${uploadId}"]`);
|
|
6813
|
+
if (preview) {
|
|
6814
|
+
preview.classList.add("upload-complete");
|
|
6815
|
+
const progressContainer = preview.querySelector(".attachment-progress");
|
|
6816
|
+
if (progressContainer) {
|
|
6817
|
+
progressContainer.remove();
|
|
6818
|
+
}
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
/**
|
|
6822
|
+
* Handle upload error
|
|
6823
|
+
* @param {string} uploadId - Upload ID
|
|
6824
|
+
* @param {Error} error - Error object
|
|
6825
|
+
*/
|
|
6826
|
+
handleUploadError(uploadId, error) {
|
|
6827
|
+
this.pendingUploads.delete(uploadId);
|
|
6828
|
+
const preview = this.element.querySelector(`[data-upload-id="${uploadId}"]`);
|
|
6829
|
+
if (preview) {
|
|
6830
|
+
preview.classList.add("upload-error");
|
|
6831
|
+
preview.querySelector(".attachment-info").innerHTML += `<span class="text-danger ms-2">Upload failed</span>`;
|
|
6832
|
+
}
|
|
6833
|
+
}
|
|
6834
|
+
/**
|
|
6835
|
+
* Remove attachment
|
|
6836
|
+
*/
|
|
6837
|
+
async onActionRemoveAttachment(event, element) {
|
|
6838
|
+
const uploadId = element.dataset.uploadId;
|
|
6839
|
+
this.pendingUploads.delete(uploadId);
|
|
6840
|
+
const preview = this.element.querySelector(`[data-upload-id="${uploadId}"]`);
|
|
6841
|
+
if (preview) {
|
|
6842
|
+
preview.remove();
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
/**
|
|
6846
|
+
* Send message
|
|
6847
|
+
*/
|
|
6848
|
+
async onActionSendMessage(event, element) {
|
|
6849
|
+
const textarea = this.element.querySelector(".chat-input");
|
|
6850
|
+
const text = textarea.value.trim();
|
|
6851
|
+
if (!text && this.attachments.length === 0) {
|
|
6852
|
+
return;
|
|
6853
|
+
}
|
|
6854
|
+
if (this.pendingUploads.size > 0) {
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
this.emit("message:send", {
|
|
6858
|
+
text,
|
|
6859
|
+
files: this.attachments
|
|
6860
|
+
});
|
|
6861
|
+
}
|
|
6862
|
+
/**
|
|
6863
|
+
* Clear input and attachments
|
|
6864
|
+
*/
|
|
6865
|
+
clearInput() {
|
|
6866
|
+
const textarea = this.element.querySelector(".chat-input");
|
|
6867
|
+
if (textarea) {
|
|
6868
|
+
textarea.value = "";
|
|
6869
|
+
textarea.style.height = "auto";
|
|
6870
|
+
}
|
|
6871
|
+
const container = this.element.querySelector('[data-container="attachments"]');
|
|
6872
|
+
if (container) {
|
|
6873
|
+
container.innerHTML = "";
|
|
6874
|
+
}
|
|
6875
|
+
this.attachments = [];
|
|
6876
|
+
this.pendingUploads.clear();
|
|
6877
|
+
}
|
|
6878
|
+
/**
|
|
6879
|
+
* Auto-resize textarea based on content
|
|
6880
|
+
* @param {HTMLTextAreaElement} textarea
|
|
6881
|
+
*/
|
|
6882
|
+
autoResizeTextarea(textarea) {
|
|
6883
|
+
textarea.style.height = "auto";
|
|
6884
|
+
textarea.style.height = Math.min(textarea.scrollHeight, 150) + "px";
|
|
6885
|
+
}
|
|
6886
|
+
/**
|
|
6887
|
+
* Format file size for display
|
|
6888
|
+
* @param {number} bytes
|
|
6889
|
+
* @returns {string}
|
|
6890
|
+
*/
|
|
6891
|
+
formatFileSize(bytes) {
|
|
6892
|
+
if (bytes === 0) return "0 B";
|
|
6893
|
+
const k = 1024;
|
|
6894
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
6895
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
6896
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
6897
|
+
}
|
|
6898
|
+
/**
|
|
6899
|
+
* Escape HTML to prevent XSS
|
|
6900
|
+
* @param {string} text
|
|
6901
|
+
* @returns {string}
|
|
6902
|
+
*/
|
|
6903
|
+
escapeHtml(text) {
|
|
6904
|
+
const div = document.createElement("div");
|
|
6905
|
+
div.textContent = text;
|
|
6906
|
+
return div.innerHTML;
|
|
6907
|
+
}
|
|
6908
|
+
}
|
|
6909
|
+
applyFileDropMixin(ChatInputView);
|
|
6910
|
+
class ChatView extends View {
|
|
6911
|
+
constructor(options = {}) {
|
|
6912
|
+
super({
|
|
6913
|
+
className: "chat-view",
|
|
6914
|
+
...options
|
|
6915
|
+
});
|
|
6916
|
+
this.adapter = options.adapter;
|
|
6917
|
+
this.theme = options.theme || "compact";
|
|
6918
|
+
this.currentUserId = options.currentUserId;
|
|
6919
|
+
this.inputPlaceholder = options.inputPlaceholder || "Type a message...";
|
|
6920
|
+
this.inputButtonText = options.inputButtonText || "Send";
|
|
6921
|
+
this.messages = [];
|
|
6922
|
+
this.messageViews = /* @__PURE__ */ new Map();
|
|
6923
|
+
}
|
|
6924
|
+
getTemplate() {
|
|
6925
|
+
return `
|
|
6926
|
+
<div class="chat-container chat-theme-${this.theme}">
|
|
6927
|
+
<div class="chat-messages" data-container="messages"></div>
|
|
6928
|
+
<div class="chat-input-wrapper" data-container="input"></div>
|
|
6929
|
+
</div>
|
|
6930
|
+
`;
|
|
6931
|
+
}
|
|
6932
|
+
async onInit() {
|
|
6933
|
+
this.messages = await this.adapter.fetch();
|
|
6934
|
+
this.inputView = new ChatInputView({
|
|
6935
|
+
containerId: "input",
|
|
6936
|
+
placeholder: this.inputPlaceholder,
|
|
6937
|
+
buttonText: this.inputButtonText
|
|
6938
|
+
});
|
|
6939
|
+
this.addChild(this.inputView);
|
|
6940
|
+
this.inputView.on("message:send", async (data) => {
|
|
6941
|
+
await this.handleSendMessage(data);
|
|
6942
|
+
});
|
|
6943
|
+
}
|
|
6944
|
+
async onAfterRender() {
|
|
6945
|
+
this._buildMessageViews();
|
|
6946
|
+
await this._renderChildren();
|
|
6947
|
+
this.scrollToBottom();
|
|
6948
|
+
}
|
|
6949
|
+
/**
|
|
6950
|
+
* Render child message views (similar to ListView._renderChildren)
|
|
6951
|
+
* @private
|
|
6952
|
+
*/
|
|
6953
|
+
async _renderChildren() {
|
|
6954
|
+
await super._renderChildren();
|
|
6955
|
+
const messagesContainer = this.element.querySelector('[data-container="messages"]');
|
|
6956
|
+
if (!messagesContainer) {
|
|
6957
|
+
console.error("ChatView: messages container not found");
|
|
6958
|
+
return;
|
|
6959
|
+
}
|
|
6960
|
+
this.messageViews.forEach((messageView) => {
|
|
6961
|
+
messagesContainer.appendChild(messageView.element);
|
|
6962
|
+
messageView.render(false);
|
|
6963
|
+
});
|
|
6964
|
+
}
|
|
6965
|
+
/**
|
|
6966
|
+
* Build message views for all messages (similar to ListView._buildItems)
|
|
6967
|
+
* @private
|
|
6968
|
+
*/
|
|
6969
|
+
_buildMessageViews() {
|
|
6970
|
+
if (!this.messages || this.messages.length === 0) return;
|
|
6971
|
+
this.messages.forEach((message) => {
|
|
6972
|
+
if (!this.messageViews.has(message.id)) {
|
|
6973
|
+
this._createMessageView(message);
|
|
6974
|
+
}
|
|
6975
|
+
});
|
|
6976
|
+
}
|
|
6977
|
+
/**
|
|
6978
|
+
* Create a message view (similar to ListView._createItemView)
|
|
6979
|
+
* @private
|
|
6980
|
+
*/
|
|
6981
|
+
_createMessageView(message) {
|
|
6982
|
+
if (this.messageViews.has(message.id)) return;
|
|
6983
|
+
const isCurrentUser = message.author && message.author.id === this.currentUserId;
|
|
6984
|
+
const messageView = new ChatMessageView({
|
|
6985
|
+
message,
|
|
6986
|
+
theme: this.theme,
|
|
6987
|
+
isCurrentUser
|
|
6988
|
+
});
|
|
6989
|
+
this.addChild(messageView);
|
|
6990
|
+
this.messageViews.set(message.id, messageView);
|
|
6991
|
+
return messageView;
|
|
6992
|
+
}
|
|
6993
|
+
/**
|
|
6994
|
+
* Add a new message to the chat (for real-time updates)
|
|
6995
|
+
* @param {Object} message - Message data
|
|
6996
|
+
* @param {boolean} scroll - Whether to scroll to bottom after adding
|
|
6997
|
+
*/
|
|
6998
|
+
addMessage(message, scroll = true) {
|
|
6999
|
+
if (this.messageViews.has(message.id)) return;
|
|
7000
|
+
const messageView = this._createMessageView(message);
|
|
7001
|
+
if (this.isMounted()) {
|
|
7002
|
+
const messagesContainer = this.element.querySelector('[data-container="messages"]');
|
|
7003
|
+
if (messagesContainer) {
|
|
7004
|
+
messagesContainer.appendChild(messageView.element);
|
|
7005
|
+
messageView.render(false);
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
7008
|
+
if (scroll) {
|
|
7009
|
+
this.scrollToBottom();
|
|
7010
|
+
}
|
|
7011
|
+
}
|
|
7012
|
+
/**
|
|
7013
|
+
* Handle sending a new message
|
|
7014
|
+
* @param {Object} data - Message data {text, files}
|
|
7015
|
+
* @private
|
|
7016
|
+
*/
|
|
7017
|
+
async handleSendMessage(data) {
|
|
7018
|
+
try {
|
|
7019
|
+
const result = await this.adapter.addNote(data);
|
|
7020
|
+
if (result.success) {
|
|
7021
|
+
this.messages = await this.adapter.fetch();
|
|
7022
|
+
this.messages.forEach((message) => {
|
|
7023
|
+
if (!this.messageViews.has(message.id)) {
|
|
7024
|
+
this.addMessage(message, true);
|
|
7025
|
+
}
|
|
7026
|
+
});
|
|
7027
|
+
this.inputView.clearInput();
|
|
7028
|
+
}
|
|
7029
|
+
} catch (error) {
|
|
7030
|
+
console.error("Failed to send message:", error);
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
/**
|
|
7034
|
+
* Scroll chat to bottom
|
|
7035
|
+
*/
|
|
7036
|
+
scrollToBottom() {
|
|
7037
|
+
const container = this.element.querySelector(".chat-messages");
|
|
7038
|
+
if (container) {
|
|
7039
|
+
requestAnimationFrame(() => {
|
|
7040
|
+
container.scrollTop = container.scrollHeight;
|
|
7041
|
+
});
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
/**
|
|
7045
|
+
* Clear all messages
|
|
7046
|
+
*/
|
|
7047
|
+
clearMessages() {
|
|
7048
|
+
this.messageViews.forEach((view) => view.destroy());
|
|
7049
|
+
this.messageViews.clear();
|
|
7050
|
+
this.messages = [];
|
|
7051
|
+
const container = this.element.querySelector('[data-container="messages"]');
|
|
7052
|
+
if (container) {
|
|
7053
|
+
container.innerHTML = "";
|
|
7054
|
+
}
|
|
7055
|
+
}
|
|
7056
|
+
/**
|
|
7057
|
+
* Refresh messages from adapter
|
|
7058
|
+
*/
|
|
7059
|
+
async refresh() {
|
|
7060
|
+
this.clearMessages();
|
|
7061
|
+
this.messages = await this.adapter.fetch();
|
|
7062
|
+
this._buildMessageViews();
|
|
7063
|
+
if (this.isMounted()) {
|
|
7064
|
+
await this._renderChildren();
|
|
7065
|
+
this.scrollToBottom();
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
6536
7069
|
export {
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
7070
|
+
JobForms as $,
|
|
7071
|
+
IncidentEventList as A,
|
|
7072
|
+
IncidentEventForms as B,
|
|
7073
|
+
ChatView as C,
|
|
7074
|
+
Incident as D,
|
|
6542
7075
|
EmailDomain as E,
|
|
6543
7076
|
FilePreviewView as F,
|
|
6544
|
-
|
|
6545
|
-
|
|
7077
|
+
IncidentList as G,
|
|
7078
|
+
IncidentForms as H,
|
|
6546
7079
|
IncidentEvent as I,
|
|
6547
|
-
|
|
6548
|
-
|
|
7080
|
+
IncidentRuleSet as J,
|
|
7081
|
+
IncidentRuleSetList as K,
|
|
6549
7082
|
ListView as L,
|
|
6550
7083
|
Member as M,
|
|
6551
|
-
|
|
6552
|
-
|
|
7084
|
+
IncidentRule as N,
|
|
7085
|
+
IncidentRuleList as O,
|
|
6553
7086
|
ProgressView as P,
|
|
6554
|
-
|
|
6555
|
-
|
|
7087
|
+
IncidentHistory as Q,
|
|
7088
|
+
IncidentHistoryList as R,
|
|
6556
7089
|
S3Bucket as S,
|
|
6557
7090
|
TableView as T,
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
7091
|
+
RuleSet as U,
|
|
7092
|
+
RuleSetList as V,
|
|
7093
|
+
Rule as W,
|
|
7094
|
+
RuleList as X,
|
|
7095
|
+
IncidentStats as Y,
|
|
7096
|
+
Job as Z,
|
|
7097
|
+
JobList as _,
|
|
6565
7098
|
TableRow as a,
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
7099
|
+
JobLog as a0,
|
|
7100
|
+
JobLogList as a1,
|
|
7101
|
+
JobEvent as a2,
|
|
7102
|
+
JobEventList as a3,
|
|
7103
|
+
JobsEngineStats as a4,
|
|
7104
|
+
JobRunner as a5,
|
|
7105
|
+
JobRunnerList as a6,
|
|
7106
|
+
JobRunnerForms as a7,
|
|
7107
|
+
Log as a8,
|
|
7108
|
+
LogList as a9,
|
|
7109
|
+
MemberList as aa,
|
|
7110
|
+
MemberForms as ab,
|
|
7111
|
+
MetricsPermission as ac,
|
|
7112
|
+
MetricsPermissionList as ad,
|
|
7113
|
+
MetricsForms as ae,
|
|
7114
|
+
PushDevice as af,
|
|
7115
|
+
PushDeviceList as ag,
|
|
7116
|
+
PushTemplate as ah,
|
|
7117
|
+
PushTemplateList as ai,
|
|
7118
|
+
PushConfig as aj,
|
|
7119
|
+
PushConfigList as ak,
|
|
7120
|
+
PushDelivery as al,
|
|
7121
|
+
PushDeliveryList as am,
|
|
7122
|
+
PushConfigForms as an,
|
|
7123
|
+
PushTemplateForms as ao,
|
|
7124
|
+
GeoLocatedIP as ap,
|
|
7125
|
+
GeoLocatedIPList as aq,
|
|
7126
|
+
Ticket as ar,
|
|
7127
|
+
TicketList as as,
|
|
7128
|
+
TicketNote as at,
|
|
7129
|
+
TicketNoteList as au,
|
|
7130
|
+
TicketForms as av,
|
|
7131
|
+
TicketCategories as aw,
|
|
6596
7132
|
TablePage as b,
|
|
6597
7133
|
ListViewItem as c,
|
|
6598
7134
|
TabView as d,
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
7135
|
+
ChatMessageView as e,
|
|
7136
|
+
ChatInputView as f,
|
|
7137
|
+
FileUpload as g,
|
|
7138
|
+
S3BucketList as h,
|
|
7139
|
+
S3BucketForms as i,
|
|
7140
|
+
EmailDomainList as j,
|
|
7141
|
+
EmailDomainForms as k,
|
|
7142
|
+
Mailbox as l,
|
|
7143
|
+
MailboxList as m,
|
|
7144
|
+
MailboxForms as n,
|
|
7145
|
+
SentMessage as o,
|
|
7146
|
+
SentMessageList as p,
|
|
7147
|
+
SentMessageForms as q,
|
|
7148
|
+
EmailTemplate as r,
|
|
7149
|
+
EmailTemplateList as s,
|
|
7150
|
+
EmailTemplateForms as t,
|
|
7151
|
+
FileManager as u,
|
|
7152
|
+
FileManagerList as v,
|
|
7153
|
+
FileManagerForms as w,
|
|
7154
|
+
File$1 as x,
|
|
7155
|
+
FileList as y,
|
|
7156
|
+
FileForms as z
|
|
6621
7157
|
};
|
|
6622
|
-
//# sourceMappingURL=
|
|
7158
|
+
//# sourceMappingURL=ChatView-DkK2PBny.js.map
|