feat: Add shared components, refactor stores, improve data-entry workflow

Shared Components:
- Add CompanySelector.vue and PeriodSelector.vue components
- Add AppHeader.vue and SlideMenu.vue layout components
- Add shared stores factories (companies.js, accountingPeriod.js)
- Add shared routes factories (companies.py, calendar.py)
- Add shared models (company.py, calendar.py)
- Add shared layout styles (header.css, navigation.css)

Data Entry App:
- Update CLAUDE.md with prod/test server documentation
- Improve nomenclature sync service with better error handling
- Update receipts router and CRUD operations
- Add company/period stores using shared factories
- Update App.vue layout with shared components
- Fix OCRUploadZone file handling

Reports App:
- Refactor stores to use shared factories
- Update App.vue to use shared layout components

Infrastructure:
- Replace start-data-entry.sh with separate dev/test scripts
- Add .claude/rules for authentication, backend patterns, etc.
- Add implementation plan for OCR receipt improvements
- Clean up old documentation files

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-15 15:00:45 +02:00
parent c5fde510a8
commit 1a6e9b17d2
47 changed files with 4079 additions and 2595 deletions

View File

@@ -0,0 +1,132 @@
<template>
<header class="header-container" :class="headerClass">
<nav class="header-nav">
<!-- Left side: Hamburger + Brand -->
<div class="header-left">
<button
class="hamburger-btn"
:class="{ active: menuOpen }"
@click="$emit('menu-toggle')"
aria-label="Toggle navigation menu"
>
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
</button>
<router-link :to="brandLink" class="header-brand">
<slot name="brand">
<span>{{ title }}</span>
</slot>
</router-link>
</div>
<!-- Right side: Period + Company + User -->
<div class="header-actions">
<PeriodSelector
v-if="showPeriod && selectedCompany"
:period-store="periodStore"
:companies-store="companiesStore"
variant="header"
@period-changed="onPeriodChanged"
/>
<CompanySelector
v-if="showCompany"
:companies-store="companiesStore"
:show-fiscal-code="true"
variant="header"
@company-changed="onCompanyChanged"
/>
<slot name="user-menu">
<div v-if="showUser && currentUser" class="header-user" @click="$emit('user-menu-toggle')">
<i class="pi pi-user"></i>
<span class="desktop-only">{{ currentUser?.username || 'User' }}</span>
</div>
</slot>
</div>
</nav>
</header>
</template>
<script>
import { computed } from "vue";
import CompanySelector from "../CompanySelector.vue";
import PeriodSelector from "../PeriodSelector.vue";
export default {
name: "AppHeader",
components: {
CompanySelector,
PeriodSelector,
},
props: {
// Header title/brand text
title: {
type: String,
default: "ROA2WEB",
},
// Router link for brand click
brandLink: {
type: String,
default: "/",
},
// Additional CSS class for header (e.g., 'header-container--gradient')
headerClass: {
type: String,
default: "",
},
// Is hamburger menu open?
menuOpen: {
type: Boolean,
default: false,
},
// Companies store instance (required for selectors)
companiesStore: {
type: Object,
required: true,
},
// Period store instance (required for period selector)
periodStore: {
type: Object,
required: true,
},
// Current user object for display
currentUser: {
type: Object,
default: null,
},
// Show/hide period selector
showPeriod: {
type: Boolean,
default: true,
},
// Show/hide company selector
showCompany: {
type: Boolean,
default: true,
},
// Show/hide user info
showUser: {
type: Boolean,
default: true,
},
},
emits: ["menu-toggle", "company-changed", "period-changed", "user-menu-toggle"],
setup(props, { emit }) {
const selectedCompany = computed(() => props.companiesStore.selectedCompany);
const onCompanyChanged = (company) => {
emit("company-changed", company);
};
const onPeriodChanged = (period) => {
emit("period-changed", period);
};
return {
selectedCompany,
onCompanyChanged,
onPeriodChanged,
};
},
};
</script>