feat(ui): Add DevTools button for mobile debugging

Add Eruda DevTools integration accessible from hamburger menu on mobile devices.
- Add DevTools menu item with mobileOnly flag
- Load Eruda dynamically from CDN when clicked
- Filter menu items based on device type

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-05 14:24:19 +00:00
parent bb72b690ab
commit b2fe26da3a
2 changed files with 52 additions and 4 deletions

View File

@@ -21,7 +21,8 @@ export const menuSections = [
{ to: '/reports/telegram', icon: 'pi pi-telegram', label: 'Telegram Bot' },
{ to: '/reports/cache-stats', icon: 'pi pi-chart-bar', label: 'Statistici Cache' },
{ to: '/data-entry/ocr-metrics', icon: 'pi pi-eye', label: 'Statistici OCR' },
{ to: '/reports/server-logs', icon: 'pi pi-server', label: 'Server Logs' }
{ to: '/reports/server-logs', icon: 'pi pi-server', label: 'Server Logs' },
{ action: 'devtools', icon: 'pi pi-cog', label: 'DevTools', mobileOnly: true }
]
}
]

View File

@@ -18,11 +18,13 @@
<h3 class="menu-title">{{ section.title }}</h3>
<ul class="menu-list">
<li
v-for="item in section.items"
:key="item.to"
v-for="item in getFilteredItems(section.items)"
:key="item.to || item.action"
class="menu-item"
>
<!-- Router link for navigation -->
<router-link
v-if="item.to"
:to="item.to"
class="menu-link"
:class="{ active: isRouteActive(item.to) }"
@@ -32,6 +34,16 @@
<span>{{ item.label }}</span>
<span v-if="item.badge" class="menu-badge">{{ item.badge }}</span>
</router-link>
<!-- Action link for DevTools etc -->
<a
v-else-if="item.action"
href="#"
class="menu-link"
@click.prevent="handleAction(item.action)"
>
<i :class="['menu-icon', item.icon]"></i>
<span>{{ item.label }}</span>
</a>
</li>
</ul>
</div>
@@ -58,6 +70,7 @@
<script>
import { useRoute } from "vue-router";
import { ref, onMounted } from "vue";
export default {
name: "SlideMenu",
@@ -68,7 +81,7 @@ export default {
default: false,
},
// Menu items configuration
// Format: [{ title: 'Section', items: [{ to: '/path', icon: 'pi pi-icon', label: 'Label', badge: null }] }]
// Format: [{ title: 'Section', items: [{ to: '/path', icon: 'pi pi-icon', label: 'Label', badge: null, action: null, mobileOnly: false }] }]
menuItems: {
type: Array,
default: () => [],
@@ -83,6 +96,19 @@ export default {
setup(props, { emit }) {
const route = useRoute();
// Mobile detection
const isMobile = ref(false);
onMounted(() => {
isMobile.value = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
});
// Filter items based on mobileOnly flag
const getFilteredItems = (items) => {
if (!items) return [];
return items.filter(item => !item.mobileOnly || isMobile.value);
};
const isRouteActive = (path) => {
return route.path === path;
};
@@ -92,9 +118,30 @@ export default {
emit("close");
};
const handleAction = (action) => {
if (action === 'devtools') {
loadEruda();
}
emit('close');
};
const loadEruda = () => {
if (window.eruda) {
window.eruda.show();
return;
}
const script = document.createElement('script');
script.src = '//cdn.jsdelivr.net/npm/eruda';
document.body.appendChild(script);
script.onload = () => window.eruda.init();
};
return {
isMobile,
getFilteredItems,
isRouteActive,
handleLogout,
handleAction,
};
},
};