web-mojo 2.1.1043 → 2.1.1044
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/README.md +19 -16
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +12 -10
- 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.css +305 -266
- package/dist/auth.es.js +537 -2175
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +3 -2
- package/dist/charts.es.js.map +1 -1
- package/dist/chunks/ChatView-Bvkdj-lq.js +2 -0
- package/dist/chunks/{ChatView-CGBaudUc.js.map → ChatView-Bvkdj-lq.js.map} +1 -1
- package/dist/chunks/{ChatView-DguKw-gR.js → ChatView-DBgQzOyI.js} +5 -6
- package/dist/chunks/{ChatView-DguKw-gR.js.map → ChatView-DBgQzOyI.js.map} +1 -1
- package/dist/chunks/{Collection-YRfGoT73.js → Collection-DaTm-2LH.js} +2 -2
- package/dist/chunks/{Collection-YRfGoT73.js.map → Collection-DaTm-2LH.js.map} +1 -1
- package/dist/chunks/{ContextMenu-B4_YS0G8.js → ContextMenu-hQH_6Pyi.js} +349 -2
- package/dist/chunks/ContextMenu-hQH_6Pyi.js.map +1 -0
- package/dist/chunks/ContextMenu-snx9Dd1s.js +3 -0
- package/dist/chunks/ContextMenu-snx9Dd1s.js.map +1 -0
- package/dist/chunks/{DataView-OUqaLmGB.js → DataView-CWejLV3B.js} +2 -1
- package/dist/chunks/DataView-CWejLV3B.js.map +1 -0
- package/dist/chunks/DataView-D7j4IWyS.js +2 -0
- package/dist/chunks/DataView-D7j4IWyS.js.map +1 -0
- package/dist/chunks/Dialog-7T8ENHYD.js +2 -0
- package/dist/chunks/Dialog-7T8ENHYD.js.map +1 -0
- package/dist/chunks/{Dialog-BiVgKzSK.js → Dialog-BcJG5Vta.js} +1358 -4
- package/dist/chunks/Dialog-BcJG5Vta.js.map +1 -0
- package/dist/chunks/{ListView-BMNhd5-B.js → ListView-BrsQ26R6.js} +2 -2
- package/dist/chunks/{ListView-BMNhd5-B.js.map → ListView-BrsQ26R6.js.map} +1 -1
- package/dist/chunks/MetricsMiniChartWidget-CN1HPnWf.js +2 -0
- package/dist/chunks/{MetricsMiniChartWidget-Esvv-lFp.js.map → MetricsMiniChartWidget-CN1HPnWf.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js → MetricsMiniChartWidget-DALWxrzu.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js.map → MetricsMiniChartWidget-DALWxrzu.js.map} +1 -1
- package/dist/chunks/{PDFViewer-NeL91Gon.js → PDFViewer-CgdSGU1n.js} +2 -2
- package/dist/chunks/{PDFViewer-NeL91Gon.js.map → PDFViewer-CgdSGU1n.js.map} +1 -1
- package/dist/chunks/{PDFViewer-D4uo3oiA.js → PDFViewer-DtJIlPXi.js} +2 -2
- package/dist/chunks/{PDFViewer-D4uo3oiA.js.map → PDFViewer-DtJIlPXi.js.map} +1 -1
- package/dist/chunks/{TopNav-DC8oGpHp.js → TokenManager-BanwFrq7.js} +368 -5
- package/dist/chunks/TokenManager-BanwFrq7.js.map +1 -0
- package/dist/chunks/TokenManager-DIEFCQ3B.js +2 -0
- package/dist/chunks/TokenManager-DIEFCQ3B.js.map +1 -0
- package/dist/chunks/version-BaFu2yii.js +38 -0
- package/dist/chunks/version-BaFu2yii.js.map +1 -0
- package/dist/chunks/version-WMgX72-y.js +2 -0
- package/dist/chunks/version-WMgX72-y.js.map +1 -0
- package/dist/css/web-mojo.css +1 -17
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.cjs.js.map +1 -1
- package/dist/docit.es.js +5 -7
- package/dist/docit.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +30 -32
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.cjs.js.map +1 -1
- package/dist/lightbox.es.js +4 -3
- package/dist/lightbox.es.js.map +1 -1
- package/dist/map.es.js +1 -1
- package/dist/timeline.es.js +2 -2
- package/package.json +1 -1
- package/dist/chunks/ChatView-CGBaudUc.js +0 -2
- package/dist/chunks/ContextMenu-B4_YS0G8.js.map +0 -1
- package/dist/chunks/ContextMenu-DcLhcYMp.js +0 -3
- package/dist/chunks/ContextMenu-DcLhcYMp.js.map +0 -1
- package/dist/chunks/DataView-CdDY9ijM.js +0 -2
- package/dist/chunks/DataView-CdDY9ijM.js.map +0 -1
- package/dist/chunks/DataView-OUqaLmGB.js.map +0 -1
- package/dist/chunks/Dialog-BiVgKzSK.js.map +0 -1
- package/dist/chunks/Dialog-DmIPK_Bi.js +0 -2
- package/dist/chunks/Dialog-DmIPK_Bi.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-Esvv-lFp.js +0 -2
- package/dist/chunks/Page-CvbwEoLv.js +0 -2
- package/dist/chunks/Page-CvbwEoLv.js.map +0 -1
- package/dist/chunks/Page-Deq4y2Kq.js +0 -351
- package/dist/chunks/Page-Deq4y2Kq.js.map +0 -1
- package/dist/chunks/TokenManager-CAZNcCMs.js +0 -366
- package/dist/chunks/TokenManager-CAZNcCMs.js.map +0 -1
- package/dist/chunks/TokenManager-CJBYcVqs.js +0 -2
- package/dist/chunks/TokenManager-CJBYcVqs.js.map +0 -1
- package/dist/chunks/TopNav-23B5R-dl.js +0 -2
- package/dist/chunks/TopNav-23B5R-dl.js.map +0 -1
- package/dist/chunks/TopNav-DC8oGpHp.js.map +0 -1
- package/dist/chunks/WebApp-C1vcdSuu.js +0 -1388
- package/dist/chunks/WebApp-C1vcdSuu.js.map +0 -1
- package/dist/chunks/WebApp-CpxtmTk0.js +0 -2
- package/dist/chunks/WebApp-CpxtmTk0.js.map +0 -1
package/dist/map.es.js
CHANGED
package/dist/timeline.es.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { L as ListViewItem, a as ListView } from "./chunks/ListView-
|
|
1
|
+
import { L as ListViewItem, a as ListView } from "./chunks/ListView-BrsQ26R6.js";
|
|
2
2
|
import { d as dataFormatter } from "./chunks/Rest-CS4jRCAs.js";
|
|
3
3
|
import { V } from "./chunks/Rest-CS4jRCAs.js";
|
|
4
|
-
import { C, M } from "./chunks/Collection-
|
|
4
|
+
import { C, M } from "./chunks/Collection-DaTm-2LH.js";
|
|
5
5
|
class TimelineViewItem extends ListViewItem {
|
|
6
6
|
constructor(options = {}) {
|
|
7
7
|
super({
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const e=require("./Collection-DD1_31eh.js"),t=require("./Rest-BNYqGlnP.js"),s=require("./ContextMenu-DcLhcYMp.js"),i=require("./Page-CvbwEoLv.js"),a=require("./Dialog-DmIPK_Bi.js"),n=require("./ListView-BRGiITfD.js"),l=require("./FormView-nulck4nL.js");class S3Bucket extends e.Model{constructor(e={}){super(e,{endpoint:"/api/aws/s3/bucket"})}}class S3BucketList extends e.Collection{constructor(e={}){super({ModelClass:S3Bucket,endpoint:"/api/aws/s3/bucket",size:10,...e})}}class EmailDomain extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/domain",...t})}async onboard(e={},t={}){if(!this.id)return await this.showError("Cannot onboard domain without ID"),{success:!1,status:400,error:"Missing domain id"};try{const s=`${this.buildUrl(this.id)}/onboard`;return await this.rest.POST(s,e,t.params)}catch(s){return{success:!1,status:s?.status||500,error:s?.message||"Failed to onboard domain"}}}async audit(e={}){if(!this.id)return await this.showError("Cannot audit domain without ID"),{success:!1,status:400,error:"Missing domain id"};const t=(e.method||"GET").toUpperCase(),s=`${this.buildUrl(this.id)}/audit`;try{return"POST"===t?await this.rest.POST(s,e.data||{},e.params):await this.rest.GET(s,e.params)}catch(i){return{success:!1,status:i?.status||500,error:i?.message||"Failed to audit domain"}}}async reconcile(e={},t={}){if(!this.id)return await this.showError("Cannot reconcile domain without ID"),{success:!1,status:400,error:"Missing domain id"};try{const s=`${this.buildUrl(this.id)}/reconcile`;return await this.rest.POST(s,e,t.params)}catch(s){return{success:!1,status:s?.status||500,error:s?.message||"Failed to reconcile domain"}}}static async onboardById(e,t={},s={}){const i=new EmailDomain({id:e},s);return await i.onboard(t,s)}static async auditById(e,t={}){const s=new EmailDomain({id:e},t);return await s.audit(t)}static async reconcileById(e,t={},s={}){const i=new EmailDomain({id:e},s);return await i.reconcile(t,s)}}class EmailDomainList extends e.Collection{constructor(e={}){super({ModelClass:EmailDomain,endpoint:"/api/aws/email/domain",size:10,...e})}}class Mailbox extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/mailbox",...t})}}Mailbox.sendEmail=async function(e){return await t.rest.POST("/api/aws/email/send",e)};class MailboxList extends e.Collection{constructor(e={}){super({ModelClass:Mailbox,endpoint:"/api/aws/email/mailbox",size:10,...e})}}const o={create:{title:"Add Mailbox",fields:[{type:"collection",name:"domain",label:"Domain",Collection:EmailDomainList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search domains...",emptyFetch:!1,required:!0,debounceMs:300,columns:12},{name:"email",type:"email",label:"Email Address",placeholder:"support@example.com",required:!0,columns:12},{name:"allow_inbound",type:"switch",label:"Allow Inbound",columns:6},{name:"allow_outbound",type:"switch",label:"Allow Outbound",defaultValue:!0,columns:6},{name:"is_system_default",type:"switch",label:"System Default",columns:6},{name:"is_domain_default",type:"switch",label:"Domain Default",columns:6},{name:"async_handler",type:"text",label:"Async Handler (optional)",placeholder:"myapp.handlers.process_support",columns:12,help:"Module:function to process inbound messages via task system"}]},edit:{title:"Edit Mailbox",fields:[{name:"email",type:"email",label:"Email Address",required:!0,columns:12},{name:"allow_inbound",type:"switch",label:"Allow Inbound",columns:6},{name:"allow_outbound",type:"switch",label:"Allow Outbound",columns:6},{name:"is_system_default",type:"switch",label:"System Default",columns:6},{name:"is_domain_default",type:"switch",label:"Domain Default",columns:6},{name:"async_handler",type:"text",label:"Async Handler (optional)",placeholder:"myapp.handlers.process_support",columns:12}]}};class SentMessage extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/sent",...t})}}class SentMessageList extends e.Collection{constructor(e={}){super({ModelClass:SentMessage,endpoint:"/api/aws/email/sent",size:10,...e})}}class EmailTemplate extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/template",...t})}}class EmailTemplateList extends e.Collection{constructor(e={}){super({ModelClass:EmailTemplate,endpoint:"/api/aws/email/template",size:10,...e})}}class ProgressView extends t.View{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=t.dataFormatter.pipe(this.filesize,"filesize"),this.progress=0,this.percentage=0,this.loaded=0,this.total=this.filesize,this.loadedFormatted="0 B",this.totalFormatted=this.filesizeFormatted,this.status="Starting upload...",this.showCancel=!1!==e.showCancel,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return'\n <div class="progress-view">\n <div class="d-flex justify-content-between align-items-start mb-2">\n <div class="flex-grow-1 min-width-0">\n <div class="fw-medium text-truncate" title="{{filename}}">\n <i class="bi bi-file-earmark me-1"></i>\n {{filename}}\n </div>\n <small class="text-muted">{{status}}</small>\n </div>\n {{#showCancel}}\n <button type="button" \n class="btn btn-sm btn-outline-secondary ms-2" \n data-action="cancel"\n {{#cancelled}}disabled{{/cancelled}}>\n <i class="bi bi-x"></i>\n </button>\n {{/showCancel}}\n </div>\n \n <div class="progress mb-2" style="height: 8px;">\n <div class="progress-bar" \n role="progressbar" \n style="width: {{percentage}}%"\n aria-valuenow="{{percentage}}" \n aria-valuemin="0" \n aria-valuemax="100">\n </div>\n </div>\n \n <div class="d-flex justify-content-between">\n <small class="text-muted">\n {{loadedFormatted}} / {{totalFormatted}}\n </small>\n <small class="text-muted">\n {{percentage}}%\n </small>\n </div>\n </div>\n '}updateProgress(e){this.cancelled||this.completed||(this.progress=e.progress,this.percentage=e.percentage,this.loaded=e.loaded,this.total=e.total||this.filesize,this.loadedFormatted=t.dataFormatter.pipe(this.loaded,"filesize"),this.totalFormatted=t.dataFormatter.pipe(this.total,"filesize"),this.percentage<100?this.status=`Uploading... ${this.percentage}%`:this.status="Finalizing upload...",this.render())}markCompleted(e="Upload completed!"){this.completed=!0,this.progress=1,this.percentage=100,this.status=e,this.render()}markFailed(e="Upload failed"){this.status=e,this.render()}markCancelled(){this.cancelled=!0,this.status="Upload cancelled",this.render()}async onActionCancel(e,t,s){if(!this.cancelled&&!this.completed&&(s.disabled=!0,this.markCancelled(),this.emit("cancel"),"function"==typeof this.onCancel))try{await this.onCancel()}catch(i){console.error("Error in cancel callback:",i)}}setFilename(e){this.filename=e,this.render()}setFilesize(e){this.filesize=e,this.filesizeFormatted=t.dataFormatter.pipe(e,"filesize"),this.total=e,this.totalFormatted=this.filesizeFormatted,this.render()}getPercentage(){return this.percentage}isCompleted(){return this.completed}isCancelled(){return this.cancelled}getStats(){return{filename:this.filename,filesize:this.filesize,progress:this.progress,percentage:this.percentage,loaded:this.loaded,total:this.total,cancelled:this.cancelled,completed:this.completed,status:this.status}}}class FileUpload{constructor(e,t={}){if(this.fileModel=e,this.options={file:null,name:null,group:null,description:null,onProgress:null,onComplete:null,onError:null,showToast:!0,...t},!(this.options.file&&this.options.file instanceof File))throw new Error("FileUpload requires a valid File object");this.cancelled=!1,this.uploadRequest=null,this.progressToast=null,this.progressView=null,this.toastService=null,this.options.showToast&&(this.toastService=new s.ToastService),this.promise=this._startUpload()}async _startUpload(){try{let t,s;this.options.showToast&&this._showProgressToast();try{t=await this._initiateUpload()}catch(e){throw new Error(`Failed to initiate upload: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");if(!t||!t.upload_url)throw new Error("Invalid upload response: missing upload URL");try{s=await this._performUpload(t.upload_url)}catch(e){throw new Error(`File upload failed: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");try{await this._completeUpload()}catch(e){console.warn("Failed to mark upload as completed:",e)}return this._onComplete(this.fileModel),this.fileModel}catch(e){throw"Upload cancelled"!==e.message&&this._onError(e),e}}async _initiateUpload(){try{const e={filename:this.options.name||this.options.file.name,file_size:this.options.file.size,content_type:this.options.file.type};this.options.group&&(e.group=this.options.group),this.options.description&&(e.description=this.options.description);const t=await this.fileModel.rest.POST("/api/fileman/upload/initiate",e);if(!t)throw new Error("No response from upload initiation API");if(!t.data)throw new Error("Upload initiation response missing data");if(!t.data.status){const e=t.data.error||"Upload initiation failed";throw new Error(e)}if(!t.data.data)throw new Error("Upload initiation response missing data payload");return t.data.data.id&&this.fileModel.set("id",t.data.data.id),t.data.data}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload initiation. Please check your connection.");throw e}}async _performUpload(e){return new Promise((t,s)=>{if(!(this.options.file instanceof File))return void s(new Error("Only single File objects are supported"));const i=new XMLHttpRequest;this.uploadRequest=i,i.upload.onprogress=e=>{if(this.cancelled)return;const t={progress:e.loaded/e.total,loaded:e.loaded,total:e.total,percentage:Math.round(e.loaded/e.total*100)};this._onProgress(t)},i.onload=()=>{i.status>=200&&i.status<300?t({data:i.response,status:i.status,statusText:i.statusText,xhr:i}):s(new Error(`Upload failed: ${i.status} ${i.statusText}`))},i.onerror=()=>{s(new Error("Upload failed: Network error"))},i.ontimeout=()=>{s(new Error("Upload failed: Timeout"))},i.onabort=()=>{s(new Error("Upload cancelled"))},i.ontimeout=()=>{s(new Error("Upload timeout - file may be too large or connection too slow"))},i.open("PUT",e),i.setRequestHeader("Content-Type",this.options.file.type),i.timeout=3e4,i.send(this.options.file)})}async _completeUpload(){try{const e=await this.fileModel.save({action:"mark_as_completed"});if(!e)throw new Error("No response from upload completion API");if(e.data&&!e.data.status){const t=e.data.error||"Failed to mark upload as completed";throw new Error(t)}return e}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload completion. The file may have uploaded successfully.");throw e}}_onProgress(e){this.progressToast&&this.progressToast.updateProgress&&this.progressToast.updateProgress(e),"function"==typeof this.options.onProgress&&this.options.onProgress(e)}_onComplete(e){this.progressView&&this.progressView.markCompleted("Upload completed successfully!"),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast:",e)}},2e3),"function"==typeof this.options.onComplete&&this.options.onComplete(e)}_onError(e){if(this.progressToast)try{this.progressToast.hide()}catch(t){console.warn("Error hiding progress toast on error:",t)}this.toastService&&this.toastService.error(`Upload failed: ${e.message}`),"function"==typeof this.options.onError&&this.options.onError(e)}_showProgressToast(){this.progressView=new ProgressView({filename:this.options.name||this.options.file.name,filesize:this.options.file.size,showCancel:!0,onCancel:()=>this.cancel()}),this.progressToast=this.toastService.showView(this.progressView,"info",{title:"File Upload",autohide:!1,dismissible:!1})}cancel(){return!this.cancelled&&(this.cancelled=!0,this.uploadRequest&&"function"==typeof this.uploadRequest.abort&&this.uploadRequest.abort(),this.progressView&&this.progressView.markCancelled(),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast on cancel:",e)}},1500),!0)}isCancelled(){return this.cancelled}then(e,t){return this.promise.then(e,t)}catch(e){return this.promise.catch(e)}finally(e){return this.promise.finally(e)}getStats(){return{filename:this.options.file.name,size:this.options.file.size,type:this.options.file.type,cancelled:this.cancelled,group:this.options.group,description:this.options.description}}}class FileManager extends e.Model{constructor(e={}){super(e,{endpoint:"/api/fileman/manager"})}}class FileManagerList extends e.Collection{constructor(e={}){super({ModelClass:FileManager,endpoint:"/api/fileman/manager",size:10,...e})}}const r={create:{title:"Add Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,value:"s3://BUCKET_NAME/OPTION_FOLDER",placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6}]},edit:{title:"Edit Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"allowed_origins",type:"text",label:"Domains Who Can Upload",cols:12},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6}]},owners:{fields:[{type:"collection",name:"group",label:"Group (Owner)",Collection:s.GroupList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300},{type:"collection",name:"user",label:"User (Owner)",Collection:s.UserList,labelField:"display_name",valueField:"id",maxItems:10,placeholder:"Search users...",emptyFetch:!1,debounceMs:300}]},credentials:{fields:[{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"}]}};let c=class extends e.Model{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return"image"===this.get("category")}upload(e={}){return new FileUpload(this,e)}};class FileList extends e.Collection{constructor(e={}){super({ModelClass:c,endpoint:"/api/fileman/file",size:10,...e})}}class IncidentStats extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/stats",requiresId:!1})}}class IncidentEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event"})}}class IncidentEventList extends e.Collection{constructor(e={}){super({ModelClass:IncidentEvent,endpoint:"/api/incident/event",size:10,...e})}}const d={edit:{title:"Edit Incident Event",fields:[{name:"category",type:"select",label:"Category",placeholder:"Select category",options:()=>Incident.COMPONENTS,editable:!0,force_top:!0,cols:6},{name:"incident",type:"text",label:"Incident",placeholder:"Enter Incident ID",cols:6},{name:"description",type:"textarea",label:"Description",placeholder:"Enter Description",cols:12},{name:"details",type:"textarea",label:"Details",placeholder:"Enter Details",cols:12},{name:"component",type:"text",label:"Component",placeholder:"Enter Component",cols:8},{name:"component_id",type:"text",label:"Component ID",placeholder:"Enter Component ID",cols:4}]}};class Incident extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/incident"})}}class IncidentList extends e.Collection{constructor(e={}){super({ModelClass:Incident,endpoint:"/api/incident/incident",size:10,...e})}}class IncidentRuleSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset"})}}class IncidentRuleSetList extends e.Collection{constructor(e={}){super({ModelClass:IncidentRuleSet,endpoint:"/api/incident/event/ruleset",size:10,...e})}}class IncidentRule extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset/rule"})}}class IncidentRuleList extends e.Collection{constructor(e={}){super({ModelClass:IncidentRule,endpoint:"/api/incident/event/ruleset/rule",size:10,...e})}}class IncidentHistory extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/incident/history"})}}class IncidentHistoryList extends e.Collection{constructor(e={}){super({ModelClass:IncidentHistory,endpoint:"/api/incident/incident/history",size:10,...e})}}const u=[{value:0,label:"No Bundling"},{value:1,label:"By Hostname"},{value:2,label:"By Model Name"},{value:3,label:"By Model Name + ID"},{value:4,label:"By Source IP"},{value:5,label:"By Hostname + Model Name"},{value:6,label:"By Hostname + Model Name + ID"},{value:7,label:"By Source IP + Model Name"},{value:8,label:"By Source IP + Model Name + ID"},{value:9,label:"By Source IP + Hostname"}],h=[{value:0,label:"ALL (must match all rules)"},{value:1,label:"ANY (match any rule)"}],p=[{value:"==",label:"Equal (==)"},{value:"eq",label:"Equal (eq)"},{value:">",label:"Greater Than (>)"},{value:">=",label:"Greater Than or Equal (>=)"},{value:"<",label:"Less Than (<)"},{value:"<=",label:"Less Than or Equal (<=)"},{value:"contains",label:"Contains"},{value:"regex",label:"Regular Expression"}],m=[{value:"str",label:"String"},{value:"int",label:"Integer"},{value:"float",label:"Float"},{value:"bool",label:"Boolean"}],b=[{value:"level",label:"Level",description:"Event level (error, warning, info, debug)",meta:{type:"str"}},{value:"source_ip",label:"Source IP Address",description:"IP address of the event source",meta:{type:"str"}},{value:"rule_id",label:"Rule ID",description:"Numeric rule identifier",meta:{type:"int"}},{value:"hostname",label:"Hostname",description:"Hostname where event occurred",meta:{type:"str"}},{value:"component",label:"Component",description:"System component name",meta:{type:"str"}},{value:"component_id",label:"Component ID",description:"Component identifier",meta:{type:"str"}},{value:"category",label:"Category",description:"Event category (ossec, auth, api_error, etc.)",meta:{type:"str"}},{value:"description",label:"Description",description:"Event description text",meta:{type:"str"}},{value:"details",label:"Details",description:"Additional event details",meta:{type:"str"}},{value:"alert_id",label:"Alert ID",description:"Numeric alert identifier",meta:{type:"int"}},{value:"severity",label:"Severity",description:"Numeric severity level",meta:{type:"int"}},{value:"user",label:"User",description:"Username associated with event",meta:{type:"str"}},{value:"action",label:"Action",description:"Action that triggered the event",meta:{type:"str"}},{value:"status",label:"Status",description:"Status value or code",meta:{type:"str"}},{value:"status_code",label:"Status Code",description:"Numeric status code (e.g., HTTP status)",meta:{type:"int"}},{value:"message",label:"Message",description:"Event message text",meta:{type:"str"}},{value:"path",label:"Path",description:"File path or URL path",meta:{type:"str"}},{value:"title",label:"Title",description:"OSSEC Title",meta:{type:"str"}},{value:"country_code",label:"Country Code",description:"Country code associated with event",meta:{type:"str"}},{value:"region",label:"Region",description:"Region associated with event",meta:{type:"str"}},{value:"city",label:"City",description:"City associated with event",meta:{type:"str"}},{value:"http_user_agent",label:"HTTP User Agent",description:"User agent string associated with event",meta:{type:"str"}},{value:"request_path",label:"Request Path",description:"Request path associated with event",meta:{type:"str"}},{value:"method",label:"Method",description:"HTTP method or function name",meta:{type:"str"}}];class RuleSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset"})}}class RuleSetList extends e.Collection{constructor(e={}){super({ModelClass:RuleSet,endpoint:"/api/incident/event/ruleset",...e})}}const g={create:{title:"Create RuleSet",size:"lg",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter ruleset name",cols:12},{name:"category",type:"text",label:"Category",required:!0,placeholder:"e.g., ossec, auth, api_error",cols:6},{name:"priority",type:"number",label:"Priority",value:10,required:!0,cols:6},{name:"match_by",type:"select",label:"Match Logic",value:0,options:h,cols:12},{name:"bundle_by",type:"select",label:"Bundle By",value:0,options:u,cols:12},{name:"bundle_minutes",type:"number",label:"Bundle Minutes",value:10,placeholder:"Time window for bundling",cols:12},{name:"handler",type:"text",label:"Handler",placeholder:"e.g., email://user@company.com, task://name",cols:12},{name:"is_active",type:"switch",label:"Is Active",value:!0,cols:6}]},edit:{title:"Edit RuleSet",size:"lg",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter ruleset name",cols:12},{name:"category",type:"text",label:"Category",required:!0,placeholder:"e.g., ossec, auth, api_error",cols:6},{name:"priority",type:"number",label:"Priority",required:!0,cols:6},{name:"match_by",type:"select",label:"Match Logic",options:h,cols:12},{name:"bundle_by",type:"select",label:"Bundle By",options:u,cols:12},{name:"bundle_minutes",type:"select",label:"Bundle Minutes",placeholder:"Time window for bundling",options:[{value:0,label:"Disabled - don't bundle by time"},{value:5,label:"5 minutes"},{value:10,label:"10 minutes"},{value:15,label:"15 minutes"},{value:30,label:"30 minutes"},{value:60,label:"1 hour"},{value:120,label:"2 hours"},{value:360,label:"6 hours"},{value:720,label:"12 hours"},{value:1440,label:"1 day"},{value:null,label:"No limit - bundle forever"}],cols:12},{name:"handler",type:"text",label:"Handler",placeholder:"e.g., email://user@company.com, task://name",cols:12},{name:"is_active",type:"switch",label:"Is Active",cols:6}]}};class Rule extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset/rule"})}}class RuleList extends e.Collection{constructor(e={}){super({ModelClass:Rule,endpoint:"/api/incident/event/ruleset/rule",...e})}}const y={create:{title:"Create Rule",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter rule name",cols:12},{name:"field_name",type:"combo",label:"Field Name",required:!0,placeholder:"Select or enter field name...",options:b,allowCustom:!0,showDescription:!0,help:"Select a common field or type a custom field name",cols:12},{name:"comparator",type:"select",label:"Comparator",required:!0,options:p,cols:6},{name:"value_type",type:"select",label:"Value Type",required:!0,options:m,value:"str",cols:6},{name:"value",type:"text",label:"Value",required:!0,placeholder:"Enter comparison value",cols:12}]},edit:{title:"Edit Rule",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter rule name",cols:12},{name:"field_name",type:"combo",label:"Field Name",required:!0,placeholder:"Select or enter field name...",options:b,allowCustom:!0,showDescription:!0,help:"Select a common field or type a custom field name",cols:12},{name:"comparator",type:"select",label:"Comparator",required:!0,options:p,cols:6},{name:"value_type",type:"select",label:"Value Type",required:!0,options:m,cols:6},{name:"value",type:"text",label:"Value",required:!0,placeholder:"Enter comparison value",cols:12}]}};RuleSet.EDIT_FORM=g.edit,RuleSet.ADD_FORM=g.create,RuleSet.CREATE_FORM=g.create,RuleSet.BundleByOptions=u,RuleSet.MatchByOptions=h,Rule.EDIT_FORM=y.edit,Rule.ADD_FORM=y.create,Rule.CREATE_FORM=y.create,Rule.ComparatorOptions=p,Rule.ValueTypeOptions=m;class Job extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/job"})}async cancel(){const e=await this.save({cancel_request:!0});return e.success&&e.data.status&&this.set("cancel_requested",!0),e}async retry(e=null){const t=e?{retry_request:{retry:!0,delay:e}}:{retry_request:!0},s=await this.save(t);return s.success&&s.data.status&&s.data.new_job_id?{...s,newJobId:s.data.new_job_id,originalJobId:s.data.original_job_id}:s}async getDetailedStatus(){const e=await this.save({get_status:!0});return e.success&&e.data.status&&this.set(e.data.data),e}async cloneJob(e={}){const t={publish_job:{payload:e,...e}},s=await this.save(t);return s.success&&s.data.status&&s.data.job_id?{...s,newJobId:s.data.job_id,templateJobId:s.data.template_job_id}:s}isActive(){const e=this.get("status");return["pending","running"].includes(e)}isTerminal(){const e=this.get("status");return["completed","failed","canceled","expired"].includes(e)}canRetry(){const e=this.get("status");return["failed","canceled","expired"].includes(e)&&!1!==this.get("is_retriable")}canCancel(){const e=this.get("status");return["pending","running"].includes(e)&&!this.get("cancel_requested")}getStatusBadgeClass(){return{pending:"bg-primary",running:"bg-success",completed:"bg-info",failed:"bg-danger",canceled:"bg-secondary",expired:"bg-warning"}[this.get("status")]||"bg-secondary"}getStatusIcon(){return{pending:"bi-hourglass",running:"bi-arrow-repeat",completed:"bi-check-circle",failed:"bi-x-octagon",canceled:"bi-x-circle",expired:"bi-clock"}[this.get("status")]||"bi-question-circle"}getEvents(){return this.get("recent_events")||[]}getFormattedDuration(){const e=this.get("duration_ms");return e&&0!==e?e<1e3?`${e}ms`:e<6e4?`${(e/1e3).toFixed(1)}s`:e<36e5?`${(e/6e4).toFixed(1)}m`:`${(e/36e5).toFixed(1)}h`:"N/A"}getQueuePosition(){return this.get("queue_position")}hasExpired(){const e=this.get("expires_at");return!!e&&new Date(e)</* @__PURE__ */new Date}getRunnerId(){return this.get("runner_id")}getPayload(){return this.get("payload")||{}}getMetadata(){return this.get("metadata")||{}}}class JobList extends e.Collection{constructor(e={}){super({ModelClass:Job,endpoint:"/api/jobs/job",...e})}async fetchByStatus(e,t={}){return this.fetch({status:e,...t})}async fetchByChannel(e,t={}){return this.fetch({channel:e,...t})}async fetchPending(e={}){return this.fetchByStatus("pending",e)}async fetchRunning(e={}){return this.fetchByStatus("running",e)}async fetchCompleted(e={}){return this.fetchByStatus("completed",e)}async fetchFailed(e={}){return this.fetchByStatus("failed",e)}async fetchScheduled(e={}){return this.fetch({scheduled:!0,...e})}}Job.publish=async function(e){return await t.rest.POST("/api/jobs/publish",e)},Job.getStats=async function(){const e=await t.rest.GET("/api/jobs/stats");return e.success?e.data:null},Job.getHealth=async function(e=null){const s=e?`/api/jobs/health/${e}`:"/api/jobs/health",i=t.rest.GET(s);return i.success?i.data:null},Job.test=async function(){return await t.rest.POST("/api/jobs/test",{})},Job.tests=async function(){return await t.rest.POST("/api/jobs/tests",{})},Job.clearStuck=async function(e=null){const s=e?{channel:e}:{};return await t.rest.POST("/api/jobs/control/clear-stuck",s)},Job.clearChannel=async function(e){return await t.rest.POST("/api/jobs/control/clear-queue",{channel:e,confirm:"yes"})},Job.cleanConsumers=async function(){return await t.rest.POST("/api/jobs/control/cleanup-consumers")},Job.purgeJobs=async function(e){return await t.rest.POST("/api/jobs/control/purge",{days_old:e})};class JobLog extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/logs"})}}class JobLogList extends e.Collection{constructor(e={}){super({endpoint:"/api/jobs/logs",model:JobLog,...e})}}class JobEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/event"})}}class JobEventList extends e.Collection{constructor(e={}){super({endpoint:"/api/jobs/event",model:JobEvent,...e})}}class JobsEngineStats extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/stats",requiresId:!1})}}class JobRunner extends e.Model{constructor(e={}){e.runner_id&&!e.id&&(e.id=e.runner_id),super(e,{endpoint:"/api/jobs/runners",idAttribute:"runner_id"})}async ping(e=2){const t=this.getApp();if(!t||!t.rest)throw new Error("App or REST client not available");const s=await t.rest.POST("/api/jobs/runners/ping",{runner_id:this.get("runner_id"),timeout:e});return s.success&&s.data.status&&(this.set("last_heartbeat",/* @__PURE__ */(new Date).toISOString()),this.set("ping_status",s.data.ping_status||"success")),s}async shutdown(e=!0){const t=this.getApp();if(!t||!t.rest)throw new Error("App or REST client not available");const s=await t.rest.POST("/api/jobs/runners/shutdown",{runner_id:this.get("runner_id"),graceful:e});return s.success&&s.data.status&&this.set("alive",!1),s}getChannels(){return this.get("channels")||[]}getStatusBadgeClass(){return this.get("alive")?"bg-success":"bg-danger"}getStatusIcon(){return this.get("alive")?"bi-check-circle-fill":"bi-x-octagon-fill"}isActive(){return!0===this.get("alive")}isHealthy(){if(!this.isActive())return!1;const e=this.get("last_heartbeat");return!!e&&(Date.now()-new Date(e).getTime())/1e3<120}getFormattedHeartbeatAge(){const e=this.get("last_heartbeat");if(!e)return"Never";const t=(Date.now()-new Date(e).getTime())/1e3;return t<60?`${Math.round(t)}s ago`:t<3600?`${Math.round(t/60)}m ago`:`${Math.round(t/3600)}h ago`}getUtilization(){const e=(this.get("jobs_processed")||0)+(this.get("jobs_failed")||0);return e>10?100:Math.min(10*e,100)}getFormattedUptime(){const e=this.get("started");if(!e)return"Unknown";const t=new Date(e),s=/* @__PURE__ */new Date-t,i=Math.floor(s/1e3);return i<60?`${i}s`:i<3600?`${Math.floor(i/60)}m`:i<86400?`${Math.floor(i/3600)}h`:`${Math.floor(i/86400)}d`}getWorkerInfo(){const e=this.get("jobs_processed")||0,t=this.get("jobs_failed")||0;return{processed:e,failed:t,total:e+t,alive:this.get("alive"),utilization:this.getUtilization()}}getDisplayName(){const e=this.get("runner_id");if(!e)return"Unknown Runner";const t=e.split("-");return t.length>1?t[0]:e}canControl(){return!0===this.get("alive")}}class JobRunnerList extends e.Collection{constructor(e={}){super({ModelClass:JobRunner,endpoint:"/api/jobs/runners",...e})}getActive(){return this.where(e=>e.isActive())}getByChannel(e){return this.where(t=>t.getChannels().includes(e))}getHealthy(){return this.where(e=>e.isHealthy())}getTotalProcessed(){return this.models.reduce((e,t)=>e+(t.get("jobs_processed")||0),0)}getTotalFailed(){return this.models.reduce((e,t)=>e+(t.get("jobs_failed")||0),0)}getSystemHealth(){if(0===this.models.length)return 0;const e=this.models.filter(e=>e.get("alive")).length;return Math.round(e/this.models.length*100)}getAllChannels(){const e=/* @__PURE__ */new Set;return this.models.forEach(t=>{t.getChannels().forEach(t=>e.add(t))}),Array.from(e).sort()}}JobRunner.ping=async function(e,t=2){const s="undefined"!=typeof window&&window.app;if(!s||!s.rest)throw new Error("App or REST client not available");return await s.rest.POST("/api/jobs/runners/ping",{runner_id:e,timeout:t})},JobRunner.shutdown=async function(e,t=!0){const s="undefined"!=typeof window&&window.app;if(!s||!s.rest)throw new Error("App or REST client not available");return await s.rest.POST("/api/jobs/runners/shutdown",{runner_id:e,graceful:t})},JobRunner.broadcast=async function(e,t={},s=2){const i="undefined"!=typeof window&&window.app;if(!i||!i.rest)throw new Error("App or REST client not available");return await i.rest.POST("/api/jobs/runners/broadcast",{command:e,data:t,timeout:s})},JobRunner.broadcastStatus=async function(e=2){return JobRunner.broadcast("status",{},e)},JobRunner.broadcastShutdown=async function(e=2){return JobRunner.broadcast("shutdown",{},e)},JobRunner.broadcastPause=async function(e=2){return JobRunner.broadcast("pause",{},e)},JobRunner.broadcastResume=async function(e=2){return JobRunner.broadcast("resume",{},e)},JobRunner.broadcastReload=async function(e=2){return JobRunner.broadcast("reload",{},e)};class Log extends e.Model{constructor(e={}){super(e,{endpoint:"/api/logs"})}}class LogList extends e.Collection{constructor(e={}){super({ModelClass:Log,endpoint:"/api/logs",size:10,...e})}}class Member extends e.Model{constructor(e={}){super(e,{endpoint:"/api/group/member"})}hasPermission(e){if(Array.isArray(e))return e.some(e=>this.hasPermission(e));const t=this.get("permissions");return!!t&&1==t[e]}async fetchForGroup(e){return await this.fetch({url:`/api/group/${e}/member`})}}class MemberList extends e.Collection{constructor(e={}){super({ModelClass:Member,endpoint:"/api/group/member",size:10,...e})}}const f={edit:{title:"Edit Membership",fields:[{name:"user.display_name",type:"text",label:"Display Name",placeholder:"Enter Display Name"},{name:"metadata.role",type:"text",label:"Role",placeholder:"Enter role"},{name:"is_active",type:"switch",label:"Is Enabled",columns:12}]}};Member.EDIT_FORM=f.edit,Member.ADD_FORM=f.create;class MetricsPermission extends e.Model{constructor(e={}){super(e,{endpoint:"/api/metrics/permissions",id_key:"account"})}}class MetricsPermissionList extends e.Collection{constructor(e={}){super({ModelClass:MetricsPermission,endpoint:"/api/metrics/permissions",...e})}}class PushDevice extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push"})}}class PushDeviceList extends e.Collection{constructor(e={}){super({ModelClass:PushDevice,endpoint:"/api/account/devices/push",...e})}}class PushTemplate extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/templates"})}}class PushTemplateList extends e.Collection{constructor(e={}){super({ModelClass:PushTemplate,endpoint:"/api/account/devices/push/templates",...e})}}class PushConfig extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/config"})}}class PushConfigList extends e.Collection{constructor(e={}){super({ModelClass:PushConfig,endpoint:"/api/account/devices/push/config",...e})}}class PushDelivery extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/deliveries"})}}class PushDeliveryList extends e.Collection{constructor(e={}){super({ModelClass:PushDelivery,endpoint:"/api/account/devices/push/deliveries",...e})}}const w={create:{title:"Create Push Configuration",fields:[{name:"name",label:"Name",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:s.GroupList,labelField:"name",valueField:"id"},{name:"fcm_service_account",label:"Service Account",type:"textarea",rows:10}]},edit:{title:"Edit Push Configuration",fields:[{name:"name",label:"Name",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:s.GroupList,labelField:"name",valueField:"id"},{name:"fcm_service_account",label:"Service Account",type:"textarea",rows:10},{name:"is_active",label:"Is Active",type:"switch",value:!0}]}},v={edit:{title:"Edit Push Template",fields:[{name:"name",label:"Name",required:!0},{name:"category",label:"Category",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:s.GroupList,labelField:"name",valueField:"id",defaultParams:{is_active:!0}},{name:"title_template",label:"Title Template",required:!0},{name:"body_template",label:"Body Template",type:"textarea",required:!0},{name:"action_url",label:"Action URL"},{name:"priority",label:"Priority",type:"select",options:["high","normal"]},{name:"variables",label:"Variables",type:"json",help:"JSON format"},{name:"is_active",label:"Is Active",type:"switch"}]}};w.create=w.edit,v.create=v.edit;const x={title:"Edit Location",size:"lg",fields:[{name:"ip_address",label:"IP Address",type:"text",required:!0,readonly:!0,cols:6},{name:"subnet",label:"Subnet",type:"text",cols:6},{name:"country_name",label:"Country",type:"text",cols:6},{name:"country_code",label:"Country Code",type:"text",cols:6},{name:"region",label:"Region",type:"text",cols:6},{name:"city",label:"City",type:"text",cols:6},{name:"postal_code",label:"Postal Code",type:"text",cols:6},{name:"timezone",label:"Timezone",type:"text",cols:6},{name:"latitude",label:"Latitude",type:"number",step:"any",cols:6},{name:"longitude",label:"Longitude",type:"number",step:"any",cols:6}]};class GeoLocatedIP extends e.Model{constructor(e={}){super(e,{endpoint:"/api/system/geoip"})}static async lookup(e){const t=new GeoLocatedIP,s=await t.rest.GET("/api/system/geoip/lookup",{ip:e});return s.success&&s.data&&s.data.data?new GeoLocatedIP(s.data.data):null}}GeoLocatedIP.EDIT_FORM=x,GeoLocatedIP.EDIT_LOCATION_FORM=x,GeoLocatedIP.EDIT_SECURITY_FORM={title:"Edit Security",size:"md",fields:[{name:"threat_level",label:"Threat Level",type:"select",cols:12,options:[{value:"",label:"None"},{value:"low",label:"Low"},{value:"medium",label:"Medium"},{value:"high",label:"High"},{value:"critical",label:"Critical"}]},{name:"is_threat",label:"Threat",type:"switch",cols:6},{name:"is_suspicious",label:"Suspicious",type:"switch",cols:6},{name:"is_known_attacker",label:"Known Attacker",type:"switch",cols:6},{name:"is_known_abuser",label:"Known Abuser",type:"switch",cols:6},{name:"risk_score",label:"Risk Score",type:"number",cols:6},{name:"is_tor",label:"TOR Exit Node",type:"switch",cols:6},{name:"is_vpn",label:"VPN",type:"switch",cols:6},{name:"is_proxy",label:"Proxy",type:"switch",cols:6},{name:"is_cloud",label:"Cloud Provider",type:"switch",cols:6},{name:"is_datacenter",label:"Datacenter",type:"switch",cols:6}]},GeoLocatedIP.EDIT_NETWORK_FORM={title:"Edit Network",size:"md",fields:[{name:"asn",label:"ASN",type:"text",cols:6},{name:"asn_org",label:"ASN Organization",type:"text",cols:6},{name:"isp",label:"ISP",type:"text",cols:12},{name:"connection_type",label:"Connection Type",type:"text",cols:6},{name:"provider",label:"Provider",type:"text",cols:6},{name:"is_mobile",label:"Mobile Connection",type:"switch",cols:6},{name:"mobile_carrier",label:"Mobile Carrier",type:"text",cols:6},{name:"last_seen",label:"Last Seen",type:"datetime",cols:12}]};class GeoLocatedIPList extends e.Collection{constructor(e={}){super({ModelClass:GeoLocatedIP,endpoint:"/api/system/geoip",...e})}}class Ticket extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ticket"})}}class TicketList extends e.Collection{constructor(e={}){super({ModelClass:Ticket,endpoint:"/api/incident/ticket",...e})}}const S={ticket:"Ticket",bug:"Bug",feature:"Feature Request",incident:"Incident",security:"Security Incident",fulfillment:"Fulfillment",new_user:"New User",new_group:"New Group",qa:"Quality Assurance"},C=Object.entries(S).map(([e,t])=>({value:e,label:t})),E={create:{title:"Create Ticket",fields:[{name:"title",type:"text",label:"Title",required:!0,cols:12},{name:"description",type:"textarea",label:"Description",required:!1,cols:12},{name:"category",type:"select",label:"Category",options:C,cols:12,value:"ticket"},{name:"priority",type:"number",label:"Priority",value:5,cols:6},{name:"status",type:"select",label:"Status",options:["new","open","paused","resolved","qa","ignored"],cols:6,value:"new"},{type:"collection",name:"assignee",label:"Assignee",Collection:s.UserList,labelField:"display_name",valueField:"id",cols:12},{type:"collection",name:"incident",label:"Incident",Collection:IncidentList,labelField:"title",valueField:"id",cols:12}]},edit:{title:"Edit Ticket",fields:[{name:"title",type:"text",label:"Title",required:!0,cols:12},{name:"description",type:"textarea",label:"Description",required:!1,cols:12},{name:"category",type:"select",label:"Category",options:C,cols:12},{name:"priority",type:"number",label:"Priority",cols:6},{name:"status",type:"select",label:"Status",options:["new","open","paused","resolved","qa","ignored"],cols:6},{type:"collection",name:"assignee",label:"Assignee",Collection:s.UserList,labelField:"display_name",valueField:"id",cols:12},{type:"collection",name:"incident",label:"Incident",Collection:IncidentList,labelField:"title",valueField:"id",cols:12}]}};class TicketNote extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ticket/note"})}}class TicketNoteList extends e.Collection{constructor(e={}){super({ModelClass:TicketNote,endpoint:"/api/incident/ticket/note",...e})}}class TableRow extends n.ListViewItem{constructor(e={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...e}),this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.tableView=e.tableView||e.listView||null,this.editingCells=/* @__PURE__ */new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if("string"==typeof e)return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if("object"==typeof e){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}buildRowTemplate(){let e="";return this.tableView&&this.tableView.isSelectable()&&(e+='\n <td style="padding: 0;">\n <div class="mojo-select-cell {{#selected}}selected{{/selected}}"\n data-action="select">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </td>\n '),this.columns.forEach(t=>{const s=[t.class||t.className||"",this.getResponsiveClasses(t.visibility),t.editable?"editable-cell":""].filter(e=>e).join(" "),i=this.buildCellTemplate(t);let a=t.action;!a&&t.editable?a="edit-cell":!a&&this.tableView.rowAction&&(a=this.tableView.rowAction),e+=a?`<td class="${s}" data-action="${a}" data-column="${t.key}">${i}</td>`:`<td class="${s}" data-column="${t.key}">${i}</td>`}),this.actions?e+=this.buildActionsTemplate():this.contextMenu&&(e+=this.buildContextMenuTemplate()),e}buildCellTemplate(e){const t=`model.${e.key}`,s=e.formatter||e.format;if(s){if("string"==typeof s)return`{{{${t}|${s}}}}`;if("function"==typeof s)return`<span data-formatter="${e.key}">{{${t}}}</span>`}return e.template?e.template:e.editable?`<span class="cell-content" data-field="${e.key}">{{{${t}}}}</span>`:`{{{${t}}}}`}buildActionsTemplate(){return this.actions&&0!==this.actions.length?`<td><div class="btn-group btn-group-sm">${this.actions.map(e=>{if("string"==typeof e)switch(e){case"view":return'\n <button class="btn btn-sm btn-outline-primary"\n data-action="view"\n title="View">\n <i class="bi bi-eye"></i>\n </button>\n ';case"edit":return'\n <button class="btn btn-sm btn-outline-secondary"\n data-action="edit"\n title="Edit">\n <i class="bi bi-pencil"></i>\n </button>\n ';case"delete":return'\n <button class="btn btn-sm btn-outline-danger"\n data-action="delete"\n title="Delete">\n <i class="bi bi-trash"></i>\n </button>\n ';default:return""}else if("object"==typeof e)return`\n <button class="btn btn-sm ${e.class||"btn-outline-primary"}"\n data-id="${this.model.id}"\n data-action="${e.action}"\n title="${e.label||""}">\n ${e.icon?`<i class="${e.icon}"></i>`:""}\n ${e.label&&!e.icon?e.label:""}\n </button>\n `;return""}).join("")}</div></td>`:""}buildContextMenuTemplate(){return this.contextMenu&&0!==this.contextMenu.length?`\n <td class="text-end" style="width: 1px;">\n <div class="dropdown">\n <button class="btn btn-sm btn-link border-0"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false"\n style="color: #6c757d;">\n <i class="bi bi-three-dots-vertical"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end shadow-sm">\n ${this.buildContextMenuItems()}\n </ul>\n </div>\n </td>\n `:""}buildContextMenuItems(){return this.contextMenu.map(e=>{if(e.separator||e.divider)return'<li><hr class="dropdown-divider"></li>';let t="dropdown-item";return("delete"===e.action||e.danger)&&(t+=" text-danger"),e.disabled&&(t+=" disabled"),`\n <li>\n <a class="${t}" href="#"\n data-id="{{model.id}}"\n data-action="${e.action}"\n ${e.disabled?'aria-disabled="true" tabindex="-1"':""}>\n ${e.icon?`<i class="${e.icon} me-2"></i>`:""}\n ${e.label}\n </a>\n </li>\n `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach(e=>{if(e.formatter&&"function"==typeof e.formatter){const s=this.element.querySelector(`[data-formatter="${e.key}"]`);if(s){const i=this.model.get?this.model.get(e.key):this.model[e.key],a={value:i,row:this.model,model:this.model,column:e,table:this.tableView,index:this.index};try{s.innerHTML=e.formatter(i,a)}catch(t){console.error(`Error formatting cell for column ${e.key}:`,t)}}}}),this.selected&&this.element.classList.add("selected");const e=this.model.get?this.model.get("id"):this.model.id;e&&this.element.setAttribute("data-id",e)}async onActionEditCell(e,t){e.stopPropagation();const s=t.getAttribute("data-column"),i=this.columns.find(e=>e.key===s);i&&i.editable&&(this.editingCells.has(s)||await this.enterEditMode(s,i,t))}async onActionRowClick(e,t){e.target.closest(".btn-group")||e.target.closest(".dropdown")||e.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}))}async onActionView(e,t){e.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:e})}async onActionEdit(e,t){return e.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:e}),!0}async onActionDelete(e,t){e.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:e})}async enterEditMode(e,t,s){const i=s.querySelector(".cell-content");if(!i)return;this.editingCells.add(e);const a=this.model.get?this.model.get(e):this.model[e],n=this.createCellEditor(t,a),l=i.innerHTML;i.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=n,s.appendChild(o);const r=o.querySelector("input, select, .form-check-input");r&&(r.focus(),"text"!==r.type&&"textarea"!==r.type||r.select()),o.dataset.originalContent=l,o.dataset.columnKey=e,this.setupEditorEvents(o,e,t),this.emit("cell:edit",{row:this,model:this.model,column:e,originalValue:a})}createCellEditor(e,t){const s=e.editableOptions||{};switch(s.type){case"select":return this.createSelectEditor(s,t);case"switch":case"checkbox":return this.createSwitchEditor(s,t);case"textarea":return this.createTextareaEditor(s,t);default:return this.createTextEditor(s,t)}}createTextEditor(e,t){const s=e.placeholder||"";return`\n <div class="d-flex gap-1 align-items-center">\n <input type="${e.inputType||"text"}"\n class="form-control form-control-sm cell-input"\n value="${this.escapeHtml(t||"")}"\n placeholder="${s}">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createTextareaEditor(e,t){const s=e.placeholder||"";return`\n <div class="d-flex gap-1">\n <textarea class="form-control form-control-sm cell-input"\n rows="${e.rows||2}"\n placeholder="${s}">${this.escapeHtml(t||"")}</textarea>\n <div class="d-flex flex-column gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}createSelectEditor(e,t){const s=e.options||[];let i="";return s.forEach(e=>{if("string"==typeof e)i+=`<option value="${e}" ${e===t?"selected":""}>${e}</option>`;else if("object"==typeof e&&void 0!==e.value){const s=e.value===t?"selected":"";i+=`<option value="${e.value}" ${s}>${e.label||e.value}</option>`}}),`\n <div class="d-flex gap-1 align-items-center">\n <select class="form-select form-select-sm cell-input">\n ${i}\n </select>\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createSwitchEditor(e,t){const s=t?"checked":"";return`\n <div class="d-flex gap-2 align-items-center">\n <div class="form-check ${"switch"===e.type?"form-switch":""}">\n <input class="form-check-input cell-input" type="checkbox" ${s}>\n </div>\n <div class="d-flex gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}setupEditorEvents(e,t,s){const i=e.querySelector(".cell-input"),a=e.querySelector(".cell-save"),n=e.querySelector(".cell-cancel");!i||"text"!==i.type&&"email"!==i.type&&"number"!==i.type||i.addEventListener("keydown",i=>{"Enter"===i.key?(i.preventDefault(),this.saveCellEdit(e,t,s)):"Escape"===i.key&&(i.preventDefault(),this.cancelCellEdit(e,t))}),!i||"checkbox"!==i.type&&"SELECT"!==i.tagName||!1===s.autoSave||i.addEventListener("change",()=>{this.saveCellEdit(e,t,s)}),a?.addEventListener("click",()=>{this.saveCellEdit(e,t,s)}),n?.addEventListener("click",()=>{this.cancelCellEdit(e,t)})}async saveCellEdit(e,t,s){const i=e.querySelector(".cell-input");if(!i)return;let a;a="checkbox"===i.type?i.checked:(i.tagName,i.value);const n=this.model.get?this.model.get(t):this.model[t];try{this.model.save?await this.model.save({[t]:a}):this.model[t]=a,this.exitEditMode(e,t,a),this.emit("cell:save",{row:this,model:this.model,column:t,oldValue:n,newValue:a})}catch(l){console.error("Failed to save cell edit:",l),this.emit("cell:save:error",{row:this,model:this.model,column:t,oldValue:n,newValue:a,error:l}),e.classList.add("saving-error"),setTimeout(()=>e.classList.remove("saving-error"),3e3)}}cancelCellEdit(e,t){const s=e.dataset.originalContent;this.exitEditMode(e,t,null,s),this.emit("cell:cancel",{row:this,model:this.model,column:t})}exitEditMode(e,s,i=null,a=null){const n=e.closest("td").querySelector(".cell-content");if(n){if(null!==i){const e=this.columns.find(e=>e.key===s);let a=i;e&&e.formatter&&"string"==typeof e.formatter&&(a=t.dataFormatter.pipe(i,e.formatter)),n.innerHTML=this.escapeHtml(a)}else a&&(n.innerHTML=a);n.style.display=""}e.remove(),this.editingCells.delete(s)}escapeHtml(e){if(null==e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}select(){super.select(),this.addClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.remove("selected")}}class TableView extends n.ListView{constructor(e={}){super({className:"table-view-component",itemClass:e.itemClass||TableRow,selectionMode:e.selectable?"multiple":"none",emptyMessage:e.emptyMessage||"No data available",addButtonIcon:e.addButtonIcon||"bi bi-plus-circle",...e}),this.isFullscreen=!1,this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.searchable=!1!==e.searchable,this.sortable=!1!==e.sortable,this.filterable=!1!==e.filterable,this.paginated=!1!==e.paginated,this.clickAction=e.clickAction||"view",this.itemView=e.itemView,this.addForm=e.addForm,this.editForm=e.editForm,this.deleteTemplate=e.deleteTemplate,this.formDialogConfig=e.formDialogConfig||{},this.viewDialogOptions=e.viewDialogOptions||{},this.exportOptions=e.exportOptions||null,this.options.showExport&&!this.exportOptions&&(this.exportOptions=[{format:"csv",label:"Export as CSV",icon:"bi bi-file-earmark-spreadsheet"},{format:"json",label:"Export as JSON",icon:"bi bi-file-earmark-code"}]),this.exportSource=e.exportSource||"remote",this.filters={},this.additionalFilters=e.filters||[],this.hideActivePills=e.hideActivePills||!1,this.hideActivePillNames=e.hideActivePillNames||[],this.rowAction=e.rowAction||"row-click",this.batchBarLocation=e.batchBarLocation||"bottom",this.options.addButtonLabel=e.addButtonLabel||"Add",this.toolbarButtons=e.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...e.tableOptions},this.searchPlacement=e.searchPlacement||"toolbar",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(e=>!0===e.footer_total),this.hasFooterTotals=this.footerTotalColumns.length>0,this.template=this.buildTableTemplate(),this.setupCollectionListeners()}setupCollectionListeners(){this.hasFooterTotals&&this.collection&&this.collection.on("reset add remove change",()=>{this.updateFooterTotals()})}initializeColumns(){this.columns.forEach(e=>{!e.key&&e.name&&(e.key=e.name),e.label||e.title||(e.label=e.key.charAt(0).toUpperCase()+e.key.slice(1))})}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if("string"==typeof e)return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if("object"==typeof e){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}parseColumnKey(e){const t=e.split("|");return{fieldKey:t[0],formatter:t[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const e=this.calculateFooterTotals();let t=0;this.columns.forEach(s=>{if(s.footer_total){const i=`col_${t}`,a=this.element.querySelector(`[data-total-column="${i}"]`);if(a&&e[i]){const t=this.parseColumnKey(s.key).formatter||s.formatter;let n;n=t&&"string"==typeof t?this.formatValue(e[i].value,t):e[i].value,a.textContent=n}t++}})}formatValue(e,s){try{return t.dataFormatter.pipe(e,s)}catch(i){return console.warn("Error formatting value:",i),e}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||0===this.collection.length)return{};const e={};return this.footerTotalColumns.forEach((t,s)=>{const{fieldKey:i,formatter:a}=this.parseColumnKey(t.key);let n=0;this.collection.forEach(e=>{const t=e.get?e.get(i):e[i],s=parseFloat(t)||0;n+=s}),t.key,this.collection.length,e[`col_${s}`]={value:n,formatter:a||t.formatter,fieldKey:i,originalKey:t.key}}),e}extractColumnFilters(){this.filters={},this.columns.forEach(e=>{if(e.filter){const{fieldKey:t}=this.parseColumnKey(e.key);this.filters[t]=e.filter}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&"multiple"==this.selectionMode}buildTableTemplate(){const e="top"===this.batchBarLocation?this.buildBatchActionsPanel():"",t="bottom"===this.batchBarLocation?this.buildBatchActionsPanel():"";return`\n <div class="mojo-table-wrapper">\n ${this.buildToolbarTemplate()}\n ${e}\n <div class="table-container"${(()=>{const e=this.tableOptions&&null!=this.tableOptions.fontSize?this.tableOptions.fontSize:this.options&&this.options.fontSize,t="sm"===e?"0.9rem":"xs"===e?"0.8rem":e?String(e):null;return t?` style="font-size: ${t};"`:""})()}>\n {{#loading}}\n <div class="mojo-table-loading d-flex justify-content-center align-items-center py-5">\n <div class="spinner-border" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="table-empty text-center py-5">\n <i class="bi bi-inbox fa-2x mb-2 text-muted"></i>\n <p class="text-muted">{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <table class="${this.buildTableClasses()}">\n ${this.buildTableHeaderTemplate()}\n <tbody data-container="items"></tbody>\n ${this.hasFooterTotals?this.buildTableFooterTemplate():""}\n </table>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${t}\n ${this.buildPaginationTemplate()}\n </div>\n `}buildTableClasses(){let e=["table"];return this.tableOptions.striped&&e.push("table-striped"),this.tableOptions.bordered&&e.push("table-bordered"),this.tableOptions.hover&&e.push("table-hover"),this.tableOptions.responsive&&e.push("table-responsive"),this.tableOptions.background&&e.push(`table-${this.tableOptions.background}`),"sm"===this.tableOptions.size&&e.push("table-sm"),"lg"===this.tableOptions.size&&e.push("table-lg"),e.join(" ")}buildToolbarTemplate(){return this.searchable||this.filterable?`\n <div class="table-action-buttons mb-3">\n <div class="d-flex align-items-center gap-2">\n ${this.buildActionButtonsTemplate()}\n ${this.filterable?this.buildFilterDropdownTemplate():""}\n ${this.searchable&&"toolbar"===this.searchPlacement?this.buildSearchTemplate():""}\n\n </div>\n <div data-container="filter-pills"></div>\n </div>\n `:""}buildActionButtonsTemplate(){let e=[];if(e.push('\n <button class="btn btn-sm btn-outline-secondary btn-refresh"\n data-action="refresh"\n title="Refresh">\n <i class="bi bi-arrow-clockwise"></i>\n </button>\n '),this.isFullscreenSupported()&&e.push('\n <button class="btn btn-sm btn-outline-secondary btn-fullscreen"\n data-action="toggle-fullscreen"\n title="Toggle Fullscreen">\n <i class="bi bi-fullscreen"></i>\n </button>\n '),this.options.showAdd&&e.push(`\n <button class="btn btn-sm btn-success btn-add"\n data-action="add"\n title="${this.options.addButtonLabel}">\n <i class="${this.options.addButtonIcon} me-1"></i>\n <span class="d-none d-lg-inline">${this.options.addButtonLabel}</span>\n </button>\n `),this.options.showExport)if(this.exportOptions&&this.exportOptions.length>1){const t=this.exportOptions.map(e=>`\n <li>\n <a class="dropdown-item" href="#" data-action="export" data-format="${e.format}">\n <i class="${e.icon||"bi bi-file-earmark-arrow-down"} me-2"></i>${e.label}\n </a>\n </li>\n `).join("");e.push(`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false" title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n <ul class="dropdown-menu">\n ${t}\n </ul>\n </div>\n `)}else{const t=this.exportOptions&&1===this.exportOptions.length?this.exportOptions[0].format:"json";e.push(`\n <button class="btn btn-sm btn-outline-secondary btn-export"\n data-action="export"\n data-format="${t}"\n title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((t,s)=>{const{label:i="Button",icon:a="",action:n="",handler:l=null,variant:o="outline-secondary",title:r=i,className:c="",permissions:d=null}=t;if(d&&!this.checkPermissions(d))return;const u=a?`<i class="${a} me-1"></i>`:"",h=`<span class="d-none d-lg-inline">${i}</span>`;let p="";l?p=`data-action="custom-toolbar-button" data-button-index="${s}"`:n&&(p=`data-action="${n}"`);const m=`btn btn-sm btn-${o} ${c}`.trim();e.push(`\n <button class="${m}"\n ${p}\n title="${r}">\n ${u}${h}\n </button>\n `)}),e.join("")}buildSearchTemplate(){return'\n <div class="flex-grow-1" style="max-width: 400px;">\n <div class="input-group input-group-sm">\n <span class="input-group-text">\n <i class="bi bi-search"></i>\n </span>\n <input type="search"\n class="form-control"\n placeholder="{{searchPlaceholder}}"\n data-filter="search"\n data-change-action="apply-search"\n value="{{collection.params.search}}"\n aria-label="Search">\n {{#searchValue}}\n <button class="btn btn-outline-secondary" type="button"\n data-action="clear-search"\n title="Clear search">\n <i class="bi bi-x"></i>\n </button>\n {{/searchValue}}\n </div>\n </div>\n '}buildFilterDropdownTemplate(){return this.filters&&Object.keys(this.filters).length>0||this.additionalFilters&&this.additionalFilters.length>0?`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false">\n <i class="bi bi-filter me-1"></i>\n <span class="d-none d-lg-inline">Add Filter</span>\n </button>\n <div class="dropdown-menu" style="min-width: 250px;">\n ${this.buildFilterList()}\n </div>\n </div>\n `:""}buildFilterList(){const e=this.getAllAvailableFilters(),t=this.getActiveFilters();return 0===e.length?'<div class="dropdown-item-text text-muted">No filters available</div>':`\n ${e.map(e=>{const s=t.hasOwnProperty(e.key),i=s?"active":"",a=this.getFilterIcon(e.type||e.config?.type);return`\n <button class="dropdown-item ${i}"\n data-action="add-filter"\n data-filter-key="${e.key}">\n <i class="bi bi-${a} me-2"></i>\n ${e.label}\n ${s?'<i class="bi bi-check-circle ms-auto"></i>':""}\n </button>\n `}).join("")}\n ${Object.keys(t).length>0?'\n <div class="dropdown-divider"></div>\n <button class="dropdown-item text-danger" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-2"></i>Clear All Filters\n </button>\n ':""}\n `}updateFilterPills(){const e=this.element?.querySelector('[data-container="filter-pills"]');if(!e)return;this.getActiveFilters();const t=this.buildActivePills();e.innerHTML=t}updateSearchInputs(e){const t=this.element?.querySelectorAll('[data-filter="search"]');t&&t.forEach(t=>{t.value=e||""})}buildActivePills(){if(this.hideActivePills)return"";const e=this.getActiveFilters(),t=e.search&&""!==e.search.toString().trim();let s=Object.entries(e).filter(([e,t])=>t&&""!==t.toString().trim()&&"search"!==e);return this.hideActivePillNames&&this.hideActivePillNames.length>0&&(s=s.filter(([e])=>!this.hideActivePillNames.includes(e))),0!==s.length||t?`\n <div class="row mt-2">\n <div class="col-12">\n <div class="d-flex flex-wrap align-items-center">\n ${s.map(([e,t])=>`\n <span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">\n <i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>\n\n <button type="button" class="btn btn-link text-white p-0 ms-1"\n style="font-size: 0.65rem; line-height: 1;"\n data-action="edit-filter"\n data-filter="${e}"\n title="Edit filter">\n ${this.getFilterLabel(e)}: ${this.getFilterDisplayValue(e,t)}\n </button>\n\n <button type="button" class="btn-close btn-close-white ms-1"\n style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"\n data-action="remove-filter"\n data-filter="${e}"\n title="Remove filter">\n </button>\n </span>\n `).join("")}\n ${s.length>1||s.length>0&&t||0===s.length&&t?'\n <button class="btn btn-sm btn-outline-secondary mb-1 py-0 px-2" style="font-size: 0.75rem;" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-1" style="font-size: 0.7rem;"></i>\n <small>Clear All</small>\n </button>\n ':""}\n </div>\n </div>\n </div>\n `:""}buildTableHeaderTemplate(){let e="";return this.isSelectable()&&(e+='\n <th style="width: 40px; padding: 0;">\n <div class="mojo-select-all-cell" data-action="select-all">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </th>\n '),this.columns.forEach(t=>{const{fieldKey:s}=this.parseColumnKey(t.key),i=this.sortable&&!1!==t.sortable,a=this.getSortBy()===s?this.getSortDirection():null,n=this.getSortIcon(a),l=t.label||t.title||s,o=this.getResponsiveClasses(t.visibility);e+=`\n <th class="${i?"sortable":""} ${o}">\n <div class="d-flex align-items-center">\n <span>${l}</span>\n ${i?`\n <div class="dropdown d-inline-block ms-2">\n <button class="btn btn-sm btn-link p-0 text-decoration-none" type="button"\n data-bs-toggle="dropdown" aria-expanded="false"\n data-column="${s}">\n ${n}\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n <li><a class="dropdown-item ${"asc"===a?"active":""}"\n data-action="sort" data-field="${s}" data-direction="asc">\n <i class="bi bi-sort-alpha-down me-2"></i>Sort A-Z\n </a></li>\n <li><a class="dropdown-item ${"desc"===a?"active":""}"\n data-action="sort" data-field="${s}" data-direction="desc">\n <i class="bi bi-sort-alpha-down-alt me-2"></i>Sort Z-A\n </a></li>\n <li><a class="dropdown-item ${null===a?"active":""}"\n data-action="sort" data-field="${s}" data-direction="none">\n <i class="bi bi-x-circle me-2"></i>No Sort\n </a></li>\n </ul>\n </div>\n `:""}\n </div>\n </th>\n `}),this.actions?e+="<th>Actions</th>":this.contextMenu&&(e+='<th style="width: 1px;"></th>'),`\n <thead>\n <tr>\n ${e}\n </tr>\n </thead>\n `}buildTableFooterTemplate(){let e="";this.isSelectable()&&(e+="<td></td>");let t=0;return this.columns.forEach((s,i)=>{const a=this.getResponsiveClasses(s.visibility);if(s.footer_total){const i=`col_${t}`,n=this.parseColumnKey(s.key).formatter||s.formatter;let l;l=n&&"string"==typeof n?`{{{footerTotals.${i}.value|${n}}}}`:`{{footerTotals.${i}.value}}`,e+=`<td class="table-footer-total ${a}" data-total-column="${i}">${l}</td>`,t++}else e+=0===i?`<td class="table-footer-label ${a}"><strong>Totals</strong></td>`:`<td class="${a}"></td>`}),(this.actions||this.contextMenu)&&(e+="<td></td>"),`\n <tfoot>\n <tr class="table-totals-row">\n ${e}\n </tr>\n </tfoot>\n `}buildBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return"";if("top"===this.batchBarLocation){let e="";return this.batchActions.forEach(t=>{e+=`\n <button class="btn btn-sm btn-outline-secondary" data-action="batch-${t.action}" title="${t.label}">\n <i class="${t.icon} me-1"></i>\n <span class="d-none d-lg-inline">${t.label}</span>\n </button>\n `}),`\n <div class="batch-actions-panel-top alert alert-info d-none mb-3" role="alert">\n <div class="d-flex justify-content-between align-items-center">\n <div class="d-flex align-items-center">\n <strong class="me-2">\n <span class="batch-select-count">0</span> ${this.options.batchPanelTitle||"items"} selected\n </strong>\n </div>\n <div class="d-flex gap-2 align-items-center">\n ${e}\n <button class="btn btn-sm btn-outline-secondary" data-action="clear-selection" title="Clear Selection">\n <i class="bi bi-x-circle me-1"></i>\n <span class="d-none d-lg-inline">Clear</span>\n </button>\n </div>\n </div>\n </div>\n `}{let e="";return this.batchActions.forEach(t=>{e+=`\n <div class="batch-select-action text-center px-2" data-action="batch-${t.action}">\n <div class="batch-action-icon fs-3">\n <i class="${t.icon}"></i>\n </div>\n <div class="batch-action-title small">${t.label}</div>\n </div>\n `}),`\n <div class="batch-actions-panel rounded-start rounded-end" style="display: none;">\n <div class="batch-select-panel rounded-start rounded-end">\n <div class="row g-0">\n <div class="col-auto">\n <div class="batch-select-count rounded-start">0</div>\n </div>\n <div class="col">\n <div class="ps-2 batch-select-title">${this.options.batchPanelTitle||"Rows"}</div>\n </div>\n <div class="col">\n <div class="batch-select-actions d-flex justify-content-end">\n ${e}\n </div>\n </div>\n <div class="col-auto">\n <div class="batch-select-end rounded-end"></div>\n </div>\n </div>\n </div>\n </div>\n `}}buildPaginationTemplate(){return this.paginated?'\n <div class="table-status-bar mt-3">\n <div class="d-flex flex-column flex-lg-row justify-content-center justify-content-lg-between align-items-center gap-3">\n <div class="d-flex flex-column flex-sm-row align-items-center gap-2 gap-sm-3 text-center text-lg-start">\n <span class="text-muted">\n Showing <span data-value="start">0</span> to <span data-value="end">0</span>\n of <span data-value="total">0</span> entries\n </span>\n <div class="d-flex align-items-center">\n <label class="form-label me-2 mb-0">Show:</label>\n <select class="form-select form-select-sm" style="width: auto;" data-change-action="page-size">\n <option value="5">5</option>\n <option value="10">10</option>\n <option value="25">25</option>\n <option value="50">50</option>\n <option value="100">100</option>\n </select>\n </div>\n </div>\n <nav aria-label="Table pagination">\n <ul class="pagination pagination-sm mb-0 justify-content-center" data-container="pagination">\n \x3c!-- Pagination will be rendered here --\x3e\n </ul>\n </nav>\n </div>\n </div>\n ':""}_createItemView(e,t){const s=new this.itemClass({model:e,index:t,listView:this,tableView:this,template:this.itemTemplate,columns:this.columns,actions:this.actions,contextMenu:this.contextMenu,batchActions:this.batchActions,containerId:"items"});return this.itemViews.set(e.id,s),s.on("item:select",e=>{this._onItemSelect(e),this.updateBatchActionsPanel()}),s.on("item:deselect",e=>{this._onItemDeselect(e),this.updateBatchActionsPanel()}),s.on("row:click",this._onRowClick.bind(this)),s.on("row:view",this._onRowView.bind(this)),s.on("row:edit",this._onRowEdit.bind(this)),s.on("row:delete",this._onRowDelete.bind(this)),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onMounted(){await super.onMounted();const e=this.getActiveFilters();this.collection&&Object.keys(e).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){this.element&&this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(e=>{e.addEventListener("input",e=>{""===e.target.value&&this.getActiveFilters().search&&this.onActionClearSearch(e,e.target)})})}_onRowClick(e){if(this.emit("row:click",e),this.options.onRowClick)return this.options.onRowClick(e.model,e.event);"view"===this.clickAction?this._onRowView(e):"edit"===this.clickAction&&this._onRowEdit(e)}getModelClass(e){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:e?.constructor?e.constructor:null}getModelName(e){const t=this.getModelClass(e);return t&&(t.MODEL_NAME||t.name.replace(/Model$/,""))||"Item"}getItemViewClass(e){if(this.itemView)return this.itemView;const t=this.getModelClass(e);return t?.VIEW_CLASS?t.VIEW_CLASS:null}getAddFormConfig(e){return this.addForm||e?.ADD_FORM||this.editForm||e?.EDIT_FORM}getEditFormConfig(e){return this.editForm||e?.EDIT_FORM||this.addForm||e?.ADD_FORM}getFormDialogConfig(e){return{...e?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(e,s){return e?t.Mustache.render(e,s):""}async _onRowView(e){if(this.emit("row:view",e),this.options.onItemView)return void(await this.options.onItemView(e.model,e.event));const t=this.getItemViewClass(e.model);if(t){const s=new t({model:e.model});await a.default.showDialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await a.default.showData({title:`View ${this.getModelName(e.model)} #${e.model.id}`,model:e.model})}async _onRowEdit(e){if(this.emit("row:edit",e),this.options.onItemEdit)return void(await this.options.onItemEdit(e.model,e.event));const t=this.getModelClass(e.model);let s=this.getEditFormConfig(t);if(s)s.fields||(s={title:`Edit ${this.getModelName(e.model)}`,fields:s}),await a.default.showModelForm({model:e.model,...s,...this.getFormDialogConfig(t)});else{const t=await a.default.showDialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new l.FormView({model:e.model,fields:this.options.formFields||[]})});t&&(await e.model.save(t),await this.refresh())}}async _onRowDelete(e){if(this.emit("row:delete",e),this.options.onItemDelete)return void(await this.options.onItemDelete(e.model,e.event));const t=this.getModelClass(e.model),s=this.deleteTemplate||t?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',i=this.renderTemplateString(s,e.model);await a.default.confirm({message:i||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await e.model.destroy(),this.collection.fetch())}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){this.isFullscreen?await this.exitFullscreen():await this.enterFullscreen()}async enterFullscreen(){try{this.element.requestFullscreen?await this.element.requestFullscreen():this.element.mozRequestFullScreen?await this.element.mozRequestFullScreen():this.element.webkitRequestFullscreen?await this.element.webkitRequestFullscreen():this.element.msRequestFullscreen&&await this.element.msRequestFullscreen(),this.isFullscreen=!0,this.element.classList.add("table-fullscreen"),this.updateFullscreenButton(),this.setupFullscreenListeners(),this.emit("table:fullscreen:enter")}catch(e){console.warn("Could not enter fullscreen:",e)}}async exitFullscreen(){try{document.exitFullscreen?await document.exitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.msExitFullscreen&&await document.msExitFullscreen(),this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit")}catch(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)&&this.isFullscreen&&(this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit"))};document.addEventListener("fullscreenchange",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}cleanupFullscreenListeners(){this._fullscreenHandler&&(document.removeEventListener("fullscreenchange",this._fullscreenHandler),document.removeEventListener("mozfullscreenchange",this._fullscreenHandler),document.removeEventListener("webkitfullscreenchange",this._fullscreenHandler),document.removeEventListener("msfullscreenchange",this._fullscreenHandler),this._fullscreenHandler=null)}destroy(){this.cleanupFullscreenListeners(),super.destroy()}async onActionRefresh(e,t){await this.refresh()}async onActionAdd(e,t){if(this.emit("table:add",{event:e}),this.options.onAdd)return void(await this.options.onAdd(e));const s=this.getModelClass();if(!s)return void console.warn("Cannot determine Model class for add operation");let i=this.getAddFormConfig(s);if(i){const e=new s;i.fields||(i={title:`Add ${this.getModelName()}`,fields:i});const t=await a.default.showForm({model:e,...i,...this.getFormDialogConfig(s)});t&&(this.options.addRequiresActiveGroup&&(t.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(t.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(t,this.options.addFormDefaults),await e.save(t),this.collection&&this.collection.add(e),await this.refresh())}else{const e=new s,t=await a.default.showDialog({title:`Add ${this.getModelName()}`,body:new l.FormView({model:e,fields:this.options.formFields||[]})});t&&(await e.save(t),this.collection&&this.collection.add(e),await this.refresh())}}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";this.emit("table:export",{format:s,source:this.exportSource,event:e}),"remote"===this.exportSource?this.collection?await this.collection.download(s):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],s):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(e,t){const s=t.value.trim();this.collection&&(this.setFilter("search",s),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:s,event:e}),this.emit("params-changed")}async onActionClearSearch(e,t){this.setFilter("search",null),this.collection&&(this.collection.params.start=0,this.collection.restEnabled&&await this.collection.fetch()),await this.render(),this.updateFilterPills(),this.emit("table:search",{searchTerm:"",event:e}),this.emit("params-changed")}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return"asc"===e?'<i class="bi bi-sort-alpha-down text-primary"></i>':"desc"===e?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),i=t.getAttribute("data-direction");if(this.collection){let e;if(e="none"===i?void 0:"desc"===i?`-${s}`:s,this.collection.setParams({...this.collection.params,sort:e,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(e){const t=e.startsWith("-"),s=t?e.slice(1):e;this.collection.sort((e,i)=>{const a=e.get(s),n=i.get(s);return a<n?t?1:-1:a>n?t?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:s,event:e}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const e=this.getSortBy(),t=this.getSortDirection();this.columns.forEach(s=>{if(this.sortable&&!1!==s.sortable){const{fieldKey:i}=this.parseColumnKey(s.key),a=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${i}"]`);if(a){const s=e===i,n=this.getSortIcon(s?t:null);a.innerHTML=n;const l=a.nextElementSibling;if(l){const a=l.querySelector(`[data-field="${i}"][data-direction="asc"]`),n=l.querySelector(`[data-field="${i}"][data-direction="desc"]`),o=l.querySelector(`[data-field="${i}"][data-direction="none"]`);a&&a.classList.toggle("active",s&&"asc"===t),n&&n.classList.toggle("active",s&&"desc"===t),o&&o.classList.toggle("active",!s||e!==i)}}}})}async onActionSelectAll(e,t){e.stopPropagation();const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(e=>e.selected);s?this.clearSelection():this.forEachItem(e=>{e.selected||e.select()});const i=this.element?.querySelector(".mojo-select-all-cell");i&&i.classList.toggle("selected",!s),this.updateBatchActionsPanel()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){if(await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.paginated&&this.collection){const e=this.collection.meta?.count||this.collection.length(),t=this.collection.params?.start||0,s=this.collection.params?.size||10,i=Math.min(t+s,e),a=this.element.querySelector('[data-value="start"]'),n=this.element.querySelector('[data-value="end"]'),l=this.element.querySelector('[data-value="total"]');a&&(a.textContent=t+1),n&&(n.textContent=i),l&&(l.textContent=e);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=s),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const e=this.element.querySelector('[data-container="pagination"]');if(!e||!this.collection)return;const t=this.collection.meta?.count||this.collection.length(),s=this.collection.params?.size||10,i=this.collection.params?.start||0,a=Math.floor(i/s)+1,n=Math.ceil(t/s);if(n<=1)return void(e.innerHTML="");const l=a>1?a-1:n,o=a<n?a+1:1,r=[];r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${l}">\n <i class="bi bi-chevron-left"></i>\n </a>\n </li>\n `);const c=/* @__PURE__ */new Set([1,n]);for(let h=a-1;h<=a+1;h++)h>=1&&h<=n&&c.add(h);const d=Array.from(c).sort((e,t)=>e-t);let u=0;for(const h of d)u&&h-u>1&&r.push('\n <li class="page-item disabled"><span class="page-link">…</span></li>\n '),r.push(`\n <li class="page-item ${h===a?"active":""}">\n <a class="page-link" href="#" data-action="page" data-page="${h}">${h}</a>\n </li>\n `),u=h;r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${o}">\n <i class="bi bi-chevron-right"></i>\n </a>\n </li>\n `),e.innerHTML=r.join("")}async onActionPage(e,t){e.preventDefault();const s=parseInt(t.getAttribute("data-page"),10),i=this.collection.params?.size||10,a=this.collection.meta?.count||this.collection.length(),n=Math.max(1,Math.ceil(a/i));let l=isNaN(s)?1:s;l<1&&(l=n),l>n&&(l=1),this.collection.setParams({...this.collection.params,start:(l-1)*i}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:l,event:e}),this.emit("params-changed")}async onChangePageSize(e,t){const s=parseInt(t.value);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:s}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("table:pagesize",{size:s,event:e}),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:e,size:t,sort:s,...i}=this.collection.params,a={},n=/* @__PURE__ */new Set;return this.getAllAvailableFilters().forEach(e=>{if("daterange"===e.config.type){const t=e.key,s=e.config.startName||"dr_start",l=e.config.endName||"dr_end",o=e.config.fieldName||"dr_field";i[o]===t&&(i[s]||i[l])&&(a[t]={start:i[s]||"",end:i[l]||""},n.add(s),n.add(l),n.add(o))}}),Object.keys(i).forEach(e=>{n.has(e)||(a[e]=i[e])}),a}setFilter(e,t){if(!this.collection)return;const s=this.getFilterConfig(e);if(s&&"daterange"===s.type){const i=s.startName||"dr_start",a=s.endName||"dr_end",n=s.fieldName||"dr_field";delete this.collection.params[i],delete this.collection.params[a],delete this.collection.params[n],t&&"object"==typeof t&&(t.start||t.end)&&(t.start&&(this.collection.params[i]=t.start),t.end&&(this.collection.params[a]=t.end),this.collection.params[n]=e)}else null==t||""===t?delete this.collection.params[e]:this.collection.params[e]=t}getAllAvailableFilters(){const e=[];return this.columns.forEach(t=>{if(t.filter){const{fieldKey:s}=this.parseColumnKey(t.key);e.push({key:s,label:t.filter.label||t.label||s,type:t.filter.type,config:t.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(t=>{e.push({key:t.name||t.key,label:t.label,type:t.type,config:t})}),e}getFilterConfig(e){const t=this.columns.find(t=>{const{fieldKey:s}=this.parseColumnKey(t.key);return s===e});if(t&&t.filter)return t.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const t=this.additionalFilters.find(t=>(t.name||t.key)===e);if(t)return t}return null}getFilterLabel(e){if("search"===e)return"Search";const t=this.filters[e];if(t&&t.label)return t.label;const s=this.additionalFilters.find(t=>(t.name||t.key)===e);return s&&s.label?s.label:e.charAt(0).toUpperCase()+e.slice(1)}getFilterDisplayValue(e,t){if("search"===e)return`"${t}"`;const s=this.filters[e]||this.additionalFilters.find(t=>(t.name||t.key)===e);if(s&&"daterange"===s.type&&"object"==typeof t)return`${t.start||""} to ${t.end||""}`;if(s&&"select"===s.type&&s.options){if("object"==typeof s.options[0]){const e=s.options.find(e=>e.value===t);return e?e.label:t}return t}return t}getFilterIcon(e){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[e]||"filter"}async onActionAddFilter(e,t){const s=t.getAttribute("data-filter-key"),i=this.getFilterConfig(s),n=this.getActiveFilters()[s];if(!i)return void console.warn("No filter config found for key:",s);const l=await a.default.showForm({title:`${void 0!==n&&""!==n?"Edit":"Add"} ${this.getFilterLabel(s)} Filter`,size:"md",fields:[this.buildFilterDialogField(i,n,s)]});if(l){const e=this.extractFilterValue(i,l);this.setFilter(s,e),await this.applyFilters()}}buildFilterDialogField(e,t,s){const i={name:"filter_value",label:e.label,value:t,...e};return"daterange"===e.type&&(i.startName=i.startName||"dr_start",i.endName=i.endName||"dr_end",i.fieldName=i.fieldName||"dr_field",i.format=i.format||"YYYY-MM-DD",i.displayFormat=i.displayFormat||"MMM DD, YYYY",i.separator=i.separator||" to ",i.label=i.label||"Date Range",t&&"object"==typeof t&&(i.startDate=t.start||"",i.endDate=t.end||"")),i}extractFilterValue(e,t){if("daterange"===e.type){const s=e.startName||"dr_start",i=e.endName||"dr_end";return{start:t[s],end:t[i]}}return t.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),this.render()}catch(e){console.error("Failed to fetch filtered data:",e),this.render()}else this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(e,t){const s=t.getAttribute("data-filter"),i=this.getFilterConfig(s),n=this.getActiveFilters()[s];if(!i)return void console.warn("No filter config found for key:",s);const l=await a.default.showForm({title:`Edit ${this.getFilterLabel(s)} Filter`,size:"md",data:{filter_value:n},fields:[this.buildFilterDialogField(i,n,s)]});if(l){const e=this.extractFilterValue(i,l);this.setFilter(s,e),await this.applyFilters()}}async onActionRemoveFilter(e,t){const s=t.getAttribute("data-filter");this.setFilter(s,null),"search"===s&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:s}),this.emit("params-changed")}async onActionClearAllFilters(e,t){if(!this.collection)return;const{start:s,size:i,sort:a}=this.collection.params;this.collection.params={start:s,size:i},a&&(this.collection.params.sort=a),this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}updateBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return;const e=this.getSelectedItems().length;if("top"===this.batchBarLocation){const t=this.element?.querySelector(".batch-actions-panel-top"),s=this.element?.querySelector(".batch-select-count");t&&s&&(s.textContent=e,e>0?t.classList.remove("d-none"):t.classList.add("d-none"))}else{const t=this.element?.querySelector(".batch-actions-panel"),s=this.element?.querySelector(".batch-select-count");t&&s&&(s.textContent=e,t.style.display=e>0?"block":"none")}const t=this.element?.querySelector(".mojo-select-all-cell");if(t){const e=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(e=>e.selected),s=Array.from(this.itemViews.values()).some(e=>e.selected);t.classList.toggle("selected",e),t.classList.toggle("indeterminate",!e&&s);const i=t.querySelector("i");i&&(i.className=!e&&s?"bi bi-dash":"bi bi-check")}}async onActionBatch(e,t){const s=t.getAttribute("data-action").replace("batch-",""),i=this.getSelectedItems();this.emit("batch:action",{action:s,items:i,event:e})}async onActionClearSelection(e,t){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(e,t){const s=parseInt(t.getAttribute("data-button-index"),10),i=this.toolbarButtons[s];i&&"function"==typeof i.handler&&await i.handler.call(this,e,t)}}class TablePage extends i.Page{constructor(e={}){super({...e,pageName:e.pageName||e.name||"table"}),this.title=e.title||this.pageName,this.description=e.description||"",this.Collection=e.Collection||null,this.collection=e.collection||null,this.defaultQuery=e.defaultQuery||{},this.groupField=e.groupField||"group",this.tableViewConfig={columns:e.columns||[],actions:e.actions||null,contextMenu:e.contextMenu||null,batchActions:e.batchActions||null,batchBarLocation:e.batchBarLocation||"top",clickAction:e.clickAction||"view",addForm:e.addForm||e.formFields||e.formCreate,editForm:e.editForm||e.formEdit||e.formFields,itemView:e.itemView,deleteTemplate:e.deleteTemplate,formDialogConfig:e.formDialogConfig,viewDialogOptions:e.viewDialogOptions,searchable:!1!==e.searchable,sortable:!1!==e.sortable,filterable:!1!==e.filterable,paginated:!1!==e.paginated,selectionMode:e.selectionMode||(e.selectable?"multiple":"none"),filters:e.filters||e.additionalFilters||[],hideActivePills:e.hideActivePills||!1,hideActivePillNames:e.hideActivePillNames||[],searchPlacement:e.searchPlacement||"toolbar",tableOptions:{striped:!0,bordered:!1,hover:!0,responsive:!1,...e.tableOptions},emptyMessage:e.emptyMessage||"No data available",searchPlaceholder:e.searchPlaceholder||"Search...",showAdd:!1!==e.showAdd,showExport:!1!==e.showExport,onItemView:e.onItemView,onItemEdit:e.onItemEdit,onItemDelete:e.onItemDelete,onAdd:e.onAdd,onExport:e.onExport,...e.tableViewOptions},this.urlSyncEnabled=!1!==e.urlSyncEnabled,this.lastUpdated=null,this.isLoading=!1,this.template=e.template||this.buildTemplate()}buildTemplate(){return'\n <div class="table-page-container">\n\n <div class="table-container" data-container="table"></div>\n\n {{#showStatus}}\n <div class="table-status-bar table-status-top">\n <div class="status-info">\n <div class="d-flex justify-content-between w-100">\n <span class="text-muted">\n <i class="bi bi-clock me-1"></i>\n Last updated: <span data-status="last-updated">{{lastUpdated}}</span>\n </span>\n <span class="text-muted">\n <i class="bi bi-list-ol me-1"></i>\n Total records: <span data-status="record-count">0</span>\n </span>\n </div>\n </div>\n </div>\n {{/showStatus}}\n\n </div>\n '}async onInit(){await super.onInit(),this.collection||(this.Collection?this.collection=new this.Collection:this.collection=new e.Collection),this.applyQueryToCollection(),this.tableView=new TableView({collection:this.collection,containerId:"table",fetchOnMount:!0,...this.tableViewConfig}),this.addChild(this.tableView),this.setupEventListeners()}setupEventListeners(){this.urlSyncEnabled&&this.collection&&(this.collection.on("fetch:start",()=>{this.isLoading=!0}),this.collection.on("fetch:end",()=>{this.isLoading=!1,this.lastUpdated=/* @__PURE__ */(new Date).toLocaleTimeString(),this.updateStatusDisplay()})),this.tableView.on("params-changed",()=>{this.urlSyncEnabled&&this.syncUrl()}),this.tableView.on("table:search",({searchTerm:e})=>{}),this.tableView.on("table:sort",({field:e})=>{}),this.tableView.on("table:page",({page:e})=>{}),this.tableView.on("filter:edit",async({key:e})=>{await this.handleFilterEdit(e)}),this.tableView.on("row:view",async({model:e})=>{this.tableViewConfig.onItemView&&await this.tableViewConfig.onItemView(e)}),this.tableView.on("row:edit",async({model:e})=>{this.tableViewConfig.onItemEdit&&await this.tableViewConfig.onItemEdit(e)}),this.tableView.on("row:delete",async({model:e})=>{this.tableViewConfig.onItemDelete&&await this.tableViewConfig.onItemDelete(e)}),this.tableView.on("table:add",async()=>{this.tableViewConfig.onAdd&&await this.tableViewConfig.onAdd()}),this.tableView.on("table:export",async({data:e})=>{this.tableViewConfig.onExport&&await this.tableViewConfig.onExport(e)})}applyQueryToCollection(){const e={},t={...this.defaultQuery,...this.query};if(!t||0===Object.keys(t).length)return;void 0!==t.start&&(e.start=parseInt(t.start)||0),void 0!==t.size&&(e.size=parseInt(t.size)||10),void 0!==t.sort&&(e.sort=t.sort),void 0!==t.search&&(e.search=t.search);const s=["start","size","sort","search","page"];Object.entries(t).forEach(([t,i])=>{if(!s.includes(t)&&void 0!==i&&""!==i)if("string"==typeof i&&(i.startsWith("{")||i.startsWith("[")))try{e[t]=JSON.parse(i)}catch(a){e[t]=i}else e[t]=i}),Object.keys(e).length>0&&this.collection.setParams({...this.collection.params,...e})}syncUrl(e=!0){if(!this.urlSyncEnabled||!this.collection||!this.getApp()?.router)return;const t=new URL(window.location),s={};for(const[l,o]of t.searchParams)"page"!==l&&(s[l]=o);const i={},a=this.collection.params||{};a.start&&(i.start=a.start),a.size&&(i.size=a.size),a.sort&&(i.sort=a.sort),a.search&&(i.search=a.search),Object.entries(a).forEach(([e,t])=>{["start","size","sort","search"].includes(e)||void 0===t||""===t||(i[e]="object"==typeof t?JSON.stringify(t):t)});const n=Object.keys(i).some(e=>String(s[e]||"")!==String(i[e]||""))||Object.keys(s).some(e=>!(e in i));this.query=i,(n||e)&&this.updateBrowserUrl(i,!0,!1)}updateStatusDisplay(){if(!this.element)return;const e=this.element.querySelector('[data-status="last-updated"]');e&&(e.textContent=this.lastUpdated||"Never");const t=this.element.querySelector('[data-status="record-count"]');if(t&&this.collection){const e=this.collection.meta?.count||this.collection.length();t.textContent=e}}async onEnter(){await super.onEnter(),this.options.requiresGroup&&!this.query[this.groupField]&&this.getApp().activeGroup&&(this.query[this.groupField]=this.getApp().activeGroup.id),this.applyQueryToCollection(),this.tableView&&this.tableView.element&&setTimeout(()=>{this.tableView.updateFilterPills(),this.tableView.updateSortIcons()},100)}async refresh(){await this.tableView.refresh()}getSelectedItems(){return this.tableView.getSelectedItems()}clearSelection(){this.tableView.clearSelection()}async handleFilterEdit(e){const t=this.tableView.getAllAvailableFilters().find(t=>t.key===e),s=this.collection.params[e];if(!t)return;const i={name:"filter_value",label:t.label||e,value:s,...t.config},n=await a.default.showForm({title:`Edit ${i.label} Filter`,size:"md",fields:[i]});n&&void 0!==n.filter_value&&(this.tableView.setFilter(e,n.filter_value),this.collection.restEnabled&&await this.collection.fetch(),await this.tableView.render(),this.syncUrl())}clearAllFilters(){if(!this.collection)return;const{start:e,size:t,sort:s}=this.collection.params;this.collection.params={start:e,size:t},s&&(this.collection.params.sort=s),this.syncUrl(),this.collection.restEnabled?this.collection.fetch():this.tableView.render()}async onGroupChange(e){e&&this.collection&&this.options.requiresGroup&&(this.query[this.groupField]=e.id,this.applyQueryToCollection(),this.collection&&this.collection.restEnabled&&await this.collection.fetch())}async onBeforeDestroy(){this.collection&&(this.collection.off("fetch:start"),this.collection.off("fetch:end")),this.tableView&&(this.tableView.off("params-changed"),this.tableView.off("table:search"),this.tableView.off("table:sort"),this.tableView.off("table:page"),this.tableView.off("filter:edit"),this.tableView.off("row:view"),this.tableView.off("row:edit"),this.tableView.off("row:delete"),this.tableView.off("table:add"),this.tableView.off("table:export")),await super.onBeforeDestroy()}get showStatus(){return!0===this.options.showStatus}static create(e={}){return new this(e)}}class TabView extends t.View{constructor(e={}){const{tabs:t,activeTab:s,tabsClass:i,contentClass:a,minWidth:n,enableResponsive:l,tabPadding:o,dropdownStyle:r,...c}=e;super({tagName:"div",className:"tab-view",...c}),this.tabs={},this.tabLabels=Object.keys(this.tabs),this.activeTab=s||this.tabLabels[0]||null,this.tabsClass=i||"nav nav-tabs mb-3",this.contentClass=a||"tab-content",this.dropdownStyle=r||"select",this.minWidth=n||300,this.enableResponsive=!1!==l,this.tabPadding=o||80,this.currentMode="tabs",this.tabWidthCache=/* @__PURE__ */new Map,this.lastContainerWidth=0,this.resizeObserver=null,this._measurementSpan=null,this._tabComputedStyle=null,this.isMobileMode=!1,this.hasOverflow=!1;for(const[d,u]of Object.entries(t))this.addTab(d,u);this.handleResize=this.handleResize.bind(this)}async renderTemplate(){return`\n <div class="tab-view-container">\n ${this.buildTabNavigation()}\n ${this.buildTabContent()}\n </div>\n `}buildTabNavigation(){return 0===this.tabLabels.length?"":"dropdown"===this.currentMode?this.buildDropdownNavigation():this.buildTabsNavigation()}buildTabsNavigation(){const e=this.tabLabels.map(e=>{const t=e===this.activeTab,s=this.getTabId(e);return`\n <li class="nav-item" role="presentation">\n <button class="nav-link ${t?"active":""}"\n id="${s}-tab"\n data-action="show-tab"\n data-tab-label="${this.escapeHtml(e)}"\n type="button"\n role="tab"\n aria-controls="${s}"\n aria-selected="${t}">\n ${this.escapeHtml(e)}\n </button>\n </li>\n `}).join("");return`\n <ul class="${this.tabsClass}" role="tablist" data-tab-mode="tabs">\n ${e}\n </ul>\n `}buildDropdownNavigation(){const e=this.activeTab||this.tabLabels[0],t=this.tabLabels.map(e=>{const t=e===this.activeTab;return`\n <li>\n <button class="dropdown-item ${t?"active":""}"\n data-action="show-tab"\n data-tab-label="${this.escapeHtml(e)}"\n type="button">\n ${this.escapeHtml(e)}\n ${t?'<i class="bi bi-check-lg ms-2"></i>':""}\n </button>\n </li>\n `}).join("");let s;return s="select"===this.dropdownStyle?`\n <button class="btn tab-view-select-style dropdown-toggle"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false"\n id="tab-dropdown-${this.id}">\n <span class="tab-view-select-label">${this.escapeHtml(e)}</span>\n </button>\n `:`\n <button class="btn btn-outline-secondary dropdown-toggle w-100 w-sm-auto"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false"\n id="tab-dropdown-${this.id}">\n <i class="bi bi-list me-2"></i>\n ${this.escapeHtml(e)}\n </button>\n `,`\n <div class="dropdown mb-3" data-tab-mode="dropdown">\n ${s}\n <ul class="dropdown-menu" aria-labelledby="tab-dropdown-${this.id}">\n ${t}\n </ul>\n </div>\n `}buildMobileDropdownNavigation(){const e=this.activeTab||this.tabLabels[0],t=this.tabLabels.map(e=>{const t=e===this.activeTab;return`\n <li>\n <button class="dropdown-item ${t?"active":""}"\n data-action="show-tab"\n data-tab-label="${this.escapeHtml(e)}"\n type="button">\n ${this.escapeHtml(e)}\n ${t?'<i class="bi bi-check ms-2"></i>':""}\n </button>\n </li>\n `}).join("");return`\n <div class="dropdown mb-3" data-tab-navigation="mobile">\n <button class="btn btn-outline-secondary dropdown-toggle w-100 text-start"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false">\n <i class="bi bi-list me-2"></i>\n ${this.escapeHtml(e)}\n </button>\n <ul class="dropdown-menu w-100">\n ${t}\n </ul>\n </div>\n `}shouldUseMobileDropdown(){if(!this.enableMobileDropdown)return!1;const e=window.innerWidth;return e<this.mobileBreakpoint||this.hasOverflow&&e<992}buildTabContent(){if(0===this.tabLabels.length)return'<div class="alert alert-info">No tabs to display</div>';const e=this.tabLabels.map(e=>{const t=e===this.activeTab,s=this.getTabId(e);return`\n <div class="tab-pane fade ${t?"show active":""}"\n id="${s}"\n role="tabpanel"\n aria-labelledby="${s}-tab"\n data-tab-label="${this.escapeHtml(e)}">\n <div data-container="${s}-content"></div>\n </div>\n `}).join("");return`\n <div class="${this.contentClass}">\n ${e}\n </div>\n `}getTabId(e){return`tab-${e.toLowerCase().replace(/[^a-z0-9]/g,"-")}-${this.id}`}async showTab(e,t={}){const{force:s=!1}=t;if(!this.tabs[e])return console.warn(`TabView: Tab "${e}" does not exist`),!1;if(this.activeTab===e&&!s){const t=this.tabs[e];if(t&&t.isMounted()&&this.element.contains(t.element))return!0}const i=this.activeTab;this.activeTab=e;try{return await this.updateTabNavigation(e,i),await this.updateTabContent(e,i),this.emit("tab:changed",{activeTab:e,previousTab:i}),!0}catch(a){return console.error("TabView: Error showing tab:",a),this.activeTab=i,!1}}async updateTabNavigation(e,t){if(!this.element)return;if("dropdown"===this.currentMode)return void(await this.reRenderNavigation());if(t){const e=this.element.querySelector(`[data-tab-label="${t}"]`);e&&(e.classList.remove("active"),e.setAttribute("aria-selected","false"))}const s=this.element.querySelector(`[data-tab-label="${e}"]`);s&&(s.classList.add("active"),s.setAttribute("aria-selected","true"))}async updateTabContent(e,t){if(!this.element)return;const s=this.getTabId(e),i=t?this.getTabId(t):null;if(i){const e=this.element.querySelector(`#${i}`);e&&e.classList.remove("show","active")}const a=this.element.querySelector(`#${s}`);a&&a.classList.add("show","active");const n=this.tabs[e];if(n){const e=this.element.querySelector(`[data-container="${s}-content"]`);e&&!n.isMounted()&&await n.render(!0,e),n.onTabActivated&&await n.onTabActivated()}}async onActionShowTab(e,t){const s=t.getAttribute("data-tab-label");s&&await this.showTab(s)}_initializeMeasurementStyles(){if(!this.element||this._tabComputedStyle)return;const e=this.element.querySelector(".nav-link");if(e&&"function"==typeof window.getComputedStyle){const t=window.getComputedStyle(e);this._tabComputedStyle={font:t.font,letterSpacing:t.letterSpacing};const s=parseFloat(t.paddingLeft)||0,i=parseFloat(t.paddingRight)||0;this.tabPadding=s+i+12}}async onAfterRender(){await super.onAfterRender(),this._initializeMeasurementStyles(),this.enableResponsive&&this.setupResponsiveHandling(),this.activeTab&&this.tabs[this.activeTab]&&await this.showTab(this.activeTab,{force:!0})}async onAfterMount(){await super.onAfterMount()}async onBeforeDestroy(){await super.onBeforeDestroy(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),"undefined"!=typeof window&&window.removeEventListener("resize",this.handleResize),this._measurementSpan&&this._measurementSpan.parentElement&&this._measurementSpan.parentElement.removeChild(this._measurementSpan),this._measurementSpan=null;for(const[e,t]of Object.entries(this.tabs))t&&"function"==typeof t.destroy&&await t.destroy()}getActiveTab(){return this.activeTab}getTabLabels(){return[...this.tabLabels]}getTab(e){return this.tabs[e]||null}async addTab(e,t,s=!1){return this.tabs[e]?(console.warn(`TabView: Tab "${e}" already exists`),!1):!(t.options.permissions&&!this.getApp().activeUser.hasPerm(t.options.permissions)||(this.tabs[e]=t,t.containerId=this.getTabId(e),t.parent=this,this.tabLabels=Object.keys(this.tabs),this.activeTab&&!s||(this.activeTab=e),this.isMounted()&&(await this.render(),(s||this.activeTab===e)&&await this.showTab(e)),this.emit("tab:added",{label:e,view:t}),0))}async removeTab(e){if(!this.tabs[e])return console.warn(`TabView: Tab "${e}" does not exist`),!1;const t=this.tabs[e];return t&&"function"==typeof t.destroy&&await t.destroy(),delete this.tabs[e],this.tabLabels=Object.keys(this.tabs),this.activeTab===e&&(this.activeTab=this.tabLabels[0]||null),this.isMounted()&&(await this.render(),this.activeTab&&await this.showTab(this.activeTab)),this.emit("tab:removed",{label:e,view:t}),!0}calculateTabWidth(e){if(this.tabWidthCache.has(e))return this.tabWidthCache.get(e);if("undefined"==typeof document){const t=8*e.length+this.tabPadding;return this.tabWidthCache.set(e,t),t}this._measurementSpan||(this._measurementSpan=document.createElement("span"),this._measurementSpan.style.visibility="hidden",this._measurementSpan.style.position="absolute",this._measurementSpan.style.whiteSpace="nowrap");const t=this._measurementSpan;this._tabComputedStyle?(t.style.font=this._tabComputedStyle.font,t.style.letterSpacing=this._tabComputedStyle.letterSpacing):(t.style.fontSize="14px",t.style.fontFamily="system-ui, -apple-system, sans-serif"),t.textContent=e,document.body.appendChild(t);const s=t.offsetWidth+this.tabPadding;return document.body.removeChild(t),this.tabWidthCache.set(e,s),s}getTotalTabWidth(){return this.tabLabels.reduce((e,t)=>e+this.calculateTabWidth(t),0)}getContainerWidth(){return this.element&&(this.element.parentElement||this.element).offsetWidth||this.minWidth}shouldUseDropdown(){if(!this.enableResponsive)return!1;const e=this.getContainerWidth(),t=this.getTotalTabWidth();return e<Math.max(t,this.minWidth)}setupResponsiveHandling(){if(this.element&&this.enableResponsive)if(this.updateNavigationMode(),"undefined"!=typeof ResizeObserver){this.resizeObserver=new ResizeObserver(()=>{this.handleResize()});const e=this.element.parentElement||this.element;this.resizeObserver.observe(e)}else window.addEventListener("resize",this.handleResize)}async handleResize(){const e=this.getContainerWidth();Math.abs(e-this.lastContainerWidth)>50&&(this.lastContainerWidth=e,await this.updateNavigationMode())}async updateNavigationMode(){const e=this.shouldUseDropdown()?"dropdown":"tabs";e!==this.currentMode&&(this.currentMode=e,this.isMounted()&&await this.reRenderNavigation(),this.emit("navigation:modeChanged",{mode:this.currentMode,containerWidth:this.getContainerWidth(),totalTabWidth:this.getTotalTabWidth()}))}async reRenderNavigation(){if(!this.element)return;const e=this.element.querySelector("[data-tab-mode]");if(e){const t=this.buildTabNavigation();e.outerHTML=t}}getNavigationMode(){return this.currentMode}async setNavigationMode(e){"tabs"===e||"dropdown"===e?(this.currentMode=e,this.isMounted()&&await this.reRenderNavigation()):console.warn('TabView: Invalid navigation mode. Use "tabs" or "dropdown"')}clearWidthCache(){this.tabWidthCache.clear()}static create(e={}){return new TabView(e)}}class FilePreviewView extends t.View{constructor(e={}){super({className:"file-preview",...e}),this.file=e.file||{},this.isImage=this.file.content_type?.startsWith("image/"),this.isPdf="application/pdf"===this.file.content_type}getTemplate(){return`\n <div class="file-preview-item card card-body p-2 mt-2">\n <div class="d-flex align-items-center">\n <div class="flex-shrink-0">\n ${this.isImage?`<img src="${this.file.thumbnailUrl||this.file.url}" class="rounded" style="width: 40px; height: 40px; object-fit: cover;">`:'<i class="bi bi-file-earmark-text fs-2 text-secondary"></i>'}\n </div>\n <div class="flex-grow-1 ms-3">\n <div class="fw-bold text-truncate">{{file.filename}}</div>\n <div class="small text-muted">{{file.file_size|filesize}}</div>\n </div>\n <div class="flex-shrink-0">\n <button class="btn btn-sm btn-outline-primary" data-action="view-file">View</button>\n </div>\n </div>\n </div>\n `}async onActionViewFile(){if(this.isImage){const e=window.MOJO?.plugins?.LightboxGallery;e?e.show({src:this.file.url,alt:this.file.filename}):window.open(this.file.url,"_blank")}else if(this.isPdf){const e=window.MOJO?.plugins?.PDFViewer;e?e.showDialog(this.file.url,{title:this.file.filename}):window.open(this.file.url,"_blank")}else window.open(this.file.url,"_blank")}}class ChatMessageView extends t.View{constructor(e={}){super({className:"chat-message",...e}),this.message=e.message||{},this.theme=e.theme||"compact",this.isCurrentUser=e.isCurrentUser||!1,"bubbles"===this.theme&&(this.className+=this.isCurrentUser?" message-right":" message-left")}getTemplate(){return"system_event"===this.message.type?'\n <div class="chat-message-system text-center text-muted small py-2">\n <i class="bi bi-info-circle me-1"></i>\n {{message.content}} on {{message.timestamp|datetime}}\n </div>\n ':"bubbles"===this.theme?this.getBubblesTemplate():this.getCompactTemplate()}getCompactTemplate(){return`\n <div class="message-item">\n <div class="message-avatar ${this.isCurrentUser?"bg-primary":"bg-secondary"}">\n {{#message.author.avatarUrl}}\n <img src="{{message.author.avatarUrl}}" alt="{{message.author.name}}" class="w-100 h-100 rounded-circle">\n {{/message.author.avatarUrl}}\n {{^message.author.avatarUrl}}\n {{message.author.name|initials}}\n {{/message.author.avatarUrl}}\n </div>\n <div class="message-content">\n <div class="message-header">\n <div class="message-author">\n {{message.author.name}}\n {{#isCurrentUser}}\n <span class="badge bg-primary badge-sm ms-1">You</span>\n {{/isCurrentUser}}\n </div>\n <div class="message-time text-muted">{{message.timestamp|relative}}</div>\n </div>\n <div class="message-text">{{{message.content}}}</div>\n <div data-container="attachments"></div>\n </div>\n </div>\n `}getBubblesTemplate(){return'\n <div class="message-bubble-wrapper">\n <div class="message-meta">\n <strong>{{message.author.name}}</strong>\n <span class="text-muted">· {{message.timestamp|relative}}</span>\n </div>\n <div class="message-bubble">\n <div class="message-text">{{{message.content}}}</div>\n <div data-container="attachments"></div>\n </div>\n </div>\n '}async onAfterRender(){if(this.message.attachments&&this.message.attachments.length>0){const e=this.element.querySelector('[data-container="attachments"]');e&&this.message.attachments.forEach(t=>{const s=new FilePreviewView({file:t});this.addChild(s),s.render(!0,e)})}}}class ChatInputView extends t.View{constructor(e={}){super({className:"chat-input-view",...e}),this.placeholder=e.placeholder||"Type a message...",this.buttonText=e.buttonText||"Send",this.attachments=[],this.pendingUploads=/* @__PURE__ */new Map}getTemplate(){return`\n <div class="chat-input-container">\n <div class="chat-input-attachments" data-container="attachments"></div>\n <div class="chat-input-wrapper">\n <textarea\n class="chat-input form-control"\n placeholder="${this.placeholder}"\n rows="1"></textarea>\n <button class="chat-send-btn btn btn-primary" data-action="send-message" type="button">\n <i class="bi bi-send-fill"></i>\n </button>\n </div>\n <div class="chat-input-footer">\n <small class="text-muted">\n <i class="bi bi-paperclip"></i>\n Drag & drop files to attach\n </small>\n </div>\n </div>\n `}async onAfterRender(){this.enableFileDrop({dropZoneSelector:".chat-input-container",multiple:!0,acceptedTypes:["*/*"],visualFeedback:!0,dragOverClass:"drag-over",dragActiveClass:"drag-active"});const e=this.element.querySelector(".chat-input");e&&(e.addEventListener("input",()=>this.autoResizeTextarea(e)),e.addEventListener("keydown",e=>this.handleKeydown(e)))}handleKeydown(e){"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.onActionSendMessage(e,e.target))}async onFileDrop(e){for(const t of e)await this.uploadFile(t)}async uploadFile(e){const t=new c,s=Date.now()+Math.random();this.addFilePreview(s,e,0),this.pendingUploads.set(s,{file:e,fileModel:t});try{await t.upload({file:e,onProgress:e=>{this.updateFileProgress(s,e)},onComplete:e=>{this.handleUploadComplete(s,t)}})}catch(i){console.error("File upload failed:",i),this.handleUploadError(s,i)}}addFilePreview(e,t,s){const i=this.element.querySelector('[data-container="attachments"]');if(!i)return;const a=document.createElement("div");a.className="attachment-preview",a.dataset.uploadId=e,a.innerHTML=`\n <div class="attachment-info">\n <i class="bi bi-file-earmark"></i>\n <span class="attachment-name">${this.escapeHtml(t.name)}</span>\n <span class="attachment-size">(${this.formatFileSize(t.size)})</span>\n </div>\n <div class="attachment-progress">\n <div class="progress" style="height: 4px;">\n <div class="progress-bar" role="progressbar" style="width: ${s}%"></div>\n </div>\n </div>\n <button class="attachment-remove btn btn-sm btn-link text-danger" data-action="remove-attachment" data-upload-id="${e}" type="button">\n <i class="bi bi-x"></i>\n </button>\n `,i.appendChild(a)}updateFileProgress(e,t){const s=this.element.querySelector(`[data-upload-id="${e}"]`);if(s){const e=s.querySelector(".progress-bar");e&&(e.style.width=`${t}%`)}}handleUploadComplete(e,t){this.attachments.push({id:t.id,name:t.get("name"),uploadId:e}),this.pendingUploads.delete(e);const s=this.element.querySelector(`[data-upload-id="${e}"]`);if(s){s.classList.add("upload-complete");const e=s.querySelector(".attachment-progress");e&&e.remove()}}handleUploadError(e,t){this.pendingUploads.delete(e);const s=this.element.querySelector(`[data-upload-id="${e}"]`);s&&(s.classList.add("upload-error"),s.querySelector(".attachment-info").innerHTML+='<span class="text-danger ms-2">Upload failed</span>')}async onActionRemoveAttachment(e,t){const s=t.dataset.uploadId;this.pendingUploads.delete(s);const i=this.element.querySelector(`[data-upload-id="${s}"]`);i&&i.remove()}async onActionSendMessage(e,t){const s=this.element.querySelector(".chat-input").value.trim();(s||0!==this.attachments.length)&&(this.pendingUploads.size>0||this.emit("message:send",{text:s,files:this.attachments}))}clearInput(){const e=this.element.querySelector(".chat-input");e&&(e.value="",e.style.height="auto");const t=this.element.querySelector('[data-container="attachments"]');t&&(t.innerHTML=""),this.attachments=[],this.pendingUploads.clear()}autoResizeTextarea(e){e.style.height="auto",e.style.height=Math.min(e.scrollHeight,150)+"px"}formatFileSize(e){if(0===e)return"0 B";const t=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,t)).toFixed(1))+" "+["B","KB","MB","GB"][t]}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}}l.applyFileDropMixin(ChatInputView);class ChatView extends t.View{constructor(e={}){super({className:"chat-view",...e}),this.adapter=e.adapter,this.theme=e.theme||"compact",this.currentUserId=e.currentUserId,this.inputPlaceholder=e.inputPlaceholder||"Type a message...",this.inputButtonText=e.inputButtonText||"Send",this.messages=[],this.messageViews=/* @__PURE__ */new Map}getTemplate(){return`\n <div class="chat-container chat-theme-${this.theme}">\n <div class="chat-messages" data-container="messages"></div>\n <div class="chat-input-wrapper" data-container="input"></div>\n </div>\n `}async onInit(){this.messages=await this.adapter.fetch(),this.inputView=new ChatInputView({containerId:"input",placeholder:this.inputPlaceholder,buttonText:this.inputButtonText}),this.addChild(this.inputView),this.inputView.on("message:send",async e=>{await this.handleSendMessage(e)})}async onAfterRender(){this._buildMessageViews(),await this._renderChildren(),this.scrollToBottom()}async _renderChildren(){await super._renderChildren();const e=this.element.querySelector('[data-container="messages"]');e?this.messageViews.forEach(t=>{e.appendChild(t.element),t.render(!1)}):console.error("ChatView: messages container not found")}_buildMessageViews(){this.messages&&0!==this.messages.length&&this.messages.forEach(e=>{this.messageViews.has(e.id)||this._createMessageView(e)})}_createMessageView(e){if(this.messageViews.has(e.id))return;const t=e.author&&e.author.id===this.currentUserId,s=new ChatMessageView({message:e,theme:this.theme,isCurrentUser:t});return this.addChild(s),this.messageViews.set(e.id,s),s}addMessage(e,t=!0){if(this.messageViews.has(e.id))return;const s=this._createMessageView(e);if(this.isMounted()){const e=this.element.querySelector('[data-container="messages"]');e&&(e.appendChild(s.element),s.render(!1))}t&&this.scrollToBottom()}async handleSendMessage(e){try{if(e.text&&e.text.trim()&&!(await this.adapter.addNote({text:e.text,files:e.files&&e.files.length>0?[e.files[0]]:[]})).success)throw new Error("Failed to send message");for(let t=e.text&&e.text.trim()&&e.files.length>0?1:0;t<(e.files?.length||0);t++){const s=e.files[t];(await this.adapter.addNote({text:"",files:[s]})).success||console.error("Failed to upload file:",s)}this.messages=await this.adapter.fetch(),this.messages.forEach(e=>{this.messageViews.has(e.id)||this.addMessage(e,!0)}),this.inputView.clearInput()}catch(t){console.error("Failed to send message:",t)}}scrollToBottom(){const e=this.element.querySelector(".chat-messages");e&&requestAnimationFrame(()=>{e.scrollTop=e.scrollHeight})}clearMessages(){this.messageViews.forEach(e=>e.destroy()),this.messageViews.clear(),this.messages=[];const e=this.element.querySelector('[data-container="messages"]');e&&(e.innerHTML="")}async refresh(){this.clearMessages(),this.messages=await this.adapter.fetch(),this._buildMessageViews(),this.isMounted()&&(await this._renderChildren(),this.scrollToBottom())}}exports.BundleByOptions=u,exports.ChatInputView=ChatInputView,exports.ChatMessageView=ChatMessageView,exports.ChatView=ChatView,exports.CommonEventFields=b,exports.ComparatorOptions=p,exports.EmailDomain=EmailDomain,exports.EmailDomainForms={create:{title:"Add Email Domain",fields:[{name:"name",type:"text",label:"Domain Name",placeholder:"example.com",required:!0,columns:12,help:"Enter the root domain to verify with SES (no protocol)."},{name:"region",type:"text",label:"AWS Region (optional)",placeholder:"us-east-1",columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with SES permissions",columns:12,help:"Optional, AWS Key with SES permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with SES permissions",columns:12,help:"Optional, AWS Secret with SES permissions"},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12,help:"Catch-all SES receipt rule to S3 + SNS; routing is done in-app."}]},edit:{title:"Edit Email Domain",fields:[{name:"name",type:"text",label:"Domain Name",placeholder:"example.com",required:!0,columns:12,readonly:!0,help:"Domain name cannot be changed after creation."},{name:"region",type:"text",label:"AWS Region",placeholder:"us-east-1",columns:12},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12},{name:"s3_inbound_bucket",type:"text",label:"Inbound S3 Bucket",placeholder:"my-inbound-bucket",columns:12},{name:"s3_inbound_prefix",type:"text",label:"Inbound S3 Prefix",placeholder:"inbound/example.com/",columns:12},{name:"dns_mode",type:"select",label:"DNS Mode",options:[{value:"manual",text:"Manual (show records)"},{value:"godaddy",text:"GoDaddy (apply via API)"}],columns:12}]},credentials:{fields:[{name:"region",type:"select",label:"AWS Region (optional)",placeholder:"us-east-1",options:[{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with SES permissions",columns:12,help:"Optional, AWS Key with SES permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with SES permissions",columns:12,help:"Optional, AWS Secret with SES permissions"}]},onboard:{title:"Onboard Domain",fields:[{type:"header",text:"Receiving",level:6,className:"mt-2"},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12},{name:"s3_inbound_bucket",type:"text",label:"Inbound S3 Bucket",placeholder:"my-inbound-bucket",columns:12,help:"Required if receiving is enabled."},{name:"s3_inbound_prefix",type:"text",label:"Inbound S3 Prefix",placeholder:"inbound/example.com/",columns:12},{type:"header",text:"MAIL FROM (optional)",level:6,className:"mt-3"},{name:"ensure_mail_from",type:"switch",label:"Ensure MAIL FROM Setup",columns:12},{name:"mail_from_subdomain",type:"text",label:"MAIL FROM Subdomain",placeholder:"feedback",columns:12},{type:"header",text:"DNS",level:6,className:"mt-3"},{name:"dns_mode",type:"select",label:"DNS Mode",options:[{value:"manual",text:"Manual (show records)"},{value:"godaddy",text:"GoDaddy (apply via API)"}],value:"manual",columns:12},{name:"godaddy_key",type:"text",label:"GoDaddy API Key",columns:12,help:"Required when DNS Mode = GoDaddy."},{name:"godaddy_secret",type:"password",label:"GoDaddy API Secret",columns:12},{type:"header",text:"Webhook Endpoints",level:6,className:"mt-3"},{name:"endpoints.bounce",type:"text",label:"Bounce Endpoint",placeholder:"https://portal.example.com/api/aws/sns/bounce",columns:12},{name:"endpoints.complaint",type:"text",label:"Complaint Endpoint",placeholder:"https://portal.example.com/api/aws/sns/complaint",columns:12},{name:"endpoints.delivery",type:"text",label:"Delivery Endpoint",placeholder:"https://portal.example.com/api/aws/sns/delivery",columns:12},{name:"endpoints.inbound",type:"text",label:"Inbound Endpoint",placeholder:"https://portal.example.com/api/aws/sns/inbound",columns:12}]}},exports.EmailDomainList=EmailDomainList,exports.EmailTemplate=EmailTemplate,exports.EmailTemplateForms={create:{title:"Add Email Template",fields:[{name:"name",type:"text",label:"Name",required:!0,cols:12},{name:"subject_template",type:"text",label:"Subject Template",cols:12},{name:"html_template",type:"textarea",label:"HTML Template",rows:8,cols:12},{name:"text_template",type:"textarea",label:"Text Template",rows:6,cols:12}]},edit:{title:"Edit Email Template",fields:[{name:"name",type:"text",label:"Name",required:!0,cols:12},{name:"subject_template",type:"text",label:"Subject Template",cols:12},{name:"html_template",type:"textarea",label:"HTML Template",rows:8,cols:12},{name:"text_template",type:"textarea",label:"Text Template",rows:6,cols:12}]}},exports.EmailTemplateList=EmailTemplateList,exports.File=c,exports.FileForms={create:{title:"Add File",fields:[]},edit:{title:"Edit File Backend",fields:[]}},exports.FileList=FileList,exports.FileManager=FileManager,exports.FileManagerForms=r,exports.FileManagerList=FileManagerList,exports.FilePreviewView=FilePreviewView,exports.FileUpload=FileUpload,exports.GeoLocatedIP=GeoLocatedIP,exports.GeoLocatedIPList=GeoLocatedIPList,exports.Incident=Incident,exports.IncidentEvent=IncidentEvent,exports.IncidentEventForms=d,exports.IncidentEventList=IncidentEventList,exports.IncidentForms={create:{title:"Create Incident",fields:[{type:"tabset",name:"settingsTabs",tabs:[{label:"General",fields:[{name:"title",type:"text",label:"Title",required:!0,columns:12},{name:"details",type:"textarea",label:"Details",required:!0,columns:12}]},{label:"Advanced",fields:[{name:"priority",type:"select",label:"Priority",options:["1","2","3","4","5","6","7","8","9","10"],value:5,columns:6},{name:"status",type:"select",label:"Status",value:"open",options:["open","investigating","resolved","closed","paused","ignored"],columns:6},{name:"category",type:"text",label:"Category",value:"manual",columns:6}]},{label:"Metadata",fields:[{name:"metadata",type:"json",label:"Metadata",value:{example:"hello world"},rows:15,columns:12}]}]}]},edit:{title:"Edit Incident",fields:[{name:"category",type:"text",label:"Category",cols:6},{name:"state",type:"select",label:"State",placeholder:"Select State",options:["new","opened","paused","ignore","resolved"],cols:3},{name:"priority",type:"text",label:"Priority"},{name:"details",type:"textarea",label:"Description",placeholder:"Enter Name",cols:12},{name:"model_name",type:"text",label:"Model",placeholder:"Enter Model",cols:8},{name:"model_id",type:"text",label:"Model ID",placeholder:"Enter Model ID",cols:4}]}},exports.IncidentHistory=IncidentHistory,exports.IncidentHistoryList=IncidentHistoryList,exports.IncidentList=IncidentList,exports.IncidentRule=IncidentRule,exports.IncidentRuleList=IncidentRuleList,exports.IncidentRuleSet=IncidentRuleSet,exports.IncidentRuleSetList=IncidentRuleSetList,exports.IncidentStats=IncidentStats,exports.Job=Job,exports.JobEvent=JobEvent,exports.JobEventList=JobEventList,exports.JobForms={publish:{title:"Publish New Job",fields:[{name:"func",type:"text",label:"Function",required:!0,placeholder:"myapp.jobs.send_email",help:"Module path to job function"},{name:"channel",type:"text",label:"Channel",value:"default",help:'Queue channel (default: "default")'},{name:"payload",type:"textarea",label:"Payload (JSON)",required:!0,rows:8,placeholder:'{\n "key": "value"\n}',help:"JSON data passed to the job function"},{name:"delay",type:"number",label:"Delay (seconds)",min:0,help:"Delay execution by specified seconds"},{name:"run_at",type:"datetime-local",label:"Run At",help:"Schedule for specific date/time"},{name:"max_retries",type:"number",label:"Max Retries",value:3,min:0,max:10},{name:"expires_in",type:"number",label:"Expires In (seconds)",value:900,min:60,help:"Job will expire if not completed in this time"},{name:"broadcast",type:"switch",label:"Broadcast to All Workers",help:"Execute on all available workers"}]},retry:{title:"Retry Job",fields:[{name:"delay",type:"number",label:"Delay (seconds)",value:0,min:0,help:"Delay before retry (0 = immediate)"}]},clone:{title:"Clone Job",fields:[{name:"channel",type:"text",label:"Channel",help:"Override channel for cloned job"},{name:"payload",type:"textarea",label:"Modified Payload (JSON)",rows:8,help:"Modified payload for cloned job"},{name:"delay",type:"number",label:"Delay (seconds)",min:0}]}},exports.JobList=JobList,exports.JobLog=JobLog,exports.JobLogList=JobLogList,exports.JobRunner=JobRunner,exports.JobRunnerForms={broadcast:{title:"Broadcast Command",fields:[{name:"command",type:"select",label:"Command",required:!0,options:[{value:"status",label:"Status Check"},{value:"pause",label:"Pause Processing"},{value:"resume",label:"Resume Processing"},{value:"reload",label:"Reload Configuration"},{value:"shutdown",label:"Shutdown All Runners"}],help:"Command to send to all runners"},{name:"timeout",type:"number",label:"Timeout (seconds)",value:2,min:.5,max:10,step:.5,help:"How long to wait for responses"}]}},exports.JobRunnerList=JobRunnerList,exports.JobsEngineStats=JobsEngineStats,exports.Log=Log,exports.LogList=LogList,exports.Mailbox=Mailbox,exports.MailboxForms=o,exports.MailboxList=MailboxList,exports.MatchByOptions=h,exports.Member=Member,exports.MemberForms=f,exports.MemberList=MemberList,exports.MetricsForms={edit:{title:"Edit Metrics Permissions",fields:[{name:"account",type:"text",label:"Account",columns:12},{name:"view_permissions",type:"tags",label:"View Permissions",help:'Enter permissions or "public"',columns:12},{name:"write_permissions",type:"tags",label:"Write Permissions",help:"Enter permissions",columns:12}]}},exports.MetricsPermission=MetricsPermission,exports.MetricsPermissionList=MetricsPermissionList,exports.ProgressView=ProgressView,exports.PushConfig=PushConfig,exports.PushConfigForms=w,exports.PushConfigList=PushConfigList,exports.PushDelivery=PushDelivery,exports.PushDeliveryList=PushDeliveryList,exports.PushDevice=PushDevice,exports.PushDeviceList=PushDeviceList,exports.PushTemplate=PushTemplate,exports.PushTemplateForms=v,exports.PushTemplateList=PushTemplateList,exports.Rule=Rule,exports.RuleForms=y,exports.RuleList=RuleList,exports.RuleSet=RuleSet,exports.RuleSetForms=g,exports.RuleSetList=RuleSetList,exports.S3Bucket=S3Bucket,exports.S3BucketForms={create:{title:"Add S3 Bucket",fields:[{name:"bucket_name",type:"text",label:"Name",placeholder:"bucket name",help:"Enter a universally unique name for the bucket",required:!0,cols:12},{name:"is_public",type:"switch",label:"Is Public",cols:12}]},edit:{title:"Edit S3 Bucket",fields:[{name:"bucket_name",type:"text",label:"Name",placeholder:"bucket name",help:"Enter a universally unique name for the bucket",required:!0,cols:12},{name:"is_public",type:"switch",label:"Is Public",cols:12}]}},exports.S3BucketList=S3BucketList,exports.SentMessage=SentMessage,exports.SentMessageForms={view:{title:"Sent Message Details",fields:[{name:"id",type:"text",label:"ID",readonly:!0,cols:6},{name:"ses_message_id",type:"text",label:"SES Message ID",readonly:!0,cols:6},{name:"from_email",type:"text",label:"From",readonly:!0,cols:12},{name:"to",type:"textarea",label:"To",readonly:!0,rows:2,cols:12},{name:"cc",type:"textarea",label:"CC",readonly:!0,rows:2,cols:12},{name:"bcc",type:"textarea",label:"BCC",readonly:!0,rows:2,cols:12},{name:"subject",type:"text",label:"Subject",readonly:!0,cols:12},{name:"status",type:"text",label:"Status",readonly:!0,cols:6},{name:"status_reason",type:"textarea",label:"Status Reason",readonly:!0,rows:3,cols:12},{name:"created",type:"text",label:"Created",readonly:!0,cols:6}]}},exports.SentMessageList=SentMessageList,exports.TabView=TabView,exports.TablePage=TablePage,exports.TableRow=TableRow,exports.TableView=TableView,exports.Ticket=Ticket,exports.TicketCategories=S,exports.TicketForms=E,exports.TicketList=TicketList,exports.TicketNote=TicketNote,exports.TicketNoteList=TicketNoteList,exports.ValueTypeOptions=m;
|
|
2
|
-
//# sourceMappingURL=ChatView-CGBaudUc.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu-B4_YS0G8.js","sources":["../../src/core/services/ToastService.js","../../src/core/models/Group.js","../../src/core/models/User.js","../../src/core/views/feedback/ContextMenu.js"],"sourcesContent":["/**\n * ToastService - Bootstrap 5 Toast Notification Service\n * Provides methods to display toast notifications with different types and options\n *\n * Features:\n * - Bootstrap 5 toast integration\n * - Multiple toast types (success, error, info, warning)\n * - Auto-dismiss with customizable delays\n * - Toast container management\n * - Event integration\n * - Proper cleanup and memory management\n *\n * @example\n * const toastService = new ToastService();\n * toastService.success('Operation completed successfully!');\n * toastService.error('Something went wrong');\n * toastService.info('FYI: This is informational');\n * toastService.warning('Please be careful');\n */\n\nclass ToastService {\n constructor(options = {}) {\n this.options = {\n containerId: 'toast-container',\n position: 'top-end', // top-start, top-center, top-end, middle-start, etc.\n autohide: true,\n defaultDelay: 5000, // 5 seconds\n maxToasts: 5, // Maximum number of toasts to show at once\n ...options\n };\n\n this.toasts = new Map(); // Track active toasts\n this.toastCounter = 0; // For unique IDs\n \n this.init();\n }\n\n /**\n * Initialize the toast service\n */\n init() {\n this.createContainer();\n }\n\n /**\n * Create the toast container if it doesn't exist\n */\n createContainer() {\n let container = document.getElementById(this.options.containerId);\n \n if (!container) {\n container = document.createElement('div');\n container.id = this.options.containerId;\n container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n container.style.zIndex = '1070'; // Bootstrap toast z-index\n container.setAttribute('aria-live', 'polite');\n container.setAttribute('aria-atomic', 'true');\n \n document.body.appendChild(container);\n }\n \n this.container = container;\n }\n\n /**\n * Get CSS classes for toast positioning\n */\n getPositionClasses() {\n const positionMap = {\n 'top-start': 'top-0 start-0 p-3',\n 'top-center': 'top-0 start-50 translate-middle-x p-3',\n 'top-end': 'top-0 end-0 p-3',\n 'middle-start': 'top-50 start-0 translate-middle-y p-3',\n 'middle-center': 'top-50 start-50 translate-middle p-3',\n 'middle-end': 'top-50 end-0 translate-middle-y p-3',\n 'bottom-start': 'bottom-0 start-0 p-3',\n 'bottom-center': 'bottom-0 start-50 translate-middle-x p-3',\n 'bottom-end': 'bottom-0 end-0 p-3'\n };\n \n return positionMap[this.options.position] || positionMap['top-end'];\n }\n\n\n\n /**\n * Show a success toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n success(message, options = {}) {\n return this.show(message, 'success', {\n icon: 'bi-check-circle-fill',\n ...options\n });\n }\n\n /**\n * Show an error toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n error(message, options = {}) {\n return this.show(message, 'error', {\n icon: 'bi-exclamation-triangle-fill',\n autohide: false, // Keep error toasts visible until manually dismissed\n ...options\n });\n }\n\n /**\n * Show an info toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n info(message, options = {}) {\n return this.show(message, 'info', {\n icon: 'bi-info-circle-fill',\n ...options\n });\n }\n\n /**\n * Show a warning toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n warning(message, options = {}) {\n return this.show(message, 'warning', {\n icon: 'bi-exclamation-triangle-fill',\n ...options\n });\n }\n\n /**\n * Show a plain toast without specific styling\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n plain(message, options = {}) {\n return this.show(message, 'plain', {\n ...options\n });\n }\n\n /**\n * Show a toast with specified type and options\n * @param {string} message - The message to display\n * @param {string} type - Toast type (success, error, info, warning)\n * @param {object} options - Additional options\n */\n show(message, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n \n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: this.getDefaultTitle(type),\n icon: this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createToastElement(toastId, message, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n message: message\n });\n\n // Setup cleanup on hide\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanup(toastId);\n });\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding toast:', error);\n }\n },\n dispose: () => this.cleanup(toastId),\n updateProgress: options.updateProgress || null\n };\n }\n\n /**\n * Show a toast with a View component in the body\n * @param {View} view - The View component to display\n * @param {string} type - Toast type (success, error, info, warning, plain)\n * @param {object} options - Additional options\n */\n showView(view, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n \n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: options.title || this.getDefaultTitle(type),\n icon: options.icon || this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createViewToastElement(toastId, view, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference with view\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n view: view,\n message: 'View toast'\n });\n\n // Setup cleanup on hide - dispose view properly\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanupView(toastId);\n });\n\n // Mount and render the view\n const bodyContainer = toastElement.querySelector('.toast-view-body');\n if (bodyContainer && view) {\n view.render(true, bodyContainer);\n }\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n view: view,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding view toast:', error);\n }\n },\n dispose: () => this.cleanupView(toastId),\n updateProgress: (progressInfo) => {\n if (view && typeof view.updateProgress === 'function') {\n view.updateProgress(progressInfo);\n }\n }\n };\n }\n\n /**\n * Create toast DOM element\n */\n createToastElement(id, message, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createToastBody(message, config.icon && !config.title);\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast DOM element for View component\n */\n createViewToastElement(id, view, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createViewToastBody();\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast body for View component\n */\n createViewToastBody() {\n return `\n <div class=\"toast-body p-0\">\n <div class=\"toast-view-body p-3\"></div>\n </div>\n `;\n }\n\n /**\n * Create toast header with title and icon\n */\n createToastHeader(config, _type) {\n const iconHtml = config.icon ? \n `<i class=\"${config.icon} toast-service-icon me-2\"></i>` : '';\n \n const titleHtml = config.title ? \n `<strong class=\"me-auto\">${iconHtml}${this.escapeHtml(config.title)}</strong>` : '';\n\n const timeHtml = config.showTime ? \n `<small class=\"text-muted\">${this.getTimeString()}</small>` : '';\n\n const closeButton = config.dismissible ? \n `<button type=\"button\" class=\"btn-close toast-service-close\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>` : '';\n\n if (!titleHtml && !timeHtml && !closeButton) {\n return '';\n }\n\n return `\n <div class=\"toast-header\">\n ${titleHtml}\n ${timeHtml}\n ${closeButton}\n </div>\n `;\n }\n\n /**\n * Create toast body with message\n */\n createToastBody(message, showIcon = false) {\n const iconHtml = showIcon ? \n `<i class=\"${this.getDefaultIcon('info')} toast-service-icon me-2\"></i>` : '';\n \n return `\n <div class=\"toast-body d-flex align-items-center\">\n ${iconHtml}\n <span>${this.escapeHtml(message)}</span>\n </div>\n `;\n }\n\n /**\n * Get default title for toast type\n */\n getDefaultTitle(type) {\n const titles = {\n success: 'Success',\n error: 'Error',\n warning: 'Warning',\n info: 'Information',\n plain: ''\n };\n return titles[type] || 'Notification';\n }\n\n /**\n * Get default icon for toast type\n */\n getDefaultIcon(type) {\n const icons = {\n success: 'bi-check-circle-fill',\n error: 'bi-exclamation-triangle-fill',\n warning: 'bi-exclamation-triangle-fill',\n info: 'bi-info-circle-fill',\n plain: ''\n };\n return icons[type] || 'bi-info-circle-fill';\n }\n\n /**\n * Enforce maximum number of toasts\n */\n enforceMaxToasts() {\n const activeToasts = Array.from(this.toasts.values());\n \n if (activeToasts.length >= this.options.maxToasts) {\n // Remove oldest toast\n const oldestId = this.toasts.keys().next().value;\n const oldest = this.toasts.get(oldestId);\n \n if (oldest) {\n oldest.bootstrap.hide();\n }\n }\n }\n\n /**\n * Clean up toast resources\n */\n cleanup(toastId) {\n const toast = this.toasts.get(toastId);\n \n if (toast) {\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n \n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n \n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Clean up view toast resources with proper view disposal\n */\n cleanupView(toastId) {\n const toast = this.toasts.get(toastId);\n \n if (toast) {\n // Dispose view first if it exists\n if (toast.view && typeof toast.view.dispose === 'function') {\n try {\n toast.view.dispose();\n } catch (e) {\n console.warn('Error disposing view in toast:', e);\n }\n }\n \n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n \n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n \n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Hide all active toasts\n */\n hideAll() {\n this.toasts.forEach((toast, _id) => {\n toast.bootstrap.hide();\n });\n }\n\n /**\n * Clear all toasts immediately\n */\n clearAll() {\n this.toasts.forEach((toast, id) => {\n this.cleanup(id);\n });\n }\n\n /**\n * Get current time string\n */\n getTimeString() {\n return new Date().toLocaleTimeString([], { \n hour: '2-digit', \n minute: '2-digit' \n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n /**\n * Dispose of the entire toast service\n */\n dispose() {\n this.clearAll();\n \n if (this.container && this.container.parentNode) {\n this.container.parentNode.removeChild(this.container);\n }\n }\n\n /**\n * Get statistics about active toasts\n */\n getStats() {\n const stats = {\n total: this.toasts.size,\n byType: {}\n };\n \n this.toasts.forEach(toast => {\n stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;\n });\n \n return stats;\n }\n\n /**\n * Set global options\n */\n setOptions(newOptions) {\n this.options = { ...this.options, ...newOptions };\n \n // Recreate container if position changed\n if (newOptions.position) {\n if (this.container) {\n this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n }\n }\n }\n}\n\nexport default ToastService;","\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\n/**\n * Group Model - Represents an organization, team, or group entity\n *\n * Features:\n * - Hierarchical group support (parent/child relationships)\n * - Member management\n * - Search and filtering capabilities\n * - Role-based permissions within groups\n * - Metadata and settings management\n */\nclass Group extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/group'\n });\n }\n}\n\n/**\n * GroupCollection - Enhanced collection for managing groups with advanced search and filtering\n */\nclass GroupList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Group,\n endpoint: '/api/group',\n size: 10,\n ...options\n });\n }\n}\n\nconst GroupKinds = {\n 'org': 'Organization',\n 'division': 'Division',\n 'department': 'Department',\n 'team': 'Team',\n 'merchant': 'Merchant',\n 'partner': 'Partner',\n 'client': 'Client',\n 'iso': 'ISO',\n 'sales': 'Sales',\n 'reseller': 'Reseller',\n 'location': 'Location',\n 'region': 'Region',\n 'route': 'Route',\n 'project': 'Project',\n \"inventory\": \"Inventory\",\n 'test': 'Testing',\n 'misc': 'Miscellaneous',\n 'qa': 'Quality Assurance'\n};\n\n// Convert GroupKinds to select options\nconst GroupKindOptions = Object.entries(GroupKinds).map(([key, label]) => ({\n value: key,\n label: label\n}));\n\n/**\n * Form configurations for group management\n */\nconst GroupForms = {\n create: {\n title: 'Create Group',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name'\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n options: GroupKindOptions\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n }\n ]\n },\n\n edit: {\n title: 'Edit Group',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name',\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n options: GroupKindOptions\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n },\n {\n name: 'metadata.domain',\n type: 'text',\n label: 'Default Domain',\n placeholder: 'Enter Domain',\n },\n {\n name: 'metadata.portal',\n type: 'text',\n label: 'Default Portal',\n placeholder: 'Enter Portal URL',\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n cols: 4\n },\n ]\n },\n\n detailed: {\n title: 'Group Details',\n fields: [\n // Profile Header\n {\n type: 'header',\n text: 'Profile Information',\n level: 4,\n class: 'text-primary mb-3'\n },\n\n // Avatar and Basic Info\n {\n type: 'group',\n columns: { xs: 12, md: 4 },\n fields: [\n {\n type: 'image',\n name: 'avatar',\n size: 'lg',\n imageSize: { width: 200, height: 200 },\n placeholder: 'Upload your avatar',\n help: 'Square images work best',\n columns: 12\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n columns: 12\n },\n ]\n },\n\n // Profile Details\n {\n type: 'group',\n columns: { xs: 12, md: 8 },\n title: 'Details',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name',\n columns: 12\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n columns: 12,\n options: [\n { value: 'org', label: 'Organization' },\n { value: 'team', label: 'Team' },\n { value: 'department', label: 'Department' },\n { value: 'merchant', label: 'Merchant' },\n { value: 'iso', label: 'ISO' },\n { value: 'group', label: 'Group' }\n ]\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n columns: 12\n }\n ]\n },\n\n // Account Settings\n {\n type: 'group',\n columns: 12,\n title: 'Account Settings',\n class: \"pt-3\",\n fields: [\n {\n type: 'select',\n name: 'metadata.timezone',\n label: 'Timezone',\n columns: 6,\n options: [\n { value: 'America/New_York', text: 'Eastern Time' },\n { value: 'America/Chicago', text: 'Central Time' },\n { value: 'America/Denver', text: 'Mountain Time' },\n { value: 'America/Los_Angeles', text: 'Pacific Time' },\n { value: 'UTC', text: 'UTC' }\n ]\n },\n {\n type: 'select',\n name: 'metadata.language',\n label: 'Language',\n columns: 6,\n options: [\n { value: 'en', text: 'English' },\n { value: 'es', text: 'Spanish' },\n { value: 'fr', text: 'French' },\n { value: 'de', text: 'German' }\n ]\n },\n {\n type: 'switch',\n name: 'metadata.notify.email',\n label: 'Email Notifications',\n columns: 4\n },\n {\n type: 'switch',\n name: 'metadata.profile_public',\n label: 'Public Profile',\n columns: 4\n }\n ]\n }\n ]\n },\n};\n\nGroup.EDIT_FORM = GroupForms.edit;\nGroup.ADD_FORM = GroupForms.create;\nGroup.CREATE_FORM = GroupForms.create; // Alias for compatibility\nGroup.GroupKindOptions = GroupKindOptions;\nGroup.GroupKinds = GroupKinds;\nexport { Group, GroupList, GroupForms };\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\nimport { GroupList } from './Group.js';\n\nclass User extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user'\n });\n }\n\n hasPermission(permission) {\n if (Array.isArray(permission)) {\n return permission.some(p => this.hasPermission(p));\n }\n\n // Check if permission has \"sys.\" prefix\n const isSysPermission = permission.startsWith('sys.');\n const permissionToCheck = isSysPermission ? permission.substring(4) : permission;\n\n if (this._hasPermission(permissionToCheck)) {\n return true;\n }\n\n // Only check member permissions if it's not a system permission\n if (!isSysPermission && this.member && this.member.hasPermission(permission)) {\n return true;\n }\n\n return false;\n }\n\n _hasPermission(permission) {\n const permissions = this.get(\"permissions\");\n if (!permissions) {\n return false;\n }\n return permissions[permission] == true;\n }\n\n hasPerm(p) {\n return this.hasPermission(p);\n }\n}\n\nclass UserList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: User,\n endpoint: '/api/user',\n ...options\n });\n }\n}\n\nUser.PERMISSIONS = [\n { name: \"manage_users\", label: \"Manage Users\" },\n { name: \"view_users\", label: \"View Users\" },\n { name: \"view_groups\", label: \"View Groups\" },\n { name: \"manage_groups\", label: \"Manage Groups\" },\n { name: \"view_metrics\", label: \"View System Metrics\" },\n { name: \"manage_metrics\", label: \"Manage System Metrics\" },\n { name: \"view_logs\", label: \"View Logs\" },\n { name: \"view_incidents\", label: \"View Incidents\" },\n { name: \"manage_incidents\", label: \"Manage Incidents\" },\n { name: \"view_tickets\", label: \"View Tickets\" },\n { name: \"manage_tickets\", label: \"Manage Tickets\" },\n { name: \"view_admin\", label: \"View Admin\" },\n { name: \"view_jobs\", label: \"View Jobs\" },\n { name: \"manage_jobs\", label: \"Manage Jobs\" },\n { name: \"view_global\", label: \"View Global\" },\n { name: \"manage_notifications\", label: \"Manage Notifications\" },\n { name: \"manage_files\", label: \"Manage Files\" },\n { name: \"force_single_session\", label: \"Force Single Session\" },\n { name: \"file_vault\", label: \"Access File Vault\" },\n { name: \"manage_aws\", label: \"Manage AWS\" },\n { name: \"manage_docit\", label: \"Manage DocIt\" }\n];\n\n\nUser.PERMISSION_FIELDS = [\n ...User.PERMISSIONS.map(permission => ({\n name: `permissions.${permission.name}`,\n type: 'switch',\n label: permission.label,\n columns: 4\n }))\n];\n\nconst UserForms = {\n create: {\n title: 'Create User',\n fields: [\n { name: 'email', type: 'text', label: 'Email', required: true },\n { name: 'phone_number', type: 'text', label: 'Phone number', columns: 12 },\n { name: 'display_name', type: 'text', label: 'Display Name' }\n ]\n },\n edit: {\n title: 'Edit User',\n fields: [\n { name: 'email', type: 'email', label: 'Email', columns: 12 },\n { name: 'display_name', type: 'text', label: 'Display Name', columns: 12},\n { name: 'phone_number', type: 'text', label: 'Phone number', columns: 12 },\n { type: 'collection', name: 'org', label: 'Organization', Collection: GroupList, labelField: 'name', valueField: 'id', columns: 12 },\n ]\n },\n permissions: {\n title: 'Edit Permissions',\n fields: User.PERMISSION_FIELDS\n }\n};\n\n\n// DataView configuration for User model\nconst UserDataView = {\n // Basic user profile view\n profile: {\n title: 'User Profile',\n columns: 2,\n fields: [\n {\n name: 'id',\n label: 'User ID',\n type: 'number',\n columns: 4\n },\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n columns: 4\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n columns: 4\n },\n {\n name: 'username',\n label: 'Username',\n type: 'text',\n format: 'lowercase',\n columns: 4\n },\n {\n name: 'display_name',\n label: 'Display Name',\n type: 'text',\n columns: 4\n },\n\n {\n name: 'email',\n label: 'Email',\n type: 'email',\n columns: 12\n },\n\n {\n name: 'org.name',\n label: 'Organization',\n type: 'text',\n columns: 6\n },\n {\n name: 'phone_number',\n label: 'Phone Number',\n type: 'text',\n columns: 6\n }\n ]\n },\n\n // Activity tracking view\n activity: {\n title: 'User Activity',\n columns: 2,\n fields: [\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n }\n ]\n },\n\n // Comprehensive view with all data\n detailed: {\n title: 'Detailed User Information',\n columns: 2,\n showEmptyValues: true,\n emptyValueText: 'Not set',\n fields: [\n // Basic Info Section\n {\n name: 'id',\n label: 'User ID',\n type: 'number',\n colSize: 3\n },\n {\n name: 'display_name',\n label: 'Display Name',\n type: 'text',\n format: 'capitalize|default(\"Unnamed User\")',\n colSize: 9\n },\n {\n name: 'username',\n label: 'Username',\n type: 'text',\n format: 'lowercase',\n colSize: 6\n },\n {\n name: 'email',\n label: 'Email Address',\n type: 'email',\n colSize: 6\n },\n {\n name: 'phone_number',\n label: 'Phone Number',\n type: 'phone',\n format: 'phone|default(\"Not provided\")',\n colSize: 6\n },\n {\n name: 'is_active',\n label: 'Account Status',\n type: 'boolean',\n colSize: 6\n },\n\n // Activity Info\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n\n // Avatar Info\n {\n name: 'avatar.url',\n label: 'Avatar',\n type: 'url',\n colSize: 12\n },\n\n // Complex Data (will use full width automatically)\n {\n name: 'permissions',\n label: 'User Permissions',\n type: 'dataview',\n dataViewColumns: 2,\n showEmptyValues: false\n },\n {\n name: 'metadata',\n label: 'User Metadata',\n type: 'dataview',\n dataViewColumns: 1\n },\n {\n name: 'avatar',\n label: 'Avatar Details',\n type: 'dataview',\n dataViewColumns: 1\n }\n ]\n },\n\n // Permissions-focused view\n permissions: {\n title: 'User Permissions',\n columns: 1,\n fields: [\n {\n name: 'display_name',\n label: 'User',\n type: 'text',\n format: 'capitalize',\n columns: 12\n },\n {\n name: 'permissions',\n label: 'Assigned Permissions',\n type: 'dataview',\n dataViewColumns: 3,\n showEmptyValues: false,\n colSize: 12\n }\n ]\n },\n\n // Compact summary view\n summary: {\n title: 'User Summary',\n columns: 3,\n fields: [\n {\n name: 'display_name',\n label: 'Name',\n type: 'text',\n format: 'capitalize|truncate(30)'\n },\n {\n name: 'email',\n label: 'Email',\n type: 'email'\n },\n {\n name: 'is_active',\n label: 'Status',\n type: 'boolean'\n },\n {\n name: 'last_activity',\n label: 'Last Seen',\n type: 'datetime',\n format: 'relative',\n colSize: 12\n }\n ]\n }\n};\n\nUser.DATA_VIEW = UserDataView.detailed;\nUser.EDIT_FORM = UserForms.edit;\nUser.ADD_FORM = UserForms.create;\n\n/* =========================\n * UserDevice\n * ========================= */\nclass UserDevice extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user/device',\n });\n }\n\n static async getByDuid(duid) {\n const model = new UserDevice();\n const resp = await model.rest.GET('/api/user/device/lookup', { duid: duid });\n if (resp.success && resp.data && resp.data.data) {\n // A direct lookup should return a single object\n return new UserDevice(resp.data.data);\n }\n return null;\n }\n}\n\nclass UserDeviceList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: UserDevice,\n endpoint: '/api/user/device',\n ...options,\n });\n }\n}\n\n/* =========================\n * UserDeviceLocation\n * ========================= */\nclass UserDeviceLocation extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user/device/location',\n });\n }\n}\n\nclass UserDeviceLocationList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: UserDeviceLocation,\n endpoint: '/api/user/device/location',\n ...options,\n });\n }\n}\n\nexport { User, UserList, UserForms, UserDataView, UserDevice, UserDeviceList, UserDeviceLocation, UserDeviceLocationList };\n","/**\n * ContextMenu - A reusable context menu component for MOJO\n *\n * Renders a Bootstrap 5 dropdown menu based on a configuration object.\n * This component is designed to be easily embedded in any other View.\n * It supports the same configuration syntax as the Dialog's contextMenu.\n *\n * Features:\n * - Renders a dropdown button with a configurable icon.\n * - Generates menu items from a configuration array.\n * - Supports dividers, icons, labels, and links.\n * - Handles actions via inline handlers or by emitting an 'action' event.\n *\n * @example\n * const contextMenu = new ContextMenu({\n * config: {\n * icon: 'bi-gear', // Optional: custom trigger icon\n * items: [\n * { label: 'Edit', action: 'edit', icon: 'bi-pencil' },\n * { label: 'Delete', action: 'delete', icon: 'bi-trash', danger: true },\n * { type: 'divider' },\n * {\n * label: 'Custom Action',\n * action: 'custom',\n * icon: 'bi-star',\n * handler: (context) => {\n * console.log('Inline handler called with context:', context);\n * }\n * }\n * ]\n * },\n * context: { id: 123, name: 'My Item' } // Optional data to pass to handlers/events\n * });\n *\n * // Listen for actions from the parent view\n * contextMenu.on('action', (data) => {\n * console.log(`Action '${data.action}' triggered for context:`, data.context);\n * if (data.action === 'edit') {\n * // handle edit\n * }\n * });\n */\n\nimport View from '@core/View.js';\n\nclass ContextMenu extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'context-menu-view dropdown',\n ...options\n });\n\n this.config = options.contextMenu || options.config || {};\n this.context = options.context || {}; // Optional data to pass to handlers/events\n }\n\n /**\n * Build the dropdown menu HTML from the configuration.\n */\n async renderTemplate() {\n const menuItems = this.config.items || [];\n if (menuItems.length === 0) {\n return ''; // Don't render anything if there are no items\n }\n\n const triggerIcon = this.config.icon || 'bi-three-dots-horizontal';\n const buttonClass = this.config.buttonClass || 'btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1';\n const dropdownId = `context-menu-${this.id}`;\n\n const menuItemsHtml = menuItems.map(item => this.buildMenuItemHTML(item)).join('');\n\n return `\n <button class=\"${buttonClass}\" type=\"button\" id=\"${dropdownId}\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"${triggerIcon}\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\" aria-labelledby=\"${dropdownId}\">\n ${menuItemsHtml}\n </ul>\n `;\n }\n\n /**\n * Build the HTML for a single menu item.\n * @param {object} item - The menu item configuration.\n * @returns {string} The HTML string for the list item.\n */\n buildMenuItemHTML(item) {\n if (item.type === 'divider' || item.separator) {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n const icon = item.icon ? `<i class=\"${item.icon} me-2\"></i>` : '';\n const label = item.label || '';\n const itemClass = `dropdown-item ${item.danger ? 'text-danger' : ''} ${item.disabled ? 'disabled' : ''}`;\n const action = item.action || '';\n\n if (item.href) {\n return `<li><a class=\"${itemClass}\" href=\"${item.href}\" target=\"${item.target || '_self'}\">${icon}${label}</a></li>`;\n }\n\n return `<li><a class=\"${itemClass}\" href=\"#\" data-action=\"menu-item-click\" data-item-action=\"${action}\">${icon}${label}</a></li>`;\n }\n\n /**\n * Handle clicks on menu items.\n * @param {Event} event - The click event.\n * @param {HTMLElement} element - The clicked anchor element.\n */\n async onActionMenuItemClick(event, element) {\n event.preventDefault();\n const action = element.getAttribute('data-item-action');\n if (!action) return;\n\n const menuItem = this.config.items.find(item => item.action === action);\n if (!menuItem || menuItem.disabled) return;\n\n // Support for inline handlers\n if (typeof menuItem.handler === 'function') {\n menuItem.handler(this.context, event, element);\n } else {\n // Emit a general event for parent views to listen to\n // this.emit('action', {\n // action: action,\n // context: this.context,\n // sourceEvent: event\n // });\n this.parent.events.dispatch(action, event, element);\n }\n this.closeDropdown();\n }\n\n closeDropdown() {\n const dropdownTrigger = this.element.querySelector('[data-bs-toggle=\"dropdown\"]');\n if (dropdownTrigger) {\n const dropdownInstance = window.bootstrap?.Dropdown.getInstance(dropdownTrigger);\n dropdownInstance?.hide();\n }\n }\n}\n\nexport default ContextMenu;\n"],"names":[],"mappings":"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"}
|