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.
Files changed (66) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +29 -187
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chat.css +392 -0
  12. package/dist/chunks/ChatView-D4ZiYBzL.js +2 -0
  13. package/dist/chunks/ChatView-D4ZiYBzL.js.map +1 -0
  14. package/dist/chunks/{FilePreviewView-CehLQVJa.js → ChatView-DkK2PBny.js} +620 -84
  15. package/dist/chunks/ChatView-DkK2PBny.js.map +1 -0
  16. package/dist/chunks/{ContextMenu-BjQymcpo.js → ContextMenu-A1PxR6Zt.js} +2 -2
  17. package/dist/chunks/{ContextMenu-BjQymcpo.js.map → ContextMenu-A1PxR6Zt.js.map} +1 -1
  18. package/dist/chunks/{ContextMenu-C0sdb9u5.js → ContextMenu-B4LTfsNs.js} +2 -2
  19. package/dist/chunks/{ContextMenu-C0sdb9u5.js.map → ContextMenu-B4LTfsNs.js.map} +1 -1
  20. package/dist/chunks/{DataView-DDe4NgiH.js → DataView-BIfnT2oG.js} +2 -2
  21. package/dist/chunks/{DataView-DDe4NgiH.js.map → DataView-BIfnT2oG.js.map} +1 -1
  22. package/dist/chunks/{DataView-Cl4kRP-W.js → DataView-DGEl3x72.js} +2 -2
  23. package/dist/chunks/{DataView-Cl4kRP-W.js.map → DataView-DGEl3x72.js.map} +1 -1
  24. package/dist/chunks/{Dialog-upGYwvdZ.js → Dialog-DtSluuiz.js} +5 -5
  25. package/dist/chunks/{Dialog-upGYwvdZ.js.map → Dialog-DtSluuiz.js.map} +1 -1
  26. package/dist/chunks/{Dialog-DAQdjRci.js → Dialog-pH-ua3tF.js} +2 -2
  27. package/dist/chunks/{Dialog-DAQdjRci.js.map → Dialog-pH-ua3tF.js.map} +1 -1
  28. package/dist/chunks/{FormView-B7pwdxqX.js → FormView-CDG8qMdU.js} +3 -3
  29. package/dist/chunks/FormView-CDG8qMdU.js.map +1 -0
  30. package/dist/chunks/{FormView-C9PgTIH2.js → FormView-DrvkHPA-.js} +99 -10
  31. package/dist/chunks/FormView-DrvkHPA-.js.map +1 -0
  32. package/dist/chunks/{MetricsChart-TwtfTNEA.js → MetricsChart-Bo0ky0UE.js} +2 -2
  33. package/dist/chunks/{MetricsChart-TwtfTNEA.js.map → MetricsChart-Bo0ky0UE.js.map} +1 -1
  34. package/dist/chunks/{MetricsChart-x15UvXRk.js → MetricsChart-DJU0UetE.js} +3 -3
  35. package/dist/chunks/{MetricsChart-x15UvXRk.js.map → MetricsChart-DJU0UetE.js.map} +1 -1
  36. package/dist/chunks/{PDFViewer-tK1fntDR.js → PDFViewer-8HA3oUBX.js} +2 -2
  37. package/dist/chunks/{PDFViewer-tK1fntDR.js.map → PDFViewer-8HA3oUBX.js.map} +1 -1
  38. package/dist/chunks/{PDFViewer---ebUwfT.js → PDFViewer-CydWqbUF.js} +3 -3
  39. package/dist/chunks/{PDFViewer---ebUwfT.js.map → PDFViewer-CydWqbUF.js.map} +1 -1
  40. package/dist/chunks/{Page-q5Gme_r0.js → Page-dP8b4UdB.js} +2 -2
  41. package/dist/chunks/{Page-q5Gme_r0.js.map → Page-dP8b4UdB.js.map} +1 -1
  42. package/dist/chunks/{Page-BUXs6-fN.js → Page-nrJqRb7x.js} +2 -2
  43. package/dist/chunks/{Page-BUXs6-fN.js.map → Page-nrJqRb7x.js.map} +1 -1
  44. package/dist/chunks/{TopNav-os-U9FQP.js → TopNav-DfikMNXj.js} +2 -2
  45. package/dist/chunks/{TopNav-os-U9FQP.js.map → TopNav-DfikMNXj.js.map} +1 -1
  46. package/dist/chunks/{TopNav-BOMPjms6.js → TopNav-jh5jqV3e.js} +2 -2
  47. package/dist/chunks/{TopNav-BOMPjms6.js.map → TopNav-jh5jqV3e.js.map} +1 -1
  48. package/dist/chunks/{WebApp-BONphh_h.js → WebApp-BdmNWAj2.js} +13 -17
  49. package/dist/chunks/{WebApp-BONphh_h.js.map → WebApp-BdmNWAj2.js.map} +1 -1
  50. package/dist/chunks/{WebApp-DUxotRc-.js → WebApp-D801tYpF.js} +2 -2
  51. package/dist/chunks/{WebApp-DUxotRc-.js.map → WebApp-D801tYpF.js.map} +1 -1
  52. package/dist/css/web-mojo.css +1 -1
  53. package/dist/docit.cjs.js +1 -1
  54. package/dist/docit.es.js +5 -5
  55. package/dist/index.cjs.js +1 -1
  56. package/dist/index.cjs.js.map +1 -1
  57. package/dist/index.es.js +86 -83
  58. package/dist/index.es.js.map +1 -1
  59. package/dist/lightbox.cjs.js +1 -1
  60. package/dist/lightbox.es.js +4 -4
  61. package/package.json +2 -2
  62. package/dist/chunks/FilePreviewView-7-RBir4g.js +0 -2
  63. package/dist/chunks/FilePreviewView-7-RBir4g.js.map +0 -1
  64. package/dist/chunks/FilePreviewView-CehLQVJa.js.map +0 -1
  65. package/dist/chunks/FormView-B7pwdxqX.js.map +0 -1
  66. 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-BjQymcpo.js";
