feat(unified-mobile-material-design): Complete US-110 - ServerLogsView Mobile Material Design
Implemented by Ralph autonomous loop. Iteration: 14 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -229,8 +229,8 @@
|
||||
"MobileBottomNav adăugat",
|
||||
"npm run build passes"
|
||||
],
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
"passes": true,
|
||||
"notes": "Completed in iteration 14"
|
||||
},
|
||||
{
|
||||
"id": "US-111",
|
||||
|
||||
@@ -84,3 +84,9 @@ Mon Jan 12 09:44:54 AM UTC 2026
|
||||
[2026-01-12 10:13:35] Working on story: US-109
|
||||
[2026-01-12 10:13:35] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_13_US-109.log)
|
||||
[2026-01-12 10:16:15] SUCCESS: Story US-109 passed!
|
||||
[2026-01-12 10:16:15] Changes committed
|
||||
[2026-01-12 10:16:15] Progress: 12/20 stories completed
|
||||
[2026-01-12 10:16:17] === Iteration 14/100 ===
|
||||
[2026-01-12 10:16:17] Working on story: US-110
|
||||
[2026-01-12 10:16:17] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_14_US-110.log)
|
||||
[2026-01-12 10:19:03] SUCCESS: Story US-110 passed!
|
||||
|
||||
@@ -1,6 +1,44 @@
|
||||
<template>
|
||||
<div class="server-logs-view">
|
||||
<div class="stats-header">
|
||||
<div class="server-logs-view" :class="{ 'mobile-layout': isMobile }">
|
||||
<!-- US-110: Mobile Material Design Top Bar -->
|
||||
<MobileTopBar
|
||||
v-if="isMobile"
|
||||
title="Loguri Server"
|
||||
:show-menu="true"
|
||||
:actions="mobileTopBarActions"
|
||||
@menu-click="toggleMobileMenu"
|
||||
@action-click="handleTopBarAction"
|
||||
/>
|
||||
|
||||
<!-- US-110: Mobile Hamburger Menu -->
|
||||
<Sidebar v-if="isMobile" v-model:visible="mobileMenuVisible" position="left" class="mobile-sidebar">
|
||||
<template #header>
|
||||
<div class="sidebar-header">
|
||||
<span class="sidebar-title">ROA2WEB</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="sidebar-menu">
|
||||
<router-link to="/data-entry" class="sidebar-item">
|
||||
<i class="pi pi-receipt"></i>
|
||||
<span>Bonuri</span>
|
||||
</router-link>
|
||||
<router-link to="/reports/server-logs" class="sidebar-item active">
|
||||
<i class="pi pi-file-edit"></i>
|
||||
<span>Loguri Server</span>
|
||||
</router-link>
|
||||
<router-link to="/reports/dashboard" class="sidebar-item">
|
||||
<i class="pi pi-chart-bar"></i>
|
||||
<span>Rapoarte</span>
|
||||
</router-link>
|
||||
<router-link to="/data-entry/ocr-metrics" class="sidebar-item">
|
||||
<i class="pi pi-cog"></i>
|
||||
<span>Setări</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</Sidebar>
|
||||
|
||||
<!-- Desktop Header -->
|
||||
<div class="stats-header" v-if="!isMobile">
|
||||
<h1>
|
||||
<i class="pi pi-file-edit"></i>
|
||||
Server Logs
|
||||
@@ -42,6 +80,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- US-110: Mobile Filter/Control Bar -->
|
||||
<div v-if="isMobile" class="mobile-controls-bar">
|
||||
<Dropdown
|
||||
v-model="selectedFile"
|
||||
:options="logFiles"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Log file"
|
||||
class="mobile-log-select"
|
||||
/>
|
||||
<Dropdown
|
||||
v-model="linesCount"
|
||||
:options="linesOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
class="mobile-lines-select"
|
||||
/>
|
||||
<Button
|
||||
:icon="autoRefresh ? 'pi pi-pause' : 'pi pi-play'"
|
||||
:severity="autoRefresh ? 'warning' : 'secondary'"
|
||||
@click="toggleAutoRefresh"
|
||||
class="mobile-auto-btn"
|
||||
v-tooltip.bottom="autoRefresh ? 'Stop Auto' : 'Start Auto'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Message v-if="error" severity="error" :closable="true" @close="error = null">
|
||||
{{ error }}
|
||||
</Message>
|
||||
@@ -78,11 +142,17 @@
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- US-110: Mobile Bottom Navigation -->
|
||||
<MobileBottomNav
|
||||
v-if="isMobile"
|
||||
:items="mobileBottomNavItems"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||
import Button from 'primevue/button'
|
||||
import Card from 'primevue/card'
|
||||
import Dropdown from 'primevue/dropdown'
|
||||
@@ -90,8 +160,13 @@ import InputText from 'primevue/inputtext'
|
||||
import Message from 'primevue/message'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import Tag from 'primevue/tag'
|
||||
import Sidebar from 'primevue/sidebar'
|
||||
import axios from 'axios'
|
||||
|
||||
// US-110: Mobile Material Design components
|
||||
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
||||
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
||||
|
||||
// System API - endpoint separat de reports
|
||||
const systemApi = axios.create({
|
||||
baseURL: import.meta.env.BASE_URL + 'api/system',
|
||||
@@ -125,6 +200,67 @@ const debugInfo = ref({
|
||||
|
||||
let refreshTimer = null
|
||||
|
||||
// US-110: Mobile state
|
||||
const isMobile = ref(window.innerWidth < 768)
|
||||
const mobileMenuVisible = ref(false)
|
||||
|
||||
// US-110: Toggle mobile hamburger menu
|
||||
const toggleMobileMenu = () => {
|
||||
mobileMenuVisible.value = !mobileMenuVisible.value
|
||||
}
|
||||
|
||||
// US-110: Mobile TopBar actions (refresh, export)
|
||||
const mobileTopBarActions = computed(() => [
|
||||
{
|
||||
icon: 'pi pi-refresh',
|
||||
label: 'Actualizează',
|
||||
tooltip: 'Actualizează'
|
||||
},
|
||||
{
|
||||
icon: 'pi pi-download',
|
||||
label: 'Export',
|
||||
tooltip: 'Export Loguri'
|
||||
}
|
||||
])
|
||||
|
||||
// US-110: Handle top bar action clicks
|
||||
const handleTopBarAction = (action) => {
|
||||
if (action.icon === 'pi pi-refresh') {
|
||||
loadLogs()
|
||||
} else if (action.icon === 'pi pi-download') {
|
||||
exportLogs()
|
||||
}
|
||||
}
|
||||
|
||||
// US-110: Bottom nav items for MobileBottomNav component
|
||||
const mobileBottomNavItems = computed(() => [
|
||||
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri' },
|
||||
{ to: '/reports/server-logs', icon: 'pi pi-file-edit', label: 'Loguri', active: true },
|
||||
{ to: '/reports/dashboard', icon: 'pi pi-chart-bar', label: 'Rapoarte' },
|
||||
{ to: '/data-entry/ocr-metrics', icon: 'pi pi-cog', label: 'Setări' }
|
||||
])
|
||||
|
||||
// US-110: Handle window resize
|
||||
const handleResize = () => {
|
||||
isMobile.value = window.innerWidth < 768
|
||||
}
|
||||
|
||||
// US-110: Export logs to text file
|
||||
const exportLogs = () => {
|
||||
if (logs.value.length === 0) return
|
||||
|
||||
const content = logs.value.join('\n')
|
||||
const blob = new Blob([content], { type: 'text/plain' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `server-logs-${selectedFile.value}-${new Date().toISOString().slice(0, 10)}.txt`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
// Options
|
||||
const logFiles = [
|
||||
{ label: 'Errors (stderr)', value: 'backend-stderr' },
|
||||
@@ -235,10 +371,14 @@ watch(linesCount, () => {
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
// US-110: Add resize listener for mobile detection
|
||||
window.addEventListener('resize', handleResize)
|
||||
loadLogs()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// US-110: Remove resize listener
|
||||
window.removeEventListener('resize', handleResize)
|
||||
stopAutoRefresh()
|
||||
})
|
||||
</script>
|
||||
@@ -356,7 +496,117 @@ onUnmounted(() => {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
/* ================================================
|
||||
US-110: Mobile Layout Styles
|
||||
================================================ */
|
||||
|
||||
.mobile-layout {
|
||||
padding: 0;
|
||||
padding-top: 56px; /* MobileTopBar height */
|
||||
padding-bottom: 56px; /* MobileBottomNav height */
|
||||
}
|
||||
|
||||
.mobile-layout .logs-card {
|
||||
margin: var(--space-sm);
|
||||
margin-top: var(--space-xs);
|
||||
}
|
||||
|
||||
.mobile-layout .logs-container {
|
||||
max-height: calc(100vh - 280px);
|
||||
}
|
||||
|
||||
/* Mobile controls bar */
|
||||
.mobile-controls-bar {
|
||||
display: flex;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-sm);
|
||||
background: var(--surface-card);
|
||||
border-bottom: 1px solid var(--surface-border);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mobile-log-select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mobile-lines-select {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.mobile-auto-btn {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Mobile sidebar styles */
|
||||
.mobile-sidebar .sidebar-header {
|
||||
padding: var(--space-md);
|
||||
border-bottom: 1px solid var(--surface-border);
|
||||
}
|
||||
|
||||
.mobile-sidebar .sidebar-title {
|
||||
font-size: var(--text-lg);
|
||||
font-weight: var(--font-bold);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.mobile-sidebar .sidebar-menu {
|
||||
padding: var(--space-sm) 0;
|
||||
}
|
||||
|
||||
.mobile-sidebar .sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.mobile-sidebar .sidebar-item:hover,
|
||||
.mobile-sidebar .sidebar-item:active {
|
||||
background: var(--surface-hover);
|
||||
}
|
||||
|
||||
.mobile-sidebar .sidebar-item.active {
|
||||
background: var(--blue-50);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.mobile-sidebar .sidebar-item i {
|
||||
font-size: var(--text-xl);
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
Dark Mode Support for Mobile
|
||||
================================================ */
|
||||
|
||||
[data-theme="dark"] .mobile-controls-bar {
|
||||
background: var(--surface-card);
|
||||
border-bottom-color: var(--surface-border);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mobile-sidebar .sidebar-item.active {
|
||||
background: var(--blue-900);
|
||||
color: var(--blue-400);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) .mobile-controls-bar {
|
||||
background: var(--surface-card);
|
||||
border-bottom-color: var(--surface-border);
|
||||
}
|
||||
|
||||
:root:not([data-theme]) .mobile-sidebar .sidebar-item.active {
|
||||
background: var(--blue-900);
|
||||
color: var(--blue-400);
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive - Desktop */
|
||||
@media (max-width: 768px) {
|
||||
.stats-header {
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user