feat: Add mobile-optimized card layout and compact UI for all report views
- Add mobile card layout for Invoices, Treasury, and Trial Balance views - Implement two-row mobile toolbar with icon-only action buttons - Add uniform totals grid across all views with compact number formatting - Move profile menu to hamburger menu on mobile devices - Fix company selector and period selector truncation on mobile - Add mobile-specific CSS with responsive breakpoints 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,9 @@
|
||||
transition: transform var(--transition-normal);
|
||||
z-index: var(--z-modal);
|
||||
overflow-y: auto;
|
||||
/* Flex container for profile section at bottom */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.slide-menu.open {
|
||||
|
||||
@@ -693,3 +693,401 @@
|
||||
min-height: 44px; /* Touch-friendly height */
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Mobile Compact Toolbar
|
||||
Ultra-compact header for mobile views
|
||||
- Filter toggle (funnel icon)
|
||||
- Actions dropdown menu
|
||||
- Single compact total display
|
||||
============================================ */
|
||||
|
||||
/* Mobile Toolbar - compact single line (~50px) */
|
||||
.mobile-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm, 0.5rem);
|
||||
padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
|
||||
background: var(--surface-card, #ffffff);
|
||||
border: 1px solid var(--surface-border, #e2e8f0);
|
||||
border-radius: var(--border-radius, 6px);
|
||||
margin-bottom: var(--space-md, 1rem);
|
||||
min-height: 50px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Filter toggle button - colored when filters are active */
|
||||
.mobile-toolbar .filter-active {
|
||||
color: var(--primary-color, #2563eb) !important;
|
||||
background: rgba(37, 99, 235, 0.1) !important;
|
||||
}
|
||||
|
||||
.mobile-toolbar .filter-active:hover {
|
||||
background: rgba(37, 99, 235, 0.2) !important;
|
||||
}
|
||||
|
||||
/* Actions button - compact */
|
||||
.mobile-toolbar .p-button-outlined {
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Compact total display - pushed to right */
|
||||
.mobile-toolbar .mobile-total {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs, 0.25rem);
|
||||
font-size: var(--text-sm, 0.875rem);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.mobile-toolbar .total-label {
|
||||
color: var(--text-color-secondary, #64748b);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mobile-toolbar .total-value {
|
||||
font-weight: var(--font-semibold, 600);
|
||||
color: var(--text-color, #1e293b);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Color classes for positive/negative values */
|
||||
.mobile-toolbar .total-value.incasari {
|
||||
color: var(--green-600, #16a34a);
|
||||
}
|
||||
|
||||
.mobile-toolbar .total-value.plati {
|
||||
color: var(--red-600, #dc2626);
|
||||
}
|
||||
|
||||
/* Mobile-only visibility - show toolbar only on mobile */
|
||||
@media (min-width: 769px) {
|
||||
.mobile-toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra compact on very small screens */
|
||||
@media (max-width: 400px) {
|
||||
.mobile-toolbar {
|
||||
padding: var(--space-xs, 0.25rem) var(--space-sm, 0.5rem);
|
||||
gap: var(--space-xs, 0.25rem);
|
||||
}
|
||||
|
||||
.mobile-toolbar .mobile-total {
|
||||
font-size: var(--text-xs, 0.75rem);
|
||||
}
|
||||
|
||||
.mobile-toolbar .p-button-outlined {
|
||||
padding: 0.375rem 0.5rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
/* Hide label on very small screens, show only icon */
|
||||
.mobile-toolbar .p-button-outlined .p-button-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-toolbar .p-button-outlined .p-button-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Filters card - more compact when visible on mobile */
|
||||
@media (max-width: 768px) {
|
||||
.filters-card {
|
||||
margin-bottom: var(--space-sm, 0.5rem);
|
||||
}
|
||||
|
||||
.filters-card .p-card-body {
|
||||
padding: var(--space-sm, 0.5rem);
|
||||
}
|
||||
|
||||
.filters-card .form-row {
|
||||
gap: var(--space-sm, 0.5rem);
|
||||
}
|
||||
|
||||
.filters-card .form-group {
|
||||
margin-bottom: var(--space-xs, 0.25rem);
|
||||
}
|
||||
|
||||
.filters-card .form-label {
|
||||
font-size: var(--text-sm, 0.875rem);
|
||||
margin-bottom: var(--space-xs, 0.25rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Mobile Data Cards
|
||||
Compact card layout for table data on mobile
|
||||
============================================ */
|
||||
|
||||
.mobile-card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.mobile-data-card {
|
||||
background: var(--surface-card, #ffffff);
|
||||
border: 1px solid var(--surface-border, #e2e8f0);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.mobile-data-card .card-header {
|
||||
font-weight: 600;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--text-color, #1e293b);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mobile-data-card .card-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-color-secondary, #64748b);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.mobile-data-card .card-meta {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.mobile-data-card .card-amount {
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.mobile-data-card .card-amount.positive {
|
||||
color: var(--green-600, #16a34a);
|
||||
}
|
||||
|
||||
.mobile-data-card .card-amount.negative {
|
||||
color: var(--red-600, #dc2626);
|
||||
}
|
||||
|
||||
/* Mobile empty state */
|
||||
.mobile-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
|
||||
.mobile-empty i {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Mobile Responsive Totals
|
||||
Unified grid layout for totals on mobile
|
||||
Supports 1, 2, or 4 totals uniformly
|
||||
============================================ */
|
||||
|
||||
.mobile-totals-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.375rem 1rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--surface-ground, #f8fafc);
|
||||
border-radius: 6px;
|
||||
font-size: 0.8125rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Single total - center it */
|
||||
.mobile-totals-grid.single-total {
|
||||
grid-template-columns: 1fr;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
/* Two totals - side by side */
|
||||
.mobile-totals-grid.two-totals {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.mobile-totals-grid .total-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.mobile-totals-grid .total-label {
|
||||
color: var(--text-color-secondary, #64748b);
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-totals-grid .total-value {
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.mobile-totals-grid .total-value.incasari,
|
||||
.mobile-totals-grid .total-value.positive {
|
||||
color: var(--green-600, #16a34a);
|
||||
}
|
||||
|
||||
.mobile-totals-grid .total-value.plati,
|
||||
.mobile-totals-grid .total-value.negative {
|
||||
color: var(--red-600, #dc2626);
|
||||
}
|
||||
|
||||
/* Backward compatibility - stack totals (deprecated, use grid) */
|
||||
.mobile-totals-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
font-size: 0.75rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mobile-totals-stack .total-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.mobile-totals-stack .total-label {
|
||||
color: var(--text-color-secondary, #64748b);
|
||||
}
|
||||
|
||||
.mobile-totals-stack .total-value {
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Mobile Toolbar v2 - Two-Row Layout
|
||||
Row 1: Icon-only action buttons
|
||||
Row 2: Totals display
|
||||
============================================ */
|
||||
|
||||
.mobile-toolbar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm, 0.5rem);
|
||||
padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
|
||||
background: var(--surface-card, #ffffff);
|
||||
border: 1px solid var(--surface-border, #e2e8f0);
|
||||
border-radius: var(--border-radius, 6px);
|
||||
margin-bottom: var(--space-md, 1rem);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Row 1: Icon-only action buttons */
|
||||
.mobile-toolbar-buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
gap: var(--space-xs, 0.25rem);
|
||||
}
|
||||
|
||||
/* Icon-only buttons - no labels */
|
||||
.mobile-toolbar-buttons .p-button {
|
||||
padding: var(--space-sm, 0.5rem);
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mobile-toolbar-buttons .p-button .p-button-label {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mobile-toolbar-buttons .p-button .p-button-icon {
|
||||
margin-right: 0 !important;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
/* Filter active state */
|
||||
.mobile-toolbar-buttons .filter-active {
|
||||
color: var(--primary-color, #2563eb) !important;
|
||||
background: rgba(37, 99, 235, 0.1) !important;
|
||||
border-color: var(--primary-color, #2563eb) !important;
|
||||
}
|
||||
|
||||
.mobile-toolbar-buttons .filter-active:hover {
|
||||
background: rgba(37, 99, 235, 0.2) !important;
|
||||
}
|
||||
|
||||
/* Row 2: Totals display */
|
||||
.mobile-toolbar-totals {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: var(--space-xs, 0.25rem);
|
||||
border-top: 1px solid var(--surface-border, #e2e8f0);
|
||||
}
|
||||
|
||||
/* Center the totals grid/stack in row 2 */
|
||||
.mobile-toolbar-totals .mobile-totals-grid,
|
||||
.mobile-toolbar-totals .mobile-totals-stack,
|
||||
.mobile-toolbar-totals .mobile-total {
|
||||
margin-left: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Hide on desktop */
|
||||
@media (min-width: 769px) {
|
||||
.mobile-toolbar-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra compact on very small screens */
|
||||
@media (max-width: 400px) {
|
||||
.mobile-toolbar-container {
|
||||
padding: var(--space-xs, 0.25rem) var(--space-sm, 0.5rem);
|
||||
gap: var(--space-xs, 0.25rem);
|
||||
}
|
||||
|
||||
.mobile-toolbar-buttons .p-button {
|
||||
padding: var(--space-xs, 0.25rem);
|
||||
min-width: 40px;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.mobile-toolbar-buttons .p-button .p-button-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Hamburger Menu Profile Section
|
||||
Profile options at bottom of slide menu
|
||||
============================================ */
|
||||
|
||||
.menu-profile {
|
||||
margin-top: auto;
|
||||
border-top: 1px solid var(--color-border, #e2e8f0);
|
||||
padding-top: var(--space-md, 1rem);
|
||||
}
|
||||
|
||||
.menu-profile .profile-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm, 0.5rem);
|
||||
padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
|
||||
font-weight: 600;
|
||||
color: var(--color-text, #1e293b);
|
||||
font-size: var(--text-sm, 0.875rem);
|
||||
}
|
||||
|
||||
.menu-profile .profile-info .pi-user {
|
||||
font-size: 1.25rem;
|
||||
color: var(--color-primary, #2563eb);
|
||||
}
|
||||
|
||||
@@ -497,18 +497,55 @@ export default {
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.company-selector-mini {
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.company-trigger {
|
||||
min-width: auto;
|
||||
max-width: 200px;
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
}
|
||||
|
||||
.company-info {
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: var(--text-xs);
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.company-code {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.company-dropdown-panel {
|
||||
left: -16px;
|
||||
right: -16px;
|
||||
width: calc(100% + 32px);
|
||||
position: fixed;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
top: 60px;
|
||||
width: auto;
|
||||
max-height: 70vh;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra small screens */
|
||||
@media (max-width: 400px) {
|
||||
.company-selector-mini {
|
||||
max-width: 160px;
|
||||
}
|
||||
|
||||
.company-trigger {
|
||||
max-width: 160px;
|
||||
}
|
||||
|
||||
.company-info {
|
||||
max-width: 110px;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
max-width: 110px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -349,18 +349,36 @@ export default {
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.period-selector-mini {
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
max-width: 140px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.period-trigger {
|
||||
min-width: auto;
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
}
|
||||
|
||||
.period-info {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.period-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.period-name {
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.period-dropdown-panel {
|
||||
left: -16px;
|
||||
right: -16px;
|
||||
width: calc(100% + 32px);
|
||||
position: fixed;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
top: 60px;
|
||||
width: auto;
|
||||
max-height: 70vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
v-model="selectedCompany"
|
||||
@company-changed="onCompanyChanged"
|
||||
/>
|
||||
<div class="user-menu-container">
|
||||
<div class="user-menu-container mobile-hide">
|
||||
<div class="header-user" @click="toggleUserMenu">
|
||||
<i class="pi pi-user"></i>
|
||||
<span class="desktop-only">{{
|
||||
@@ -311,5 +311,10 @@ export default {
|
||||
.user-dropdown-item {
|
||||
padding: var(--space-sm);
|
||||
}
|
||||
|
||||
/* Hide profile menu on mobile - use hamburger menu instead */
|
||||
.mobile-hide {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -75,6 +75,16 @@
|
||||
<span>Statistici cache</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Profile Section (at bottom) -->
|
||||
<div class="menu-section menu-profile">
|
||||
<div class="profile-info">
|
||||
<i class="pi pi-user"></i>
|
||||
<span>{{ currentUser?.username || 'Utilizator' }}</span>
|
||||
</div>
|
||||
<ul class="menu-list">
|
||||
<li class="menu-item">
|
||||
<router-link
|
||||
to="/telegram"
|
||||
@@ -86,6 +96,12 @@
|
||||
<span>Telegram Bot</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="#" class="menu-link" @click.prevent="handleLogout">
|
||||
<i class="menu-icon pi pi-sign-out"></i>
|
||||
<span>Deconectare</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -93,6 +109,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useAuthStore } from "../../stores/auth";
|
||||
|
||||
export default {
|
||||
name: "HamburgerMenu",
|
||||
props: {
|
||||
@@ -103,12 +123,29 @@ export default {
|
||||
},
|
||||
emits: ["close"],
|
||||
setup(props, { emit }) {
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const currentUser = computed(() => authStore.currentUser);
|
||||
|
||||
const closeMenu = () => {
|
||||
emit("close");
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
authStore.logout();
|
||||
closeMenu();
|
||||
await router.push("/login");
|
||||
} catch (error) {
|
||||
console.error("Logout error:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
currentUser,
|
||||
closeMenu,
|
||||
handleLogout,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<i class="pi pi-wallet"></i>
|
||||
Registru Casă / Bancă
|
||||
</h1>
|
||||
<p class="page-subtitle">
|
||||
Selectați tipul de registru pentru a vizualiza mișcările
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Company Selection (when no company selected) -->
|
||||
@@ -33,8 +30,71 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Mobile: Two-row toolbar -->
|
||||
<div v-if="isMobile && companyStore.selectedCompany" class="mobile-toolbar-container">
|
||||
<!-- Row 1: Icon-only action buttons -->
|
||||
<div class="mobile-toolbar-buttons">
|
||||
<Button
|
||||
icon="pi pi-filter"
|
||||
:class="{ 'filter-active': hasActiveFilters }"
|
||||
class="p-button-text"
|
||||
@click="showFilters = !showFilters"
|
||||
v-tooltip.bottom="'Filtre'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
class="p-button-text"
|
||||
@click="resetFilters"
|
||||
v-tooltip.bottom="'Resetează'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-file-excel"
|
||||
class="p-button-text p-button-success"
|
||||
@click="exportExcel"
|
||||
:disabled="!hasData"
|
||||
v-tooltip.bottom="'Excel'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-file-pdf"
|
||||
class="p-button-text p-button-danger"
|
||||
@click="exportPDF"
|
||||
:disabled="!hasData"
|
||||
v-tooltip.bottom="'PDF'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-refresh"
|
||||
class="p-button-text"
|
||||
:loading="treasuryStore.isLoading"
|
||||
@click="refreshData"
|
||||
v-tooltip.bottom="'Actualizează'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Totals grid -->
|
||||
<div class="mobile-toolbar-totals">
|
||||
<div class="mobile-totals-grid">
|
||||
<div class="total-item">
|
||||
<span class="total-label">Sold Prec:</span>
|
||||
<span class="total-value">{{ formatCompact(treasuryStore.totals.sold_precedent_all) }}</span>
|
||||
</div>
|
||||
<div class="total-item">
|
||||
<span class="total-label">Încasări:</span>
|
||||
<span class="total-value incasari">{{ formatCompact(treasuryStore.totals.total_incasari_all) }}</span>
|
||||
</div>
|
||||
<div class="total-item">
|
||||
<span class="total-label">Plăți:</span>
|
||||
<span class="total-value plati">{{ formatCompact(treasuryStore.totals.total_plati_all) }}</span>
|
||||
</div>
|
||||
<div class="total-item">
|
||||
<span class="total-label">Sold Final:</span>
|
||||
<span class="total-value">{{ formatCompact(treasuryStore.totals.sold_final_all) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<Card v-if="companyStore.selectedCompany" class="filters-card">
|
||||
<Card v-if="companyStore.selectedCompany && (!isMobile || showFilters)" class="filters-card">
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<div class="form-row">
|
||||
@@ -81,8 +141,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Separate action buttons row -->
|
||||
<div class="form-actions">
|
||||
<!-- Desktop: Action buttons -->
|
||||
<div v-if="!isMobile" class="form-actions">
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
label="Resetează Filtre"
|
||||
@@ -114,9 +174,9 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Summary Stats - Compact, right aligned -->
|
||||
<!-- Summary Stats - Compact, right aligned (hidden on mobile - only Sold Final in toolbar) -->
|
||||
<!-- Folosește totaluri din TOATE înregistrările (backend) nu doar pagina curentă -->
|
||||
<div v-if="companyStore.selectedCompany" class="summary-stats-inline">
|
||||
<div v-if="!isMobile && companyStore.selectedCompany" class="summary-stats-inline">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Sold Precedent:</span>
|
||||
<span
|
||||
@@ -150,7 +210,35 @@
|
||||
<!-- Data Table -->
|
||||
<Card v-if="companyStore.selectedCompany" class="data-card">
|
||||
<template #content>
|
||||
<!-- Mobile: Card Layout -->
|
||||
<div v-if="isMobile" class="mobile-card-list">
|
||||
<div
|
||||
v-for="reg in treasuryStore.registers"
|
||||
:key="`${reg.dataact}-${reg.nract}`"
|
||||
class="mobile-data-card"
|
||||
>
|
||||
<div class="card-header">{{ reg.nume || 'Fără partener' }}</div>
|
||||
<div class="card-row">
|
||||
<span class="card-meta">{{ formatDateShort(reg.dataact) }} · {{ reg.nume_cont_bancar }}</span>
|
||||
<span
|
||||
class="card-amount"
|
||||
:class="reg.incasari > 0 ? 'positive' : (reg.plati > 0 ? 'negative' : '')"
|
||||
>
|
||||
<template v-if="reg.incasari > 0">+{{ formatNumber(reg.incasari) }}</template>
|
||||
<template v-else-if="reg.plati > 0">-{{ formatNumber(reg.plati) }}</template>
|
||||
<template v-else>{{ formatNumber(0) }}</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="treasuryStore.registers.length === 0" class="mobile-empty">
|
||||
<i class="pi pi-info-circle"></i>
|
||||
<p>Nu au fost găsite înregistrări</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop: DataTable -->
|
||||
<DataTable
|
||||
v-if="!isMobile"
|
||||
:value="treasuryStore.registers"
|
||||
:loading="treasuryStore.isLoading"
|
||||
:paginator="true"
|
||||
@@ -260,7 +348,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useTreasuryStore } from "../stores/treasury";
|
||||
import { useCompanyStore } from "../stores/companies";
|
||||
@@ -276,6 +364,19 @@ const periodStore = useAccountingPeriodStore();
|
||||
// State for company selection
|
||||
const selectedCompanyId = ref(companyStore.selectedCompany?.id_firma || null);
|
||||
|
||||
// Mobile state
|
||||
const isMobile = ref(window.innerWidth < 768);
|
||||
const showFilters = ref(false);
|
||||
const actionsMenu = ref(null);
|
||||
|
||||
// Handle window resize
|
||||
const handleResize = () => {
|
||||
isMobile.value = window.innerWidth < 768;
|
||||
if (!isMobile.value) {
|
||||
showFilters.value = false; // Reset when switching to desktop
|
||||
}
|
||||
};
|
||||
|
||||
// Register type options for dropdown - doar cele 4 tipuri, fără "Toate"
|
||||
const registerTypeOptions = [
|
||||
{ label: "Casă LEI", value: "CASA_LEI" },
|
||||
@@ -319,6 +420,27 @@ const formatDate = (dateString) => {
|
||||
return format(new Date(dateString), "dd.MM.yyyy");
|
||||
};
|
||||
|
||||
// Short date format for mobile cards (DD/MM)
|
||||
const formatDateShort = (dateString) => {
|
||||
if (!dateString) return "";
|
||||
const date = new Date(dateString);
|
||||
return `${String(date.getDate()).padStart(2, "0")}/${String(date.getMonth() + 1).padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
// Compact number format (no decimals for large numbers)
|
||||
const formatCompact = (amount) => {
|
||||
if (!amount) return "0";
|
||||
if (Math.abs(amount) >= 10000) {
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
maximumFractionDigits: 0,
|
||||
}).format(amount);
|
||||
}
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
// Truncate text to maxLength characters
|
||||
const truncateText = (text, maxLength = 100) => {
|
||||
if (!text) return "";
|
||||
@@ -447,6 +569,42 @@ const resetFilters = async () => {
|
||||
// Computed
|
||||
const hasData = computed(() => treasuryStore.registers.length > 0);
|
||||
|
||||
// Mobile: Check if any filter is active (non-default value)
|
||||
const hasActiveFilters = computed(() => {
|
||||
return (
|
||||
filters.value.registerType !== "BANCA_LEI" ||
|
||||
filters.value.partnerName !== "" ||
|
||||
filters.value.bankAccount !== null
|
||||
);
|
||||
});
|
||||
|
||||
// Mobile: Actions menu items
|
||||
const actionMenuItems = computed(() => [
|
||||
{
|
||||
label: "Resetează Filtre",
|
||||
icon: "pi pi-filter-slash",
|
||||
command: resetFilters,
|
||||
},
|
||||
{
|
||||
label: "Export Excel",
|
||||
icon: "pi pi-file-excel",
|
||||
command: exportExcel,
|
||||
disabled: !hasData.value,
|
||||
},
|
||||
{
|
||||
label: "Export PDF",
|
||||
icon: "pi pi-file-pdf",
|
||||
command: exportPDF,
|
||||
disabled: !hasData.value,
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
label: "Actualizează",
|
||||
icon: "pi pi-refresh",
|
||||
command: refreshData,
|
||||
},
|
||||
]);
|
||||
|
||||
// Handle company change from dropdown
|
||||
const handleCompanyChange = async () => {
|
||||
if (!selectedCompanyId.value) return;
|
||||
@@ -696,6 +854,9 @@ const loadData = async () => {
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// Add resize listener for mobile detection
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
// Load companies if not loaded
|
||||
if (!companyStore.hasCompanies) {
|
||||
await companyStore.loadCompanies();
|
||||
@@ -708,6 +869,10 @@ onMounted(async () => {
|
||||
// Don't load data here - let period watch handle it with immediate: true
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
|
||||
// Watch for company changes
|
||||
watch(
|
||||
() => companyStore.selectedCompany,
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<i class="pi pi-file-text"></i>
|
||||
Facturi
|
||||
</h1>
|
||||
<p class="page-subtitle">
|
||||
Vizualizați și gestionați facturile pentru compania selectată
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Company Selection -->
|
||||
@@ -32,8 +29,61 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Mobile: Two-row toolbar -->
|
||||
<div v-if="isMobile && companyStore.selectedCompany" class="mobile-toolbar-container">
|
||||
<!-- Row 1: Icon-only action buttons -->
|
||||
<div class="mobile-toolbar-buttons">
|
||||
<Button
|
||||
icon="pi pi-filter"
|
||||
:class="{ 'filter-active': hasActiveFilters }"
|
||||
class="p-button-text"
|
||||
@click="showFilters = !showFilters"
|
||||
v-tooltip.bottom="'Filtre'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
class="p-button-text"
|
||||
@click="clearFilters"
|
||||
v-tooltip.bottom="'Resetează'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-file-excel"
|
||||
class="p-button-text p-button-success"
|
||||
@click="exportExcel"
|
||||
:disabled="!invoicesStore.hasInvoices"
|
||||
v-tooltip.bottom="'Excel'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-file-pdf"
|
||||
class="p-button-text p-button-danger"
|
||||
@click="exportPDF"
|
||||
:disabled="!invoicesStore.hasInvoices"
|
||||
v-tooltip.bottom="'PDF'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-refresh"
|
||||
class="p-button-text"
|
||||
:loading="invoicesStore.isLoading"
|
||||
@click="refreshData"
|
||||
v-tooltip.bottom="'Actualizează'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Totals (unified grid format) -->
|
||||
<div class="mobile-toolbar-totals">
|
||||
<div class="mobile-totals-grid single-total">
|
||||
<div class="total-item">
|
||||
<span class="total-label">Sold Total:</span>
|
||||
<span class="total-value" :class="invoicesStore.totalSoldAll > 0 ? 'positive' : 'negative'">
|
||||
{{ formatCompact(invoicesStore.totalSoldAll) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters and Controls -->
|
||||
<Card v-if="companyStore.selectedCompany" class="filters-card">
|
||||
<Card v-if="companyStore.selectedCompany && (!isMobile || showFilters)" class="filters-card">
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<div class="form-row">
|
||||
@@ -96,7 +146,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filters-actions">
|
||||
<!-- Desktop: Action buttons -->
|
||||
<div v-if="!isMobile" class="filters-actions">
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
label="Resetează Filtre"
|
||||
@@ -128,9 +179,9 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Summary Stats - Compact, right aligned -->
|
||||
<!-- Summary Stats - Compact, right aligned (hidden on mobile - shown in toolbar) -->
|
||||
<!-- Total sold din TOATE facturile filtrate (nu doar pagina curentă) -->
|
||||
<div v-if="companyStore.selectedCompany && invoicesStore.hasInvoices" class="summary-stats-inline">
|
||||
<div v-if="!isMobile && companyStore.selectedCompany && invoicesStore.hasInvoices" class="summary-stats-inline">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Total Sold:</span>
|
||||
<span class="stat-value" :class="invoicesStore.totalSoldAll > 0 ? 'plati' : 'incasari'">
|
||||
@@ -142,7 +193,33 @@
|
||||
<!-- Invoices Table -->
|
||||
<Card v-if="companyStore.selectedCompany" class="table-card">
|
||||
<template #content>
|
||||
<!-- Mobile: Card Layout -->
|
||||
<div v-if="isMobile" class="mobile-card-list">
|
||||
<div
|
||||
v-for="invoice in invoicesStore.invoiceList"
|
||||
:key="invoice.nract"
|
||||
class="mobile-data-card"
|
||||
>
|
||||
<div class="card-header">{{ invoice.nume }}</div>
|
||||
<div class="card-row">
|
||||
<span>{{ formatDate(invoice.dataact) }} · {{ invoice.nract }}</span>
|
||||
<span
|
||||
class="card-amount"
|
||||
:class="{ positive: invoice.soldfinal > 0 }"
|
||||
>
|
||||
{{ formatNumber(invoice.soldfinal) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="invoicesStore.invoiceList.length === 0" class="mobile-empty">
|
||||
<i class="pi pi-info-circle"></i>
|
||||
<p>Nu au fost găsite facturi</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop: DataTable -->
|
||||
<DataTable
|
||||
v-if="!isMobile"
|
||||
:value="invoicesStore.invoiceList"
|
||||
:loading="invoicesStore.isLoading"
|
||||
:paginator="true"
|
||||
@@ -245,7 +322,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useCompanyStore } from "../stores/companies";
|
||||
import { useInvoicesStore } from "../stores/invoices";
|
||||
@@ -262,6 +339,19 @@ const periodStore = useAccountingPeriodStore();
|
||||
// State
|
||||
const selectedCompanyId = ref(companyStore.selectedCompany?.id_firma || null);
|
||||
|
||||
// Mobile state
|
||||
const isMobile = ref(window.innerWidth < 768);
|
||||
const showFilters = ref(false);
|
||||
const actionsMenu = ref(null);
|
||||
|
||||
// Handle window resize
|
||||
const handleResize = () => {
|
||||
isMobile.value = window.innerWidth < 768;
|
||||
if (!isMobile.value) {
|
||||
showFilters.value = false; // Reset when switching to desktop
|
||||
}
|
||||
};
|
||||
|
||||
const filters = ref({
|
||||
type: "CLIENTI",
|
||||
paymentStatus: "neachitate", // Default to unpaid invoices
|
||||
@@ -280,6 +370,43 @@ const accountingPeriodText = computed(() => {
|
||||
return periodStore.selectedPeriod?.display_name || "";
|
||||
});
|
||||
|
||||
// Mobile: Check if any filter is active (non-default value)
|
||||
const hasActiveFilters = computed(() => {
|
||||
return (
|
||||
filters.value.type !== "CLIENTI" ||
|
||||
filters.value.paymentStatus !== "neachitate" ||
|
||||
filters.value.searchTerm !== "" ||
|
||||
filters.value.cont !== ""
|
||||
);
|
||||
});
|
||||
|
||||
// Mobile: Actions menu items
|
||||
const actionMenuItems = computed(() => [
|
||||
{
|
||||
label: "Resetează Filtre",
|
||||
icon: "pi pi-filter-slash",
|
||||
command: clearFilters,
|
||||
},
|
||||
{
|
||||
label: "Export Excel",
|
||||
icon: "pi pi-file-excel",
|
||||
command: exportExcel,
|
||||
disabled: !invoicesStore.hasInvoices,
|
||||
},
|
||||
{
|
||||
label: "Export PDF",
|
||||
icon: "pi pi-file-pdf",
|
||||
command: exportPDF,
|
||||
disabled: !invoicesStore.hasInvoices,
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
label: "Actualizează",
|
||||
icon: "pi pi-refresh",
|
||||
command: refreshData,
|
||||
},
|
||||
]);
|
||||
|
||||
// Options
|
||||
const invoiceTypes = [
|
||||
{ label: "Clienți", value: "CLIENTI" },
|
||||
@@ -308,6 +435,20 @@ const formatNumber = (amount) => {
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
// Compact format for mobile totals (e.g., "34.922" instead of "34.922,02 RON")
|
||||
const formatCompact = (amount) => {
|
||||
if (!amount || amount === 0) return "0";
|
||||
const absAmount = Math.abs(amount);
|
||||
if (absAmount >= 1000000) {
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
maximumFractionDigits: 1,
|
||||
}).format(amount / 1000000) + "M";
|
||||
}
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
maximumFractionDigits: 0,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return "";
|
||||
try {
|
||||
@@ -631,6 +772,9 @@ const exportPDF = async () => {
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
// Add resize listener for mobile detection
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
// Load companies if not loaded
|
||||
if (!companyStore.hasCompanies) {
|
||||
await companyStore.loadCompanies();
|
||||
@@ -638,6 +782,10 @@ onMounted(async () => {
|
||||
// Don't load here - let period watch handle it with immediate: true
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
|
||||
// Watch for company changes
|
||||
watch(
|
||||
() => companyStore.selectedCompany,
|
||||
@@ -675,18 +823,12 @@ watch(
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 0.5rem 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-color-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.company-selection-card,
|
||||
.filters-card {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
<i class="pi pi-calculator"></i>
|
||||
Balanță de Verificare
|
||||
</h1>
|
||||
<p class="page-subtitle">
|
||||
{{ currentPeriodText }} -
|
||||
{{ companyStore.selectedCompany?.name || "Selectați companie" }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Company Selection -->
|
||||
@@ -33,8 +29,63 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Mobile: Two-row toolbar -->
|
||||
<div v-if="isMobile && companyStore.selectedCompany" class="mobile-toolbar-container">
|
||||
<!-- Row 1: Icon-only action buttons -->
|
||||
<div class="mobile-toolbar-buttons">
|
||||
<Button
|
||||
icon="pi pi-filter"
|
||||
:class="{ 'filter-active': hasActiveFilters }"
|
||||
class="p-button-text"
|
||||
@click="showFilters = !showFilters"
|
||||
v-tooltip.bottom="'Filtre'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
class="p-button-text"
|
||||
@click="clearFilters"
|
||||
v-tooltip.bottom="'Resetează'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-file-excel"
|
||||
class="p-button-text p-button-success"
|
||||
@click="exportExcel"
|
||||
:disabled="!trialBalanceStore.hasData"
|
||||
v-tooltip.bottom="'Excel'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-file-pdf"
|
||||
class="p-button-text p-button-danger"
|
||||
@click="exportPDF"
|
||||
:disabled="!trialBalanceStore.hasData"
|
||||
v-tooltip.bottom="'PDF'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-refresh"
|
||||
class="p-button-text"
|
||||
:loading="trialBalanceStore.isLoading"
|
||||
@click="refreshData"
|
||||
v-tooltip.bottom="'Actualizează'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Totals (unified grid format) -->
|
||||
<div class="mobile-toolbar-totals">
|
||||
<div class="mobile-totals-grid two-totals">
|
||||
<div class="total-item">
|
||||
<span class="total-label">Sold D:</span>
|
||||
<span class="total-value">{{ formatCompact(trialBalanceStore.totals.total_sold_final_debit) }}</span>
|
||||
</div>
|
||||
<div class="total-item">
|
||||
<span class="total-label">Sold C:</span>
|
||||
<span class="total-value">{{ formatCompact(trialBalanceStore.totals.total_sold_final_credit) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<Card v-if="companyStore.selectedCompany" class="filters-card">
|
||||
<Card v-if="companyStore.selectedCompany && (!isMobile || showFilters)" class="filters-card">
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<div class="form-row">
|
||||
@@ -65,7 +116,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<!-- Desktop: Action buttons -->
|
||||
<div v-if="!isMobile" class="form-actions">
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
label="Resetează Filtre"
|
||||
@@ -97,9 +149,9 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Summary Totals - Uses shared stats.css -->
|
||||
<!-- Summary Totals - Uses shared stats.css (hidden on mobile - compact in toolbar) -->
|
||||
<!-- Totaluri din TOATE înregistrările filtrate (nu doar pagina curentă) -->
|
||||
<div v-if="companyStore.selectedCompany && trialBalanceStore.hasData" class="summary-stats-inline">
|
||||
<div v-if="!isMobile && companyStore.selectedCompany && trialBalanceStore.hasData" class="summary-stats-inline">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Sume Prec. D:</span>
|
||||
<span class="stat-value">{{ formatCurrency(trialBalanceStore.totals.total_sold_precedent_debit) }}</span>
|
||||
@@ -129,7 +181,34 @@
|
||||
<!-- Trial Balance Table -->
|
||||
<Card v-if="companyStore.selectedCompany" class="table-card">
|
||||
<template #content>
|
||||
<!-- Mobile: Card Layout -->
|
||||
<div v-if="isMobile" class="mobile-card-list">
|
||||
<div
|
||||
v-for="account in trialBalanceStore.trialBalanceData.filter(a => a.sold_final_debit > 0 || a.sold_final_credit > 0)"
|
||||
:key="account.cont"
|
||||
class="mobile-data-card"
|
||||
>
|
||||
<div class="card-header">
|
||||
<strong>{{ account.cont }}</strong> {{ truncate(account.denumire, 30) }}
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<span></span>
|
||||
<span class="card-amount">
|
||||
{{ account.sold_final_debit > 0
|
||||
? formatCurrency(account.sold_final_debit) + ' D'
|
||||
: formatCurrency(account.sold_final_credit) + ' C' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="trialBalanceStore.trialBalanceData.filter(a => a.sold_final_debit > 0 || a.sold_final_credit > 0).length === 0" class="mobile-empty">
|
||||
<i class="pi pi-info-circle"></i>
|
||||
<p>Nu au fost găsite date</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop: DataTable -->
|
||||
<DataTable
|
||||
v-if="!isMobile"
|
||||
:value="trialBalanceStore.trialBalanceData"
|
||||
:loading="trialBalanceStore.isLoading"
|
||||
:paginator="true"
|
||||
@@ -263,7 +342,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useCompanyStore } from "../stores/companies";
|
||||
import { useTrialBalanceStore } from "../stores/trialBalance";
|
||||
@@ -278,6 +357,19 @@ const periodStore = useAccountingPeriodStore();
|
||||
// State
|
||||
const selectedCompanyId = ref(companyStore.selectedCompany?.id_firma || null);
|
||||
|
||||
// Mobile state
|
||||
const isMobile = ref(window.innerWidth < 768);
|
||||
const showFilters = ref(false);
|
||||
const actionsMenu = ref(null);
|
||||
|
||||
// Handle window resize
|
||||
const handleResize = () => {
|
||||
isMobile.value = window.innerWidth < 768;
|
||||
if (!isMobile.value) {
|
||||
showFilters.value = false; // Reset when switching to desktop
|
||||
}
|
||||
};
|
||||
|
||||
const localFilters = ref({
|
||||
cont: "",
|
||||
denumire: "",
|
||||
@@ -289,6 +381,38 @@ const currentPeriodText = computed(() => {
|
||||
return periodStore.selectedPeriod?.display_name || "";
|
||||
});
|
||||
|
||||
// Mobile: Check if any filter is active (non-default value)
|
||||
const hasActiveFilters = computed(() => {
|
||||
return localFilters.value.cont !== "" || localFilters.value.denumire !== "";
|
||||
});
|
||||
|
||||
// Mobile: Actions menu items
|
||||
const actionMenuItems = computed(() => [
|
||||
{
|
||||
label: "Resetează Filtre",
|
||||
icon: "pi pi-filter-slash",
|
||||
command: clearFilters,
|
||||
},
|
||||
{
|
||||
label: "Export Excel",
|
||||
icon: "pi pi-file-excel",
|
||||
command: exportExcel,
|
||||
disabled: !trialBalanceStore.hasData,
|
||||
},
|
||||
{
|
||||
label: "Export PDF",
|
||||
icon: "pi pi-file-pdf",
|
||||
command: exportPDF,
|
||||
disabled: !trialBalanceStore.hasData,
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
label: "Actualizează",
|
||||
icon: "pi pi-refresh",
|
||||
command: refreshData,
|
||||
},
|
||||
]);
|
||||
|
||||
// Methods
|
||||
const formatCurrency = (amount) => {
|
||||
if (!amount || amount === 0) return "0,00";
|
||||
@@ -298,6 +422,26 @@ const formatCurrency = (amount) => {
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
// Compact format for mobile totals (e.g., "449.881" instead of "449.881,12")
|
||||
const formatCompact = (amount) => {
|
||||
if (!amount || amount === 0) return "0";
|
||||
const absAmount = Math.abs(amount);
|
||||
if (absAmount >= 1000000) {
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
maximumFractionDigits: 1,
|
||||
}).format(amount / 1000000) + "M";
|
||||
}
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
maximumFractionDigits: 0,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
// Truncate text for mobile cards
|
||||
const truncate = (text, maxLength) => {
|
||||
if (!text || text.length <= maxLength) return text;
|
||||
return text.substring(0, maxLength) + "...";
|
||||
};
|
||||
|
||||
const handleCompanyChange = async () => {
|
||||
if (!selectedCompanyId.value) return;
|
||||
|
||||
@@ -715,6 +859,9 @@ const exportPDF = async () => {
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
// Add resize listener for mobile detection
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
// Load companies if not loaded
|
||||
if (!companyStore.hasCompanies) {
|
||||
await companyStore.loadCompanies();
|
||||
@@ -734,6 +881,10 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
|
||||
// Watch for company changes
|
||||
watch(
|
||||
() => companyStore.selectedCompany,
|
||||
|
||||
Reference in New Issue
Block a user