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/telegram', icon: 'pi pi-telegram', label: 'Telegram Bot' },
{ to: '/reports/cache-stats', icon: 'pi pi-chart-bar', label: 'Statistici Cache' }, { 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: '/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> <h3 class="menu-title">{{ section.title }}</h3>
<ul class="menu-list"> <ul class="menu-list">
<li <li
v-for="item in section.items" v-for="item in getFilteredItems(section.items)"
:key="item.to" :key="item.to || item.action"
class="menu-item" class="menu-item"
> >
<!-- Router link for navigation -->
<router-link <router-link
v-if="item.to"
:to="item.to" :to="item.to"
class="menu-link" class="menu-link"
:class="{ active: isRouteActive(item.to) }" :class="{ active: isRouteActive(item.to) }"
@@ -32,6 +34,16 @@
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
<span v-if="item.badge" class="menu-badge">{{ item.badge }}</span> <span v-if="item.badge" class="menu-badge">{{ item.badge }}</span>
</router-link> </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> </li>
</ul> </ul>
</div> </div>
@@ -58,6 +70,7 @@
<script> <script>
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { ref, onMounted } from "vue";
export default { export default {
name: "SlideMenu", name: "SlideMenu",
@@ -68,7 +81,7 @@ export default {
default: false, default: false,
}, },
// Menu items configuration // 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: { menuItems: {
type: Array, type: Array,
default: () => [], default: () => [],
@@ -83,6 +96,19 @@ export default {
setup(props, { emit }) { setup(props, { emit }) {
const route = useRoute(); 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) => { const isRouteActive = (path) => {
return route.path === path; return route.path === path;
}; };
@@ -92,9 +118,30 @@ export default {
emit("close"); 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 { return {
isMobile,
getFilteredItems,
isRouteActive, isRouteActive,
handleLogout, handleLogout,
handleAction,
}; };
}, },
}; };