Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot

Modern ERP Reports Application with microservices architecture

Tech Stack:
- Backend: FastAPI + python-oracledb (Oracle DB integration)
- Frontend: Vue.js 3 + PrimeVue + Vite
- Telegram Bot: python-telegram-bot + SQLite
- Infrastructure: Shared database pool, JWT authentication, SSH tunnel

Features:
- FastAPI backend with async Oracle connection pool
- Vue.js 3 responsive frontend with PrimeVue components
- Telegram bot alternative interface
- Microservices architecture with shared components
- Complete deployment support (Linux Docker + Windows IIS)
- Comprehensive testing (Playwright E2E + pytest)

Repository Structure:
- reports-app/ - Main application (backend, frontend, telegram-bot)
- shared/ - Shared components (database pool, auth, utils)
- deployment/ - Deployment scripts (Linux & Windows)
- docs/ - Project documentation
- security/ - Security scanning and git hooks
This commit is contained in:
2025-10-25 14:55:08 +03:00
commit 6b13ffa183
237 changed files with 70035 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
<template>
<div id="app">
<!-- Navigation Bar -->
<Menubar
v-if="authStore.isAuthenticated"
:model="menuItems"
class="app-menubar"
>
<template #start>
<div class="flex align-items-center gap-2">
<i class="pi pi-chart-bar text-primary text-2xl"></i>
<span class="font-bold text-xl">ROA Reports</span>
</div>
</template>
<template #end>
<div class="flex align-items-center gap-3">
<Badge
:value="selectedCompany?.name || 'Selectați firmă'"
:severity="selectedCompany ? 'info' : 'warning'"
/>
<Button
icon="pi pi-sign-out"
label="Deconectare"
text
@click="logout"
class="p-button-text"
/>
</div>
</template>
</Menubar>
<!-- Main Content -->
<main
class="main-content"
:class="{ 'with-navbar': authStore.isAuthenticated }"
>
<router-view />
</main>
<!-- Global Toast Messages - positioned below header to avoid covering company selector -->
<Toast position="top-center" :style="{ top: '80px' }" />
<!-- Global Confirmation Dialog -->
<ConfirmDialog />
</div>
</template>
<script setup>
import { computed, onMounted } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "./stores/auth";
import { useCompanyStore } from "./stores/companies";
const router = useRouter();
const authStore = useAuthStore();
const companyStore = useCompanyStore();
// Dashboard options
const dashboardOptions = [
{ label: 'Main Dashboard', value: '/dashboard' },
{ label: 'New Dashboard', value: '/dashboard-new' },
{ label: 'Ultra Minimal', value: '/dashboard-v1' },
{ label: 'Compact Grid', value: '/dashboard-v2' },
{ label: 'Data Tables', value: '/dashboard-v3' },
{ label: 'Action Center', value: '/dashboard-v4' }
];
// Menu items for navigation
const menuItems = computed(() => [
{
label: "Dashboard",
icon: "pi pi-home",
items: dashboardOptions.map(option => ({
label: option.label,
command: () => router.push(option.value)
}))
},
{
label: "Facturi",
icon: "pi pi-file-text",
command: () => router.push("/invoices"),
},
{
label: "Registru Casa si Banca",
icon: "pi pi-wallet",
command: () => router.push("/bank-cash-register"),
},
]);
// Get selected company
const selectedCompany = computed(() => companyStore.selectedCompany);
// Logout function
const logout = () => {
authStore.logout();
router.push("/login");
};
// Initialize app
onMounted(async () => {
// Check authentication on app start
if (authStore.isAuthenticated) {
try {
// Load companies if authenticated
await companyStore.loadCompanies();
} catch (error) {
console.error("Failed to load companies:", error);
}
}
});
</script>
<style scoped>
#app {
min-height: 100vh;
background-color: var(--surface-ground);
}
.app-menubar {
border-radius: 0;
border-left: none;
border-right: none;
border-top: none;
}
.main-content {
transition: all 0.3s ease;
}
.main-content.with-navbar {
margin-top: 0;
min-height: calc(100vh - 70px);
}
.main-content:not(.with-navbar) {
min-height: 100vh;
}
</style>
<style>
/* Global styles */
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family:
"Inter",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Oxygen,
Ubuntu,
Cantarell,
sans-serif;
background-color: var(--surface-ground);
}
.app-container {
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
}
/* Responsive design */
@media (max-width: 768px) {
.app-container {
padding: 0.25rem;
max-width: 100%;
}
.app-menubar .p-menubar-button {
display: block;
}
}
@media (max-width: 480px) {
.main-content {
padding: 0;
}
.app-container {
padding: 0;
max-width: 100vw;
}
}
/* Custom PrimeVue overrides */
.p-button {
font-weight: 500;
}
.p-datatable .p-datatable-tbody > tr.invoice-paid {
background-color: var(--green-50);
color: var(--green-900);
}
.p-datatable .p-datatable-tbody > tr.invoice-overdue {
background-color: var(--red-50);
color: var(--red-900);
}
/* Status badges */
.status-paid {
background-color: var(--green-100);
color: var(--green-900);
}
.status-overdue {
background-color: var(--red-100);
color: var(--red-900);
}
.status-pending {
background-color: var(--yellow-100);
color: var(--yellow-900);
}
</style>