web-mojo 2.1.867 → 2.1.917

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +312 -35
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/{ChatView-8wuIN1zH.js → ChatView-D9hJ2u6c.js} +51 -6
  12. package/dist/chunks/ChatView-D9hJ2u6c.js.map +1 -0
  13. package/dist/chunks/ChatView-u-UdWa-P.js +2 -0
  14. package/dist/chunks/ChatView-u-UdWa-P.js.map +1 -0
  15. package/dist/chunks/{ContextMenu-DPxZgf69.js → ContextMenu-BlmrYFlZ.js} +2 -2
  16. package/dist/chunks/{ContextMenu-DPxZgf69.js.map → ContextMenu-BlmrYFlZ.js.map} +1 -1
  17. package/dist/chunks/{ContextMenu-Bem9gIL6.js → ContextMenu-CfXiUGrB.js} +2 -2
  18. package/dist/chunks/{ContextMenu-Bem9gIL6.js.map → ContextMenu-CfXiUGrB.js.map} +1 -1
  19. package/dist/chunks/{DataView-DhbOr4Wm.js → DataView-BDUCD0q0.js} +11 -21
  20. package/dist/chunks/DataView-BDUCD0q0.js.map +1 -0
  21. package/dist/chunks/DataView-CorHtzkO.js +2 -0
  22. package/dist/chunks/DataView-CorHtzkO.js.map +1 -0
  23. package/dist/chunks/{Dialog-CQA_WzmY.js → Dialog-D9qIh5Jb.js} +5 -5
  24. package/dist/chunks/{Dialog-CQA_WzmY.js.map → Dialog-D9qIh5Jb.js.map} +1 -1
  25. package/dist/chunks/{Dialog-gO9ln-ej.js → Dialog-ifYDY_7S.js} +2 -2
  26. package/dist/chunks/{Dialog-gO9ln-ej.js.map → Dialog-ifYDY_7S.js.map} +1 -1
  27. package/dist/chunks/{FormView-DbPm-jDU.js → FormView-Bq74K6dj.js} +2 -2
  28. package/dist/chunks/{FormView-DbPm-jDU.js.map → FormView-Bq74K6dj.js.map} +1 -1
  29. package/dist/chunks/{FormView-DkxYCmes.js → FormView-Dtzh5qLB.js} +2 -2
  30. package/dist/chunks/{FormView-DkxYCmes.js.map → FormView-Dtzh5qLB.js.map} +1 -1
  31. package/dist/chunks/{MetricsMiniChartWidget-C41dYV1K.js → MetricsMiniChartWidget-BHLlrFJf.js} +2 -2
  32. package/dist/chunks/{MetricsMiniChartWidget-C41dYV1K.js.map → MetricsMiniChartWidget-BHLlrFJf.js.map} +1 -1
  33. package/dist/chunks/{MetricsMiniChartWidget-Dz2d0GG2.js → MetricsMiniChartWidget-CoapKHw0.js} +3 -3
  34. package/dist/chunks/{MetricsMiniChartWidget-Dz2d0GG2.js.map → MetricsMiniChartWidget-CoapKHw0.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-BJT0-zQL.js → PDFViewer-BXcYgXKx.js} +3 -3
  36. package/dist/chunks/{PDFViewer-BJT0-zQL.js.map → PDFViewer-BXcYgXKx.js.map} +1 -1
  37. package/dist/chunks/{PDFViewer-DvTxpaXI.js → PDFViewer-DxoRBrX9.js} +2 -2
  38. package/dist/chunks/{PDFViewer-DvTxpaXI.js.map → PDFViewer-DxoRBrX9.js.map} +1 -1
  39. package/dist/chunks/{Page-CeYPFh_j.js → Page-CFzSUQLz.js} +2 -2
  40. package/dist/chunks/{Page-CeYPFh_j.js.map → Page-CFzSUQLz.js.map} +1 -1
  41. package/dist/chunks/{Page-xjtBxzqE.js → Page-Cw4qW3aV.js} +2 -2
  42. package/dist/chunks/{Page-xjtBxzqE.js.map → Page-Cw4qW3aV.js.map} +1 -1
  43. package/dist/chunks/{TopNav-baMWNGG2.js → TopNav-DaX7k8M8.js} +5 -5
  44. package/dist/chunks/{TopNav-baMWNGG2.js.map → TopNav-DaX7k8M8.js.map} +1 -1
  45. package/dist/chunks/{TopNav-CcS_qMvn.js → TopNav-DgPBfS1e.js} +2 -2
  46. package/dist/chunks/{TopNav-CcS_qMvn.js.map → TopNav-DgPBfS1e.js.map} +1 -1
  47. package/dist/chunks/{WebApp-DkJV3BmZ.js → WebApp-DHNZIzfN.js} +2 -2
  48. package/dist/chunks/{WebApp-DkJV3BmZ.js.map → WebApp-DHNZIzfN.js.map} +1 -1
  49. package/dist/chunks/{WebApp-DLAySO90.js → WebApp-MzDCFzar.js} +13 -13
  50. package/dist/chunks/{WebApp-DLAySO90.js.map → WebApp-MzDCFzar.js.map} +1 -1
  51. package/dist/docit.cjs.js +1 -1
  52. package/dist/docit.es.js +5 -5
  53. package/dist/index.cjs.js +1 -1
  54. package/dist/index.es.js +11 -11
  55. package/dist/lightbox.cjs.js +1 -1
  56. package/dist/lightbox.es.js +4 -4
  57. package/package.json +1 -1
  58. package/dist/chunks/ChatView-8wuIN1zH.js.map +0 -1
  59. package/dist/chunks/ChatView-BtoAS3jU.js +0 -2
  60. package/dist/chunks/ChatView-BtoAS3jU.js.map +0 -1
  61. package/dist/chunks/DataView-C9O8uelM.js +0 -2
  62. package/dist/chunks/DataView-C9O8uelM.js.map +0 -1
  63. package/dist/chunks/DataView-DhbOr4Wm.js.map +0 -1
