diff --git a/docs/DEVICE_CHANGE_LOG.md b/docs/DEVICE_CHANGE_LOG.md
new file mode 100644
index 00000000..c2f3b25f
--- /dev/null
+++ b/docs/DEVICE_CHANGE_LOG.md
@@ -0,0 +1,73 @@
+# Change log
+
+The **_Change log_** provides a historical record of changes made to your devices. It allows you to see **what changed, when it changed, and what caused the change**, making it easier to troubleshoot unexpected updates, audit configuration changes, and understand how device information evolves over time.
+
+
+
+## What is recorded?
+
+Whenever a tracked device property changes, NetAlertX records:
+
+* **Time** the change occurred
+* **Source** that made the change
+* **Device** that was updated
+* **Field** that changed
+* **Previous value**
+* **New value**
+
+Related changes that occur at the same time are grouped together into a single event, making it easy to see all updates from one scan or user action.
+
+## Understanding the Source column
+
+The **Source** column identifies what caused the change.
+
+Depending on the field, the source may be:
+
+* A discovery plugin such as **ARPSCAN**, **NSLOOKUP**, **UNIFIAPI**, or another plugin that supplied the information.
+* **user:api** for changes made manually through the NetAlertX interface or API.
+* **system** for values calculated or maintained internally by NetAlertX.
+
+This helps distinguish between information discovered automatically and changes made by users.
+
+## Filtering the history
+
+The _Change log_ includes filters to help locate specific events.
+
+You can filter by:
+
+* **Source** to view changes made by a specific plugin or user.
+* **Changed Field** to display changes to a particular device property, such as IP address or hostname.
+
+You can also use the search box to quickly find matching devices by GUID, values, or fields.
+
+## Viewing grouped changes
+
+A single action often updates multiple properties on a device.
+
+Instead of showing each field as a separate record, the _Change log_ groups related updates together. For example, a network scan might update both a device's IP address and hostname at the same time. These changes appear together in one entry with each modified field listed underneath.
+
+## Retention
+
+The amount of history retained is controlled by the **Device History Days (`DEV_HIST_DAYS`)** setting.
+
+Older history entries are automatically removed once they exceed the configured retention period. this is maintained by the `DBCLNP` plugin.
+
+Setting this value to **0** disables _Change log_ recording entirely.
+
+## Choosing what to track
+
+Not every device property needs to be recorded.
+
+The **Tracked Device History Fields (`DEV_HIST_TRACKED`)** setting lets you choose exactly which device fields should be monitored. Tracking only the fields that are important for your environment can significantly reduce database growth while keeping the most useful audit information.
+
+## Performance considerations
+
+Recording history introduces additional database writes whenever tracked fields change. For most installations the overhead is minimal, but on larger networks it can generate a substantial amount of historical data.
+
+If you want to reduce storage usage or improve performance, consider:
+
+* Tracking only the device fields that matter to you.
+* Reducing the history retention period.
+* Disabling the _Change log_ entirely by setting **`DEV_HIST_DAYS`** to **0**.
+
+These settings allow you to balance historical visibility with database size and performance. For more tips on how to optimize system resource use check the [performance guide](./PERFORMANCE.md).
diff --git a/docs/DEVICE_PRESENCE.md b/docs/DEVICE_PRESENCE.md
new file mode 100644
index 00000000..89133678
--- /dev/null
+++ b/docs/DEVICE_PRESENCE.md
@@ -0,0 +1,45 @@
+## Presence View
+
+
+
+The **Presence View** provides a visual timeline of when each device was detected on your network, making it easy to understand activity over time instead of relying only on the current status.
+
+Use this view to answer questions such as:
+
+* When was this device first discovered?
+* When was it last seen online?
+* How often does this device connect to the network?
+* Has the device been continuously online or only appearing intermittently?
+* When did the device disappear or return?
+
+The timeline makes it easy to identify recurring activity patterns, unexpected outages, or devices that only appear at certain times of the day or week.
+
+### Timeline Views
+
+The timeline can be displayed at different time scales depending on the level of detail you need:
+
+* **Week** – View recent activity with the highest level of detail.
+* **Month** – Review activity across the past month.
+* **Year** – Get a long-term overview of device presence and seasonal patterns.
+
+Switch between these views using the **Week**, **Month**, and **Year** buttons above the timeline.
+
+### Timeline Legend
+
+The timeline uses color coding to indicate device presence:
+
+*
-
diff --git a/front/changeLogCore.php b/front/changeLogCore.php
index 0d320f69..0b01c473 100644
--- a/front/changeLogCore.php
+++ b/front/changeLogCore.php
@@ -1,311 +1,413 @@
-
-
-
-
-
-
-
+
-
-
-
-
-
- | = lang('device_history_col_time'); ?> |
- = lang('device_history_col_source'); ?> |
- = lang('gen_device'); ?> |
- = lang('device_history_table_title_changes'); ?> |
-
-
-
- |
-
- |
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | = lang('device_history_col_time'); ?> |
+ = lang('device_history_col_source'); ?> |
+ = lang('gen_device'); ?> |
+ = lang('device_history_table_title_changes'); ?> |
+
+
+
+
+
+ |
+
+ |
+
+
+
+
+
-
-
+
+
\ No newline at end of file
diff --git a/front/css/app.css b/front/css/app.css
index 5824f4b5..751be365 100755
--- a/front/css/app.css
+++ b/front/css/app.css
@@ -1001,6 +1001,19 @@ height: 50px;
opacity: .75;
}
+#presencePage .helpIcon{
+ top: 2px;
+ display: initial;
+ font-size: x-small;
+}
+
+.calendarHelpIcon{
+ display: block;
+ position: absolute;
+ font-size: x-small;
+ top: -5px;
+}
+
/* --------------------------------------------------------- */
/* report */
/* --------------------------------------------------------- */
@@ -2636,7 +2649,18 @@ table.dataTable tbody > tr.selected
background-color: rgba(140, 140, 140, 0.05);
}
-/* ===== Loading Skeleton ===== */
+/* -----------------------------------------------------------------------------
+ Change log
+----------------------------------------------------------------------------- */
+
+.change-log .helpIcon{
+ left: 15px;
+ font-size: x-small;
+}
+
+/* -----------------------------------------------------------------------------
+ Loading Skeletons
+----------------------------------------------------------------------------- */
/* Custom properties (overridden in dark-patch.css for Dark/System themes) */
:root {
diff --git a/front/deviceDetailsEvents.php b/front/deviceDetailsEvents.php
index 34980b80..27247c9d 100755
--- a/front/deviceDetailsEvents.php
+++ b/front/deviceDetailsEvents.php
@@ -196,12 +196,7 @@ function initDeviceEventsPage()
// -----------------------------------------------------------------------------
// Recurring function to monitor the URL and reinitialize if needed
-function deviceEventsPageUpdater() {
- initDeviceEventsPage();
-
- // Run updater again after delay
- setTimeout(deviceEventsPageUpdater, 200);
-}
+initLazyTab('#panEvents', initDeviceEventsPage);
deviceEventsPageUpdater();
diff --git a/front/deviceDetailsPresence.php b/front/deviceDetailsPresence.php
index 31d4741f..36d5915c 100755
--- a/front/deviceDetailsPresence.php
+++ b/front/deviceDetailsPresence.php
@@ -21,6 +21,11 @@
+
+
+
+
+
diff --git a/front/js/common.js b/front/js/common.js
index 006bad8f..765d3438 100755
--- a/front/js/common.js
+++ b/front/js/common.js
@@ -1175,6 +1175,82 @@ function isSQLQuery(query) {
return sqlRegex.test(query);
}
+/**
+ * Lazy tab initializer
+ * - Ensures a tab is only initialized once
+ * - Waits until tab is visible in DOM
+ * - Uses polling (compatible with your existing architecture)
+ */
+
+function waitForAppReady(cb) {
+
+ if (window.__appReady) {
+ cb();
+ return;
+ }
+
+ const t = setInterval(() => {
+
+ if (window.__appReady) {
+ clearInterval(t);
+ cb();
+ }
+
+ }, 50);
+}
+
+function initLazyTab(selector, initFn, intervalMs = 200) {
+
+ const stateKey = `__lazy_init_${selector}`;
+ let initialized = false;
+
+ function runner() {
+
+ // prevent duplicate execution
+ if (initialized || window[stateKey]) return;
+
+ const $el = $(selector);
+
+ // ---------------------------------------------------------
+ // CASE 1: element does not exist → standalone page variant
+ // run immediately (no DOM dependency)
+ // ---------------------------------------------------------
+ if (!$el.length) {
+ initialized = true;
+ window[stateKey] = true;
+
+ try {
+ initFn();
+ } catch (e) {
+ console.error("Lazy init (no-element) failed:", selector, e);
+ }
+ return;
+ }
+
+ // ---------------------------------------------------------
+ // CASE 2: element exists AND is visible → run immediately
+ // ---------------------------------------------------------
+ if ($el.is(':visible')) {
+ initialized = true;
+ window[stateKey] = true;
+
+ try {
+ initFn();
+ } catch (e) {
+ console.error("Lazy init (visible) failed:", selector, e);
+ }
+ return;
+ }
+
+ // ---------------------------------------------------------
+ // CASE 3: element exists but hidden → wait for visibility
+ // (tab mode)
+ // ---------------------------------------------------------
+ setTimeout(runner, intervalMs);
+ }
+
+ runner();
+}
// -------------------------------------------------------------------
// Get corresponding plugin setting object
diff --git a/front/js/ui_components.js b/front/js/ui_components.js
index 917ff860..8d3bdc1c 100755
--- a/front/js/ui_components.js
+++ b/front/js/ui_components.js
@@ -1164,5 +1164,5 @@ function getLengthMenu(newEntry) {
return [values, labels];
}
-
+window.__appReady = true;
console.log("init ui_components.js")
\ No newline at end of file
diff --git a/front/php/templates/language/en_us.json b/front/php/templates/language/en_us.json
index 8d304290..bc4c54fc 100755
--- a/front/php/templates/language/en_us.json
+++ b/front/php/templates/language/en_us.json
@@ -777,7 +777,7 @@
"add_icon_event_tooltip": "Add new icon",
"add_option_event_tooltip": "Add new value",
"copy_icons_event_tooltip": "Overwrite icons of all devices with the same device type",
- "device_history_col_changes": "Select which columns are tracked via the
DEV_HIST_TRACKED setting.",
+ "device_history_col_changes": "Select which columns are tracked via the
DEV_HIST_TRACKED setting.",
"device_history_col_dropdown": "Columns",
"device_history_col_source": "Modified By",
"device_history_col_time": "Timestamp",
diff --git a/front/presence.php b/front/presence.php
index 2dc2da99..4a074e12 100755
--- a/front/presence.php
+++ b/front/presence.php
@@ -22,6 +22,7 @@
+
@@ -128,9 +129,11 @@
+
+
@@ -456,7 +464,7 @@ function getDevicesPresence (status) {
// Set title and color
$('#tableDevicesTitle')[0].className = 'box-title text-'+ color;
$('#tableDevicesBox')[0].className = 'box box-'+ color;
- $('#tableDevicesTitle').html (tableTitle);
+ $('#tableDevicesTitle').html (`${tableTitle} ${getString("Device_TableHead_PresentLastScan")}`);
const apiToken = getSetting("API_TOKEN");
diff --git a/mkdocs.yml b/mkdocs.yml
index 2d1ebc1c..2a16e508 100755
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -74,7 +74,10 @@ nav:
- Device display settings: DEVICE_DISPLAY_SETTINGS.md
- Device views filtering: DEVICE_FILTERS.md
- Session info: SESSION_INFO.md
- - Field Lock/Unlock: DEVICE_FIELD_LOCK.md
+ - Field Lock/Unlock: DEVICE_FIELD_LOCK.md
+ - Device auditing:
+ - Change log: DEVICE_CHANGE_LOG.md
+ - Presence: DEVICE_PRESENCE.md
- Device source fields: DEVICE_SOURCE_FIELDS.md
- Icons and network topology:
- Icons: ICONS.md