V 2.0.2 Nun mit Tabs
Anzahl in der Liste als const
This commit is contained in:
59
app/page.tsx
59
app/page.tsx
@@ -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,37 +60,7 @@ 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 className="flex justify-between items-center mb-6">
|
|
||||||
<h1 className="text-3xl font-bold">Ausgaben - Log</h1>
|
|
||||||
<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>
|
<div>
|
||||||
<h2 className="text-xl font-semibold mb-4">Eingabe</h2>
|
<h2 className="text-xl font-semibold mb-4">Eingabe</h2>
|
||||||
<AusgabenForm onSuccess={handleSuccess} selectedEntry={selectedEntry} typ={activeTab} />
|
<AusgabenForm onSuccess={handleSuccess} selectedEntry={selectedEntry} typ={activeTab} />
|
||||||
@@ -100,7 +68,7 @@ export default function Home() {
|
|||||||
<MonatsStatistik typ={activeTab} refreshKey={statsRefreshKey} />
|
<MonatsStatistik typ={activeTab} refreshKey={statsRefreshKey} />
|
||||||
|
|
||||||
<div className="mt-6 bg-white border border-black rounded-lg shadow-md p-6">
|
<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>
|
<h3 className="text-xl font-semibold mb-4">Letzte {MAX_ENTRIES} Einträge</h3>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="text-center py-4">Lade Daten...</div>
|
<div className="text-center py-4">Lade Daten...</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -108,19 +76,6 @@ export default function Home() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</TabLayout>
|
||||||
{/* Footer */}
|
|
||||||
<footer className="mt-8 flex justify-between items-center text-sm text-gray-600 px-4 ">
|
|
||||||
<div>
|
|
||||||
<a href="mailto:rxf@gmx.de" className="text-blue-600 hover:underline">
|
|
||||||
mailto:rxf@gmx.de
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="text-right">
|
|
||||||
Version {version} - {buildDate}
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
82
components/TabLayout.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user