package/dist/admin.es.js CHANGED
@@ -1,13 +1,13 @@
1
- import { P as Page } from "./chunks/Page-CeYPFh_j.js";
2
- import { V as View, h as MOJOUtils } from "./chunks/WebApp-DLAySO90.js";
3
- import { B, b, a, c, e, f, W } from "./chunks/WebApp-DLAySO90.js";
4
- import Dialog$1 from "./chunks/Dialog-CQA_WzmY.js";
5
- import { M as MetricsChart, c as MetricsMiniChartWidget, P as PieChart } from "./chunks/MetricsMiniChartWidget-Dz2d0GG2.js";
6
- import { b as TablePage, k as EmailDomainForms, j as EmailDomainList, E as EmailDomain, n as MailboxForms, m as MailboxList, l as Mailbox, r as EmailTemplate, d as TabView, t as EmailTemplateForms, s as EmailTemplateList, I as IncidentEvent, B as IncidentEventForms, A as IncidentEventList, w as FileManagerForms, v as FileManagerList, x as File, T as TableView, z as FileForms, y as FileList, av as GeoLocatedIP, aw as GeoLocatedIPList, ag as MemberList, af as LogList, ay as TicketList, G as IncidentList, _ as IncidentStats, R as IncidentHistoryList, Q as IncidentHistory, D as Incident, C as ChatView, H as IncidentForms, a3 as Job, a9 as JobEventList, a7 as JobLogList, a5 as JobForms, a4 as JobList, ac as JobRunnerList, aa as JobsEngineStats, ad as JobRunnerForms, ab as JobRunner, ae as Log, M as Member, ah as MemberForms, ai as MetricsPermission, ak as MetricsForms, aj as MetricsPermissionList, at as PushConfigForms, aq as PushConfigList, as as PushDeliveryList, am as PushDeviceList, au as PushTemplateForms, ao as PushTemplateList, U as RuleSet, a0 as MatchByOptions, $ as BundleByOptions, Y as RuleList, V as RuleSetList, i as S3BucketForms, h as S3BucketList, o as SentMessage, p as SentMessageList, aA as TicketNoteList, az as TicketNote, ax as Ticket, aB as TicketForms, aC as TicketCategories } from "./chunks/ChatView-8wuIN1zH.js";
7
- import DataView from "./chunks/DataView-DhbOr4Wm.js";
8
- import { b as ContextMenu, C as Collection, a as Group, G as GroupList, c as GroupForms, g as UserDevice, j as UserDeviceLocationList, h as UserDeviceList, U as User, f as UserDataView, e as UserForms, d as UserList } from "./chunks/ContextMenu-DPxZgf69.js";
9
- import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer-BJT0-zQL.js";
10
- import { a as applyFileDropMixin, F as FormView } from "./chunks/FormView-DbPm-jDU.js";
1
+ import { P as Page } from "./chunks/Page-CFzSUQLz.js";
2
+ import { V as View, h as MOJOUtils } from "./chunks/WebApp-MzDCFzar.js";
3
+ import { B, b, a, c, e, f, W } from "./chunks/WebApp-MzDCFzar.js";
4
+ import Dialog$1 from "./chunks/Dialog-D9qIh5Jb.js";
5
+ import { M as MetricsChart, c as MetricsMiniChartWidget, P as PieChart } from "./chunks/MetricsMiniChartWidget-CoapKHw0.js";
6
+ import { b as TablePage, k as EmailDomainForms, j as EmailDomainList, E as EmailDomain, n as MailboxForms, m as MailboxList, l as Mailbox, r as EmailTemplate, d as TabView, t as EmailTemplateForms, s as EmailTemplateList, I as IncidentEvent, B as IncidentEventForms, A as IncidentEventList, w as FileManagerForms, v as FileManagerList, x as File, T as TableView, z as FileForms, y as FileList, av as GeoLocatedIP, aw as GeoLocatedIPList, ag as MemberList, af as LogList, ay as TicketList, G as IncidentList, _ as IncidentStats, R as IncidentHistoryList, Q as IncidentHistory, D as Incident, C as ChatView, H as IncidentForms, a3 as Job, a9 as JobEventList, a7 as JobLogList, a5 as JobForms, a4 as JobList, ac as JobRunnerList, aa as JobsEngineStats, ad as JobRunnerForms, ab as JobRunner, ae as Log, M as Member, ah as MemberForms, ai as MetricsPermission, ak as MetricsForms, aj as MetricsPermissionList, at as PushConfigForms, aq as PushConfigList, as as PushDeliveryList, am as PushDeviceList, au as PushTemplateForms, ao as PushTemplateList, U as RuleSet, a0 as MatchByOptions, $ as BundleByOptions, Y as RuleList, V as RuleSetList, i as S3BucketForms, h as S3BucketList, o as SentMessage, p as SentMessageList, aA as TicketNoteList, az as TicketNote, ax as Ticket, aB as TicketForms, aC as TicketCategories } from "./chunks/ChatView-D9hJ2u6c.js";
7
+ import DataView from "./chunks/DataView-BDUCD0q0.js";
8
+ import { b as ContextMenu, C as Collection, a as Group, G as GroupList, c as GroupForms, g as UserDevice, j as UserDeviceLocationList, h as UserDeviceList, U as User, f as UserDataView, e as UserForms, d as UserList } from "./chunks/ContextMenu-BlmrYFlZ.js";
9
+ import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer-BXcYgXKx.js";
10
+ import { a as applyFileDropMixin, F as FormView } from "./chunks/FormView-Bq74K6dj.js";
11
11
  class AdminHeaderView extends View {
12
12
  constructor(options = {}) {
13
13
  super({
@@ -1590,7 +1590,7 @@ class FileTablePage extends TablePage {
1590
1590
  */
1591
1591
  async onActionAdd(event, element) {
1592
1592
  event.preventDefault();
1593
- const Dialog2 = (await import("./chunks/Dialog-CQA_WzmY.js")).default;
1593
+ const Dialog2 = (await import("./chunks/Dialog-D9qIh5Jb.js")).default;
1594
1594
  const formData = await Dialog2.showForm({
1595
1595
  title: "Upload File",
1596
1596
  size: "md",
@@ -1686,6 +1686,185 @@ class FileTablePage extends TablePage {
1686
1686
  }
1687
1687
  }
1688
1688
  applyFileDropMixin(FileTablePage);
1689
+ class MapView extends View {
1690
+ constructor(options = {}) {
1691
+ super({
1692
+ className: "map-view",
1693
+ ...options
1694
+ });
1695
+ this.markers = options.markers || [];
1696
+ this.center = options.center || null;
1697
+ this.zoom = options.zoom || 13;
1698
+ this.height = options.height || 400;
1699
+ this.showZoomControl = options.showZoomControl !== false;
1700
+ this.tileLayer = options.tileLayer || "osm";
1701
+ this.map = null;
1702
+ this.leafletMarkers = [];
1703
+ this.template = `
1704
+ <div class="map-container">
1705
+ <div id="map-{{id}}" style="height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;"></div>
1706
+ </div>
1707
+ `;
1708
+ }
1709
+ async onAfterRender() {
1710
+ await this.loadLeaflet();
1711
+ await this.initializeMap();
1712
+ }
1713
+ async loadLeaflet() {
1714
+ if (window.L) return;
1715
+ const cssLoaded = new Promise((resolve) => {
1716
+ const link = document.createElement("link");
1717
+ link.rel = "stylesheet";
1718
+ link.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
1719
+ link.onload = resolve;
1720
+ link.onerror = resolve;
1721
+ document.head.appendChild(link);
1722
+ });
1723
+ const jsLoaded = new Promise((resolve, reject) => {
1724
+ const script = document.createElement("script");
1725
+ script.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
1726
+ script.onload = resolve;
1727
+ script.onerror = reject;
1728
+ document.head.appendChild(script);
1729
+ });
1730
+ await Promise.all([cssLoaded, jsLoaded]);
1731
+ }
1732
+ getTileLayerUrl() {
1733
+ const tileLayers = {
1734
+ // Standard street maps
1735
+ osm: {
1736
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
1737
+ attribution: "© OpenStreetMap contributors",
1738
+ maxZoom: 19
1739
+ },
1740
+ // Satellite imagery
1741
+ satellite: {
1742
+ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
1743
+ attribution: "© Esri",
1744
+ maxZoom: 19
1745
+ },
1746
+ // Terrain and topographic
1747
+ terrain: {
1748
+ url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
1749
+ attribution: "© OpenTopoMap contributors",
1750
+ maxZoom: 17
1751
+ },
1752
+ // Dark mode styles
1753
+ dark: {
1754
+ url: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
1755
+ attribution: "© OpenStreetMap contributors © CARTO",
1756
+ maxZoom: 20
1757
+ },
1758
+ // Light/minimal styles
1759
+ light: {
1760
+ url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
1761
+ attribution: "© OpenStreetMap contributors © CARTO",
1762
+ maxZoom: 20
1763
+ },
1764
+ // Watercolor artistic style
1765
+ watercolor: {
1766
+ url: "https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg",
1767
+ attribution: "© Stadia Maps © Stamen Design © OpenStreetMap contributors",
1768
+ maxZoom: 16
1769
+ },
1770
+ // Black and white
1771
+ bw: {
1772
+ url: "https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png",
1773
+ attribution: "© OpenStreetMap contributors © CARTO",
1774
+ maxZoom: 20
1775
+ },
1776
+ // Streets with labels
1777
+ streets: {
1778
+ url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
1779
+ attribution: "© OpenStreetMap contributors © CARTO",
1780
+ maxZoom: 20
1781
+ }
1782
+ };
1783
+ return tileLayers[this.tileLayer] || tileLayers.osm;
1784
+ }
1785
+ async initializeMap() {
1786
+ const mapElement = this.element.querySelector(`#map-${this.id}`);
1787
+ if (!mapElement || !window.L) return;
1788
+ let mapCenter = this.center;
1789
+ if (!mapCenter && this.markers.length > 0) {
1790
+ mapCenter = [this.markers[0].lat, this.markers[0].lng];
1791
+ }
1792
+ if (!mapCenter) {
1793
+ mapCenter = [0, 0];
1794
+ }
1795
+ this.map = window.L.map(mapElement, {
1796
+ center: mapCenter,
1797
+ zoom: this.zoom,
1798
+ zoomControl: this.showZoomControl
1799
+ });
1800
+ const tileConfig = this.getTileLayerUrl();
1801
+ window.L.tileLayer(tileConfig.url, {
1802
+ attribution: tileConfig.attribution,
1803
+ maxZoom: tileConfig.maxZoom
1804
+ }).addTo(this.map);
1805
+ this.addMarkers(this.markers);
1806
+ if (this.markers.length > 1) {
1807
+ this.fitBounds();
1808
+ }
1809
+ setTimeout(() => {
1810
+ if (this.map) {
1811
+ this.map.invalidateSize();
1812
+ }
1813
+ }, 300);
1814
+ }
1815
+ addMarkers(markers) {
1816
+ if (!this.map || !Array.isArray(markers)) return;
1817
+ markers.forEach((markerData) => {
1818
+ const { lat, lng, popup, icon } = markerData;
1819
+ if (!lat || !lng) return;
1820
+ const markerOptions = {};
1821
+ if (icon) {
1822
+ markerOptions.icon = window.L.icon(icon);
1823
+ }
1824
+ const marker = window.L.marker([lat, lng], markerOptions).addTo(this.map);
1825
+ if (popup) {
1826
+ marker.bindPopup(popup);
1827
+ }
1828
+ this.leafletMarkers.push(marker);
1829
+ });
1830
+ }
1831
+ fitBounds() {
1832
+ if (!this.map || this.leafletMarkers.length === 0) return;
1833
+ const group = new window.L.featureGroup(this.leafletMarkers);
1834
+ this.map.fitBounds(group.getBounds().pad(0.1));
1835
+ }
1836
+ updateMarkers(newMarkers) {
1837
+ this.clearMarkers();
1838
+ this.markers = newMarkers;
1839
+ this.addMarkers(newMarkers);
1840
+ if (newMarkers.length > 1) {
1841
+ this.fitBounds();
1842
+ } else if (newMarkers.length === 1) {
1843
+ this.map.setView([newMarkers[0].lat, newMarkers[0].lng], this.zoom);
1844
+ }
1845
+ }
1846
+ clearMarkers() {
1847
+ this.leafletMarkers.forEach((marker) => {
1848
+ this.map.removeLayer(marker);
1849
+ });
1850
+ this.leafletMarkers = [];
1851
+ }
1852
+ setView(lat, lng, zoom = null) {
1853
+ if (!this.map) return;
1854
+ this.map.setView([lat, lng], zoom || this.zoom);
1855
+ }
1856
+ setZoom(zoom) {
1857
+ if (!this.map) return;
1858
+ this.map.setZoom(zoom);
1859
+ }
1860
+ async onBeforeDestroy() {
1861
+ if (this.map) {
1862
+ this.map.remove();
1863
+ this.map = null;
1864
+ }
1865
+ await super.onBeforeDestroy();
1866
+ }
1867
+ }
1689
1868
  class GeoIPView extends View {
1690
1869
  constructor(options = {}) {
1691
1870
  super({
@@ -1720,36 +1899,99 @@ class GeoIPView extends View {
1720
1899
  </div>
1721
1900
  </div>
1722
1901
 
1723
- <!-- Body -->
1724
- <div data-container="geoip-data-view"></div>
1902
+ <!-- Tabs -->
1903
+ <div data-container="geoip-tabs"></div>
1725
1904
  </div>
1726
1905
  `;
1727
1906
  }
1728
1907
  async onInit() {
1729
- this.dataView = new DataView({
1730
- containerId: "geoip-data-view",
1908
+ this.detailsView = new DataView({
1731
1909
  model: this.model,
1732
- className: "p-3 border rounded",
1910
+ className: "p-3",
1733
1911
  showEmptyValues: true,
1734
1912
  emptyValueText: "—",
1735
1913
  columns: 2,
1736
1914
  fields: [
1737
- { name: "id", label: "ID" },
1738
- { name: "subnet", label: "Subnet" },
1739
- { name: "country_code", label: "Country Code" },
1740
- { name: "region", label: "Region" },
1741
- { name: "city", label: "City" },
1742
- { name: "postal_code", label: "Postal Code" },
1743
- { name: "latitude", label: "Latitude" },
1744
- { name: "longitude", label: "Longitude" },
1745
- { name: "timezone", label: "Timezone" },
1746
- { name: "created", label: "Created", format: "datetime" },
1747
- { name: "modified", label: "Last Modified", format: "datetime" },
1748
- { name: "expires_at", label: "Expires", format: "datetime" }
1915
+ { name: "ip_address", label: "IP Address", cols: 4 },
1916
+ { name: "subnet", label: "Subnet", cols: 4 },
1917
+ { name: "country_name", label: "Country", cols: 4 },
1918
+ { name: "country_code", label: "Country Code", cols: 4 },
1919
+ { name: "region", label: "Region", cols: 4 },
1920
+ { name: "city", label: "City", cols: 4 },
1921
+ { name: "postal_code", label: "Postal Code", cols: 4 },
1922
+ { name: "timezone", label: "Timezone", cols: 4 },
1923
+ { name: "latitude", label: "Latitude", cols: 4 },
1924
+ { name: "longitude", label: "Longitude", cols: 4 }
1749
1925
  ]
1750
1926
  });
1751
- this.addChild(this.dataView);
1927
+ this.securityView = new DataView({
1928
+ model: this.model,
1929
+ className: "p-3",
1930
+ showEmptyValues: true,
1931
+ emptyValueText: "—",
1932
+ columns: 2,
1933
+ fields: [
1934
+ { name: "threat_level", label: "Threat Level", cols: 4 },
1935
+ { name: "is_tor", label: "TOR Exit Node", cols: 4 },
1936
+ { name: "is_vpn", label: "VPN", formatter: "yesnoicon", cols: 4 },
1937
+ { name: "is_proxy", label: "Proxy", formatter: "yesnoicon", cols: 4 },
1938
+ { name: "is_cloud", label: "Cloud Provider", formatter: "yesnoicon", cols: 4 },
1939
+ { name: "is_datacenter", label: "Datacenter", formatter: "yesnoicon", cols: 4 },
1940
+ { name: "asn", label: "ASN", cols: 4 },
1941
+ { name: "asn_org", label: "ASN Organization", cols: 4 },
1942
+ { name: "isp", label: "ISP", cols: 4 },
1943
+ { name: "connection_type", label: "Connection Type", cols: 6 }
1944
+ ]
1945
+ });
1946
+ this.metadataView = new DataView({
1947
+ model: this.model,
1948
+ className: "p-3",
1949
+ showEmptyValues: true,
1950
+ emptyValueText: "—",
1951
+ columns: 2,
1952
+ fields: [
1953
+ { name: "id", label: "Record ID", cols: 6 },
1954
+ { name: "provider", label: "Data Provider", formatter: "capitalize", cols: 6 },
1955
+ { name: "created", label: "Created", formatter: "datetime", cols: 6 },
1956
+ { name: "modified", label: "Last Modified", formatter: "datetime", cols: 6 },
1957
+ { name: "expires_at", label: "Expires", formatter: "datetime", cols: 12 }
1958
+ ]
1959
+ });
1960
+ const tabs = {
1961
+ "Location": this.detailsView,
1962
+ "Security": this.securityView,
1963
+ "Metadata": this.metadataView
1964
+ };
1965
+ if (this.hasCoordinates) {
1966
+ const lat = this.model.get("latitude");
1967
+ const lng = this.model.get("longitude");
1968
+ const city = this.model.get("city") || "Unknown";
1969
+ const region = this.model.get("region") || "";
1970
+ const country = this.model.get("country_name") || "";
1971
+ const locationStr = [city, region, country].filter(Boolean).join(", ");
1972
+ this.mapView = new MapView({
1973
+ markers: [{
1974
+ lat,
1975
+ lng,
1976
+ popup: `<strong>${this.model.get("ip_address")}</strong><br>${locationStr}`
1977
+ }],
1978
+ tileLayer: "light",
1979
+ zoom: 4,
1980
+ height: 450
1981
+ });
1982
+ tabs["Map"] = this.mapView;
1983
+ }
1984
+ this.tabView = new TabView({
1985
+ containerId: "geoip-tabs",
1986
+ tabs,
1987
+ activeTab: this.hasCoordinates ? "Map" : "Location"
1988
+ });
1989
+ this.addChild(this.tabView);
1752
1990
  const menuItems = [
1991
+ { label: "Edit Location", action: "edit-location", icon: "bi-geo-alt" },
1992
+ { label: "Edit Security", action: "edit-security", icon: "bi-shield-lock" },
1993
+ { label: "Edit Network", action: "edit-network", icon: "bi-diagram-3" },
1994
+ { type: "divider" },
1753
1995
  { label: "Refresh Geolocation", action: "refresh-geoip", icon: "bi-arrow-clockwise" }
1754
1996
  ];
1755
1997
  if (this.hasCoordinates) {
@@ -1774,8 +2016,41 @@ class GeoIPView extends View {
1774
2016
  });
1775
2017
  this.addChild(geoIPMenu);
1776
2018
  }
2019
+ async onActionEditLocation() {
2020
+ const resp = await Dialog$1.showModelForm({
2021
+ title: `Edit Location - ${this.model.get("ip_address")}`,
2022
+ model: this.model,
2023
+ formConfig: GeoLocatedIP.EDIT_LOCATION_FORM
2024
+ });
2025
+ if (resp) {
2026
+ await this.render();
2027
+ this.getApp()?.toast?.success("Location updated successfully");
2028
+ }
2029
+ }
2030
+ async onActionEditSecurity() {
2031
+ const resp = await Dialog$1.showModelForm({
2032
+ title: `Edit Security - ${this.model.get("ip_address")}`,
2033
+ model: this.model,
2034
+ formConfig: GeoLocatedIP.EDIT_SECURITY_FORM
2035
+ });
2036
+ if (resp) {
2037
+ await this.render();
2038
+ this.getApp()?.toast?.success("Security settings updated successfully");
2039
+ }
2040
+ }
2041
+ async onActionEditNetwork() {
2042
+ const resp = await Dialog$1.showModelForm({
2043
+ title: `Edit Network - ${this.model.get("ip_address")}`,
2044
+ model: this.model,
2045
+ formConfig: GeoLocatedIP.EDIT_NETWORK_FORM
2046
+ });
2047
+ if (resp) {
2048
+ await this.render();
2049
+ this.getApp()?.toast?.success("Network information updated successfully");
2050
+ }
2051
+ }
1777
2052
  async onActionRefreshGeoip() {
1778
- console.log("Refreshing GeoIP for:", this.model.get("ip_address"));
2053
+ await this.model.save({ refresh: true });
1779
2054
  this.getApp()?.toast?.info("Refresh request sent for " + this.model.get("ip_address"));
1780
2055
  }
1781
2056
  async onActionViewOnMap() {
@@ -1826,20 +2101,19 @@ class GeoLocatedIPTablePage extends TablePage {
1826
2101
  pageName: "GeoIP Cache",
1827
2102
  router: "admin/system/geoip",
1828
2103
  Collection: GeoLocatedIPList,
1829
- itemViewClass: GeoIPView,
2104
+ itemView: GeoIPView,
1830
2105
  viewDialogOptions: {
1831
2106
  header: false,
1832
- size: "lg"
2107
+ size: "xl"
1833
2108
  },
1834
2109
  // Column definitions
1835
2110
  columns: [
1836
- { key: "id", label: "ID", width: "70px", sortable: true, class: "text-muted" },
1837
2111
  { key: "ip_address", label: "IP Address", sortable: true },
1838
2112
  { key: "city", label: "City", sortable: true, formatter: "default('—')" },
1839
2113
  { key: "region", label: "Region", sortable: true, formatter: "default('—')" },
1840
2114
  { key: "country_name", label: "Country", sortable: true, formatter: "default('—')" },
1841
- { key: "provider", label: "Provider", sortable: true },
1842
- { key: "is_expired", label: "Expired", formatter: "boolean|badge" }
2115
+ { key: "isp", label: "ISP", sortable: true, formatter: "default('—')" },
2116
+ { key: "threat_level", label: "Threat", formatter: "default('—')" }
1843
2117
  ],
1844
2118
  // Table features
1845
2119
  selectable: true,
@@ -1847,6 +2121,9 @@ class GeoLocatedIPTablePage extends TablePage {
1847
2121
  sortable: true,
1848
2122
  filterable: true,
1849
2123
  paginated: true,
2124
+ // Actions
2125
+ // actions: ['view', 'edit', 'delete'],
2126
+ clickAction: "view",
1850
2127
  // Toolbar
1851
2128
  showRefresh: true,
1852
2129
  showAdd: false,