V 2.0.2 Nun mit Tabs

Anzahl in der Liste als const
This commit is contained in:
rxf
2026-03-06 13:36:21 +01:00
parent 204bf3bf8b
commit a7863c519f
4 changed files with 104 additions and 67 deletions

View File

@@ -4,9 +4,10 @@ import { useState, useEffect } from 'react';
import AusgabenForm from '@/components/AusgabenForm'; import AusgabenForm from '@/components/AusgabenForm';
import AusgabenList from '@/components/AusgabenList'; import AusgabenList from '@/components/AusgabenList';
import MonatsStatistik from '@/components/MonatsStatistik'; import MonatsStatistik from '@/components/MonatsStatistik';
import LogoutButton from '@/components/LogoutButton'; import TabLayout from '@/components/TabLayout';
import { AusgabenEntry } from '@/types/ausgaben'; import { AusgabenEntry } from '@/types/ausgaben';
import packageJson from '@/package.json';
const MAX_ENTRIES = 15;
export default function Home() { export default function Home() {
const [activeTab, setActiveTab] = useState(0); // 0 = Haushalt, 1 = Privat const [activeTab, setActiveTab] = useState(0); // 0 = Haushalt, 1 = Privat
@@ -15,9 +16,6 @@ export default function Home() {
const [selectedEntry, setSelectedEntry] = useState<AusgabenEntry | null>(null); const [selectedEntry, setSelectedEntry] = useState<AusgabenEntry | null>(null);
const [statsRefreshKey, setStatsRefreshKey] = useState(0); const [statsRefreshKey, setStatsRefreshKey] = useState(0);
const version = packageJson.version;
const buildDate = process.env.NEXT_PUBLIC_BUILD_DATE || new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
useEffect(() => { useEffect(() => {
fetchRecentEntries(); fetchRecentEntries();
setSelectedEntry(null); // Clear selected entry when switching tabs setSelectedEntry(null); // Clear selected entry when switching tabs
@@ -26,7 +24,7 @@ export default function Home() {
const fetchRecentEntries = async () => { const fetchRecentEntries = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
const response = await fetch(`/api/ausgaben?limit=20&typ=${activeTab}`, { const response = await fetch(`/api/ausgaben?limit=${MAX_ENTRIES}&typ=${activeTab}`, {
cache: 'no-store', cache: 'no-store',
headers: { headers: {
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
@@ -62,65 +60,22 @@ export default function Home() {
}; };
return ( return (
<div className="min-h-screen bg-white py-4 px-4"> <TabLayout activeTab={activeTab} onTabChange={setActiveTab}>
<main className="max-w-7xl mx-auto border-2 border-black rounded-lg p-6 bg-[#FFFFDD]"> <div>
<div className="flex justify-between items-center mb-6"> <h2 className="text-xl font-semibold mb-4">Eingabe</h2>
<h1 className="text-3xl font-bold">Ausgaben - Log</h1> <AusgabenForm onSuccess={handleSuccess} selectedEntry={selectedEntry} typ={activeTab} />
<LogoutButton className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm rounded-lg transition-colors shadow-md" />
</div>
{/* Tab Navigation */}
<div className="flex gap-2 mb-6">
<button
onClick={() => setActiveTab(0)}
className={`flex-1 py-3 px-6 rounded-lg font-semibold transition-colors ${
activeTab === 0
? 'bg-[#85B7D7] text-black'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
Haushalt
</button>
<button
onClick={() => setActiveTab(1)}
className={`flex-1 py-3 px-6 rounded-lg font-semibold transition-colors ${
activeTab === 1
? 'bg-[#85B7D7] text-black'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
Privat
</button>
</div>
<div> <MonatsStatistik typ={activeTab} refreshKey={statsRefreshKey} />
<h2 className="text-xl font-semibold mb-4">Eingabe</h2>
<AusgabenForm onSuccess={handleSuccess} selectedEntry={selectedEntry} typ={activeTab} />
<MonatsStatistik typ={activeTab} refreshKey={statsRefreshKey} />
<div className="mt-6 bg-white border border-black rounded-lg shadow-md p-6">
<h3 className="text-xl font-semibold mb-4">Letzte 20 Einträge</h3>
{isLoading ? (
<div className="text-center py-4">Lade Daten...</div>
) : (
<AusgabenList entries={entries} onDelete={handleDelete} onEdit={handleEdit} />
)}
</div>
</div>
{/* Footer */} <div className="mt-6 bg-white border border-black rounded-lg shadow-md p-6">
<footer className="mt-8 flex justify-between items-center text-sm text-gray-600 px-4 "> <h3 className="text-xl font-semibold mb-4">Letzte {MAX_ENTRIES} Einträge</h3>
<div> {isLoading ? (
<a href="mailto:rxf@gmx.de" className="text-blue-600 hover:underline"> <div className="text-center py-4">Lade Daten...</div>
mailto:rxf@gmx.de ) : (
</a> <AusgabenList entries={entries} onDelete={handleDelete} onEdit={handleEdit} />
</div> )}
<div className="text-right"> </div>
Version {version} - {buildDate} </div>
</div> </TabLayout>
</footer>
</main>
</div>
); );
} }

View File

@@ -50,7 +50,7 @@ export default function AusgabenList({ entries, onDelete, onEdit }: AusgabenList
<th className="border-b-2 border-black p-2 w-12">Kat.</th> <th className="border-b-2 border-black p-2 w-12">Kat.</th>
<th className="border-b-2 border-black p-2 w-8">Betrag</th> <th className="border-b-2 border-black p-2 w-8">Betrag</th>
<th className="border-b-2 border-black p-2 w-16">Wie</th> <th className="border-b-2 border-black p-2 w-16">Wie</th>
<th className="border-b-2 border-black p-2 w-38">Aktion</th> <th className="border-b-2 border-black p-2 w-48">Aktion</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -79,7 +79,7 @@ export default function AusgabenList({ entries, onDelete, onEdit }: AusgabenList
onClick={() => onEdit(entry)} onClick={() => onEdit(entry)}
className="text-blue-600 hover:text-blue-800 px-3 py-1 rounded text-sm mr-2" className="text-blue-600 hover:text-blue-800 px-3 py-1 rounded text-sm mr-2"
> >
Bearbeiten Editieren
</button> </button>
<button <button
onClick={() => setConfirmId(entry.ID)} onClick={() => setConfirmId(entry.ID)}

82
components/TabLayout.tsx Normal file
View File

@@ -0,0 +1,82 @@
'use client';
import { ReactNode } from 'react';
import LogoutButton from '@/components/LogoutButton';
import packageJson from '@/package.json';
interface Tab {
label: string;
index: number;
}
interface TabLayoutProps {
children: ReactNode;
activeTab: number;
onTabChange: (index: number) => void;
}
const TABS: Tab[] = [
{ label: 'Haushalt', index: 0 },
{ label: 'Privat', index: 1 },
];
export default function TabLayout({ children, activeTab, onTabChange }: TabLayoutProps) {
const version = packageJson.version;
const buildDate =
process.env.NEXT_PUBLIC_BUILD_DATE ||
new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
return (
<div className="min-h-screen py-8 px-4">
{/* Outer wrapper with border */}
<div className="max-w-316 mx-auto border-2 border-black rounded-xl bg-gray-200 p-6">
{/* Page title */}
<h1 className="text-4xl font-bold text-center mb-6 tracking-tight">Ausgaben - Log</h1>
{/* Inner content */}
<div className="max-w-6xl mx-auto">
{/* Tab bar */}
<div className="flex justify-between items-end">
<div className="flex">
{TABS.map(tab => {
const isActive = activeTab === tab.index;
return (
<button
key={tab.index}
onClick={() => onTabChange(tab.index)}
className="px-6 py-2 text-sm font-semibold border-t-2 border-l-2 border-r-2 rounded-tl-lg rounded-tr-lg mr-1 transition-colors"
style={
isActive
? { backgroundColor: '#FFFFDD', color: '#000000', borderColor: '#000000', borderBottom: '2px solid #FFFFDD', marginBottom: '-2px', position: 'relative', zIndex: 10 }
: { backgroundColor: '#85B7D7', color: '#374151', borderColor: '#000000' }
}
>
{tab.label}
</button>
);
})}
</div>
<div className="pb-1">
<LogoutButton className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm rounded-lg shadow-md" />
</div>
</div>
{/* Content panel */}
<main className="border-2 border-black rounded-b-lg rounded-tr-lg p-6 bg-[#FFFFDD]">
{children}
</main>
<footer className="mt-8 flex justify-between items-center text-sm text-gray-600 px-4">
<a href="mailto:rxf@gmx.de" className="hover:underline">
mailto:rxf@gmx.de
</a>
<div>Version {version} - {buildDate}</div>
</footer>
</div>
</div>
</div>
);
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "ausgaben_next", "name": "ausgaben_next",
"version": "1.2.2", "version": "2.0.2",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 3005", "dev": "next dev -p 3005",