2
- import { r as rest, V as View, d as dataFormatter, M as Mustache } from "./WebApp-BONphh_h.js";
3
- import { P as Page } from "./Page-q5Gme_r0.js";
4
- import Dialog from "./Dialog-upGYwvdZ.js";
5
- import { F as FormView } from "./FormView-C9PgTIH2.js";
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
- { name: "title", type: "text", label: "Title", required: true, cols: 12 },
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
- { name: "category", type: "select", label: "Category", options: TicketCategoriesOptions, cols: 12 },
2572
- { name: "priority", type: "number", label: "Priority", value: 5, cols: 6 },
2573
- { name: "status", type: "select", label: "Status", value: "open", options: ["new", "open", "paused", "resolved", "qa", "ignored"], cols: 6 },
2574
- { type: "collection", name: "assignee", label: "Assignee", Collection: UserList, labelField: "display_name", valueField: "id", cols: 12 },
2575
- { type: "collection", name: "incident", label: "Incident", Collection: IncidentList, labelField: "title", valueField: "id", cols: 12 }
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
- JobEvent as $,
6538
- Incident as A,
6539
- IncidentList as B,
6540
- IncidentForms as C,
6541
- IncidentRuleSet as D,
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
- IncidentRuleSetList as G,
6545
- IncidentRule as H,
7077
+ IncidentList as G,
7078
+ IncidentForms as H,
6546
7079
  IncidentEvent as I,
6547
- IncidentRuleList as J,
6548
- IncidentHistory as K,
7080
+ IncidentRuleSet as J,
7081
+ IncidentRuleSetList as K,
6549
7082
  ListView as L,
6550
7083
  Member as M,
6551
- IncidentHistoryList as N,
6552
- RuleSetList as O,
7084
+ IncidentRule as N,
7085
+ IncidentRuleList as O,
6553
7086
  ProgressView as P,
6554
- Rule as Q,
6555
- RuleSet as R,
7087
+ IncidentHistory as Q,
7088
+ IncidentHistoryList as R,
6556
7089
  S3Bucket as S,
6557
7090
  TableView as T,
6558
- RuleList as U,
6559
- IncidentStats as V,
6560
- Job as W,
6561
- JobList as X,
6562
- JobForms as Y,
6563
- JobLog as Z,
6564
- JobLogList as _,
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
- JobEventList as a0,
6567
- JobsEngineStats as a1,
6568
- JobRunner as a2,
6569
- JobRunnerList as a3,
6570
- JobRunnerForms as a4,
6571
- Log as a5,
6572
- LogList as a6,
6573
- MemberList as a7,
6574
- MemberForms as a8,
6575
- MetricsPermission as a9,
6576
- MetricsPermissionList as aa,
6577
- MetricsForms as ab,
6578
- PushDevice as ac,
6579
- PushDeviceList as ad,
6580
- PushTemplate as ae,
6581
- PushTemplateList as af,
6582
- PushConfig as ag,
6583
- PushConfigList as ah,
6584
- PushDelivery as ai,
6585
- PushDeliveryList as aj,
6586
- PushConfigForms as ak,
6587
- PushTemplateForms as al,
6588
- GeoLocatedIP as am,
6589
- GeoLocatedIPList as an,
6590
- Ticket as ao,
6591
- TicketList as ap,
6592
- TicketNote as aq,
6593
- TicketNoteList as ar,
6594
- TicketForms as as,
6595
- TicketCategories as at,
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
- FileUpload as e,
6600
- S3BucketList as f,
6601
- S3BucketForms as g,
6602
- EmailDomainList as h,
6603
- EmailDomainForms as i,
6604
- Mailbox as j,
6605
- MailboxList as k,
6606
- MailboxForms as l,
6607
- SentMessage as m,
6608
- SentMessageList as n,
6609
- SentMessageForms as o,
6610
- EmailTemplate as p,
6611
- EmailTemplateList as q,
6612
- EmailTemplateForms as r,
6613
- FileManager as s,
6614
- FileManagerList as t,
6615
- FileManagerForms as u,
6616
- File$1 as v,
6617
- FileList as w,
6618
- FileForms as x,
6619
- IncidentEventList as y,
6620
- IncidentEventForms as z
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=FilePreviewView-CehLQVJa.js.map
7158
+ //# sourceMappingURL=ChatView-DkK2PBny.js.map