Skip to content

Commit 02352f7

Browse files
author
Gemini Bot
committed
ui/ux overhaul and add subscription (by gemini)
1 parent 23444b2 commit 02352f7

15 files changed

Lines changed: 1137 additions & 941 deletions

src/components/AboutSection.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ const AboutSection: React.FC = () => {
88
<div className="rounded-xl border bg-card p-8 text-center space-y-6 shadow-sm">
99
{/* Title */}
1010
<h2 className="text-2xl font-bold text-foreground">
11-
Tentang
11+
hi there!
1212
</h2>
1313

1414
{/* Subtitle */}
1515
<p className="text-sm text-muted-foreground font-medium">
16-
Aureus adalah aplikasi pencatat keuangan sederhana yang berjalan sepenuhnya di perangkat Anda.
16+
Thanks for using my app! This project is open-source and available on GitHub. Feel free to check it out, contribute, or report any issues you find.
1717
</p>
1818

1919
{/* Social Links */}
20-
<div className="flex items-center justify-center gap-6 py-4">
20+
<div className="flex flex-wrap items-center justify-center gap-4 py-4">
2121
{/* Instagram */}
2222
<a
2323
href="https://instagram.com/rmeydani_"
@@ -44,7 +44,7 @@ const AboutSection: React.FC = () => {
4444
{/* Humble Quote */}
4545
<div className="pt-4 border-t border-border">
4646
<p className="text-sm text-muted-foreground italic">
47-
Dibuat untuk pencatatan harian yang cepat, rapi, dan mudah ditinjau.
47+
"how to center a div?"
4848
</p>
4949
</div>
5050

src/components/BottomNav.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
2-
import { Home, BarChart, File, Grid } from 'lucide-react';
2+
import { Home, BarChart, File, Grid, Ticket } from 'lucide-react';
33
import { cn } from '@/lib/utils';
44

5-
export type NavTab = 'home' | 'stats' | 'reports' | 'more';
5+
export type NavTab = 'home' | 'stats' | 'subs' | 'reports' | 'more';
66

77
interface BottomNavProps {
88
activeTab: NavTab;
@@ -13,6 +13,7 @@ const BottomNav: React.FC<BottomNavProps> = ({ activeTab, onTabChange }) => {
1313
const navItems = [
1414
{ id: 'home' as NavTab, icon: Home, label: 'Beranda' },
1515
{ id: 'stats' as NavTab, icon: BarChart, label: 'Statistik' },
16+
{ id: 'subs' as NavTab, icon: Ticket, label: 'Langganan' },
1617
{ id: 'reports' as NavTab, icon: File, label: 'Laporan' },
1718
{ id: 'more' as NavTab, icon: Grid, label: 'Lainnya' },
1819
];

src/components/Header.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React from 'react';
22
import ThemeToggle from './ThemeToggle';
3+
import { Sun } from 'lucide-react';
34

45
const Header: React.FC = () => {
56
return (
67
<header className="sticky top-0 z-50 border-b bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60 safe-area-top">
78
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
89
<div className="flex h-16 items-center justify-between">
910
<div className="flex items-center gap-3">
11+
<Sun className="h-6 w-6 text-primary animate-spin-slow" />
1012
<h1 className="text-lg font-bold tracking-tight sm:text-xl">
1113
<span className="gradient-text">Aureus</span>
1214
</h1>

src/components/SmartInsights.tsx

Lines changed: 42 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1,213 +1,58 @@
1-
2-
import React from 'react';
3-
import { Badge } from '@/components/ui/badge';
4-
import { ArrowUp, ArrowDown, AlertCircle, Circle } from 'lucide-react';
5-
import { Transaction } from '@/pages/Index';
1+
import React, { useState, useEffect } from 'react';
62
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
3+
import { Quote } from 'lucide-react';
4+
import { Transaction } from '@/pages/Index';
75

86
interface SmartInsightsProps {
97
transactions: Transaction[];
108
}
119

12-
const SmartInsights: React.FC<SmartInsightsProps> = ({ transactions }) => {
13-
const generateInsights = () => {
14-
const insights: Array<{
15-
type: 'success' | 'warning' | 'info' | 'danger';
16-
icon: React.ReactNode;
17-
title: string;
18-
message: string;
19-
}> = [];
20-
21-
const currentDate = new Date();
22-
const currentMonth = currentDate.getMonth();
23-
const currentYear = currentDate.getFullYear();
24-
const lastMonth = currentMonth === 0 ? 11 : currentMonth - 1;
25-
const lastMonthYear = currentMonth === 0 ? currentYear - 1 : currentYear;
26-
27-
// Current month transactions
28-
const thisMonthTransactions = transactions.filter(t => {
29-
const date = new Date(t.date);
30-
return date.getMonth() === currentMonth && date.getFullYear() === currentYear;
31-
});
32-
33-
// Last month transactions
34-
const lastMonthTransactions = transactions.filter(t => {
35-
const date = new Date(t.date);
36-
return date.getMonth() === lastMonth && date.getFullYear() === lastMonthYear;
37-
});
38-
39-
const thisMonthIncome = thisMonthTransactions.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0);
40-
const thisMonthExpense = thisMonthTransactions.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0);
41-
const lastMonthIncome = lastMonthTransactions.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0);
42-
const lastMonthExpense = lastMonthTransactions.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0);
43-
44-
// Income trend analysis
45-
if (lastMonthIncome > 0) {
46-
const incomeChange = ((thisMonthIncome - lastMonthIncome) / lastMonthIncome) * 100;
47-
if (incomeChange > 10) {
48-
insights.push({
49-
type: 'success',
50-
icon: <ArrowUp className="h-4 w-4" />,
51-
title: 'Pemasukan Meningkat!',
52-
message: `Pemasukan Anda naik ${incomeChange.toFixed(1)}% dibanding bulan lalu. Pertahankan!`
53-
});
54-
} else if (incomeChange < -10) {
55-
insights.push({
56-
type: 'warning',
57-
icon: <ArrowDown className="h-4 w-4" />,
58-
title: 'Pemasukan Menurun',
59-
message: `Pemasukan turun ${Math.abs(incomeChange).toFixed(1)}% dibanding bulan lalu. Perlu perhatian.`
60-
});
61-
}
62-
}
63-
64-
// Expense trend analysis
65-
if (lastMonthExpense > 0) {
66-
const expenseChange = ((thisMonthExpense - lastMonthExpense) / lastMonthExpense) * 100;
67-
if (expenseChange > 20) {
68-
insights.push({
69-
type: 'danger',
70-
icon: <AlertCircle className="h-4 w-4" />,
71-
title: 'Pengeluaran Melonjak!',
72-
message: `Pengeluaran naik ${expenseChange.toFixed(1)}% dibanding bulan lalu. Cek kategori terbesar.`
73-
});
74-
} else if (expenseChange < -10) {
75-
insights.push({
76-
type: 'success',
77-
icon: <ArrowDown className="h-4 w-4" />,
78-
title: 'Pengeluaran Terkendali',
79-
message: `Pengeluaran turun ${Math.abs(expenseChange).toFixed(1)}% dibanding bulan lalu. Bagus!`
80-
});
81-
}
82-
}
83-
84-
// Category analysis
85-
const expensesByCategory = thisMonthTransactions
86-
.filter(t => t.type === 'expense')
87-
.reduce((acc, t) => {
88-
acc[t.category] = (acc[t.category] || 0) + t.amount;
89-
return acc;
90-
}, {} as Record<string, number>);
91-
92-
const topCategory = Object.entries(expensesByCategory)
93-
.sort(([,a], [,b]) => b - a)[0];
94-
95-
if (topCategory && topCategory[1] > thisMonthExpense * 0.4) {
96-
insights.push({
97-
type: 'info',
98-
icon: <Circle className="h-4 w-4" />,
99-
title: 'Kategori Dominan',
100-
message: `${topCategory[0]} menghabiskan ${((topCategory[1] / thisMonthExpense) * 100).toFixed(1)}% total pengeluaran.`
101-
});
102-
}
103-
104-
// Savings rate
105-
if (thisMonthIncome > 0) {
106-
const savingsRate = ((thisMonthIncome - thisMonthExpense) / thisMonthIncome) * 100;
107-
if (savingsRate > 20) {
108-
insights.push({
109-
type: 'success',
110-
icon: <ArrowUp className="h-4 w-4" />,
111-
title: 'Tingkat Tabungan Bagus',
112-
message: `Anda menabung ${savingsRate.toFixed(1)}% dari pendapatan. Luar biasa!`
113-
});
114-
} else if (savingsRate < 0) {
115-
insights.push({
116-
type: 'danger',
117-
icon: <AlertCircle className="h-4 w-4" />,
118-
title: 'Pengeluaran > Pemasukan',
119-
message: 'Pengeluaran melebihi pemasukan bulan ini. Review anggaran Anda.'
120-
});
121-
}
122-
}
123-
124-
// Transaction frequency
125-
const dailyTransactions = thisMonthTransactions.length / currentDate.getDate();
126-
if (dailyTransactions > 5) {
127-
insights.push({
128-
type: 'info',
129-
icon: <Circle className="h-4 w-4" />,
130-
title: 'Aktivitas Transaksi Tinggi',
131-
message: `Rata-rata ${dailyTransactions.toFixed(1)} transaksi per hari. Pertimbangkan konsolidasi.`
132-
});
133-
}
134-
135-
return insights;
136-
};
137-
138-
const insights = generateInsights();
139-
140-
if (insights.length === 0) {
141-
return null;
142-
}
143-
144-
const getInsightStyle = (type: string) => {
145-
switch (type) {
146-
case 'success':
147-
return 'bg-success/10 border-success/30';
148-
case 'warning':
149-
return 'bg-primary/10 border-primary/30';
150-
case 'danger':
151-
return 'bg-destructive/10 border-destructive/30';
152-
case 'info':
153-
default:
154-
return 'bg-muted/40 border-border';
155-
}
156-
};
157-
158-
const getTextStyle = (type: string) => {
159-
switch (type) {
160-
case 'success':
161-
return 'text-success';
162-
case 'warning':
163-
return 'text-primary';
164-
case 'danger':
165-
return 'text-destructive';
166-
case 'info':
167-
default:
168-
return 'text-foreground';
169-
}
170-
};
10+
const MONEY_QUOTES = [
11+
"Uang tidak bisa membeli kebahagiaan, tapi lebih enak menangis di dalam Mercedes daripada di atas sepeda.",
12+
"Hemat pangkal kaya, tapi kalau pelit pangkal dijauhi teman.",
13+
"Jangan menabung apa yang tersisa setelah belanja, tapi belanjalah apa yang tersisa setelah menabung.",
14+
"Aturan No.1: Jangan pernah rugi. Aturan No.2: Jangan lupa Aturan No.1.",
15+
"Terlalu banyak orang menghabiskan uang yang tidak mereka miliki, untuk membeli barang yang tidak mereka butuhkan, demi membuat kagum orang yang tidak mereka sukai.",
16+
"Investasi dalam pengetahuan selalu membayar bunga terbaik.",
17+
"Bukan seberapa banyak uang yang kamu hasilkan, tapi seberapa banyak yang kamu simpan.",
18+
"Uang adalah hamba yang baik, tapi tuan yang buruk.",
19+
"Orang kaya punya TV kecil dan perpustakaan besar. Orang miskin punya TV besar dan tidak punya perpustakaan.",
20+
"Hati-hati dengan pengeluaran kecil; kebocoran kecil bisa menenggelamkan kapal besar.",
21+
"Saya suka uang saya berada di tempat yang bisa saya lihat: tergantung di lemari pakaian saya. (Carrie Bradshaw)",
22+
"Satu-satunya cara untuk tidak memikirkan uang adalah dengan memiliki banyak uang.",
23+
"Waktu adalah uang. Kalau kamu buang-buang waktu, kamu buang-buang uang.",
24+
"Gaji itu seperti menstruasi, datang sebulan sekali, tapi nyerinya bisa seminggu.",
25+
"Jika Anda berutang $100 pada bank, itu masalah Anda. Jika Anda berutang $100 juta, itu masalah bank."
26+
];
27+
28+
const SmartInsights: React.FC<SmartInsightsProps> = () => {
29+
const [quote, setQuote] = useState("");
30+
31+
useEffect(() => {
32+
// Pick a random quote on mount
33+
const randomQuote = MONEY_QUOTES[Math.floor(Math.random() * MONEY_QUOTES.length)];
34+
setQuote(randomQuote);
35+
}, []);
17136

17237
return (
173-
<Card>
174-
<CardHeader className="pb-4">
175-
<CardTitle className="flex items-center gap-2 text-base">
176-
<Circle className="h-5 w-5 text-primary" />
177-
Wawasan Keuangan
38+
<Card className="border-2 border-primary/20 shadow-[4px_4px_0px_0px_rgba(var(--primary),0.2)] hover:shadow-[6px_6px_0px_0px_rgba(var(--primary),0.4)] transition-all duration-300">
39+
<CardHeader className="pb-2">
40+
<CardTitle className="flex items-center gap-2 text-base font-display">
41+
<Quote className="h-5 w-5 text-primary rotate-180" />
42+
Kata Bijak (Mungkin)
17843
</CardTitle>
17944
</CardHeader>
18045
<CardContent>
181-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
182-
{insights.map((insight, index) => (
183-
<div
184-
key={index}
185-
className={`rounded-lg border p-4 ${getInsightStyle(insight.type)}`}
186-
>
187-
<div className="flex items-start gap-3">
188-
<div className={getTextStyle(insight.type)}>
189-
{insight.icon}
190-
</div>
191-
<div className="flex-1">
192-
<h4 className={`font-medium mb-1 ${getTextStyle(insight.type)}`}>
193-
{insight.title}
194-
</h4>
195-
<p className="text-sm text-muted-foreground">
196-
{insight.message}
197-
</p>
198-
</div>
199-
<Badge variant={insight.type === 'success' ? 'default' : 'secondary'} className="text-xs">
200-
{insight.type === 'success' ? 'Bagus' :
201-
insight.type === 'warning' ? 'Perhatian' :
202-
insight.type === 'danger' ? 'Penting' : 'Info'}
203-
</Badge>
204-
</div>
205-
</div>
206-
))}
207-
</div>
46+
<div className="relative p-6 bg-primary/5 rounded-lg border border-dashed border-primary/30">
47+
<Quote className="absolute top-2 left-2 h-8 w-8 text-primary/10 -scale-x-100" />
48+
<p className="text-lg font-medium text-center italic text-foreground/80 font-serif leading-relaxed">
49+
"{quote}"
50+
</p>
51+
<Quote className="absolute bottom-2 right-2 h-8 w-8 text-primary/10" />
52+
</div>
20853
</CardContent>
20954
</Card>
21055
);
21156
};
21257

213-
export default SmartInsights;
58+
export default SmartInsights;

0 commit comments

Comments
 (0)