condorhacker commited on
Commit
9db82f0
·
verified ·
1 Parent(s): 898bb2d

Upload App.tsx

Browse files
Files changed (1) hide show
  1. App.tsx +249 -0
App.tsx ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ import Navigation from './components/Navigation';
4
+ import Dashboard from './components/Dashboard';
5
+ import AssistantChat from './components/AssistantChat';
6
+ import { Transaction, CalendarEvent, FamilyMember } from './types';
7
+ import { CATEGORIES } from './constants';
8
+
9
+ const App: React.FC = () => {
10
+ const [activeTab, setActiveTab] = useState('dashboard');
11
+ const [showExpenseForm, setShowExpenseForm] = useState(false);
12
+
13
+ // Mock Initial Data
14
+ const [transactions, setTransactions] = useState<Transaction[]>([
15
+ {
16
+ id: '1',
17
+ date: '2026-03-01',
18
+ amount: 2500,
19
+ category: 'Lavoro',
20
+ payer: FamilyMember.PAPA,
21
+ description: 'Stipendio base',
22
+ type: 'INCOME'
23
+ },
24
+ {
25
+ id: '2',
26
+ date: '2026-03-05',
27
+ amount: 450,
28
+ category: 'Alimentari',
29
+ payer: FamilyMember.MAMMA,
30
+ description: 'Spesa settimanale Gourmet',
31
+ type: 'EXPENSE'
32
+ },
33
+ {
34
+ id: '3',
35
+ date: '2026-03-10',
36
+ amount: 120,
37
+ category: 'Salute',
38
+ payer: FamilyMember.NONNI,
39
+ description: 'Visita oculistica',
40
+ type: 'EXPENSE'
41
+ }
42
+ ]);
43
+
44
+ const [newExpense, setNewExpense] = useState({
45
+ amount: '',
46
+ description: '',
47
+ category: CATEGORIES.EXPENSE[0],
48
+ payer: FamilyMember.PAPA
49
+ });
50
+
51
+ const [events, setEvents] = useState<CalendarEvent[]>([
52
+ {
53
+ id: 'e1',
54
+ title: 'Cena di Pasqua',
55
+ start: '2026-04-05T20:00:00',
56
+ end: '2026-04-05T23:00:00',
57
+ participants: [FamilyMember.TUTTI, FamilyMember.NONNI]
58
+ },
59
+ {
60
+ id: 'e2',
61
+ title: 'Allenamento Calcio',
62
+ start: '2026-03-25T17:00:00',
63
+ end: '2026-03-25T19:00:00',
64
+ participants: [FamilyMember.FIGLIO_GRANDE]
65
+ }
66
+ ]);
67
+
68
+ const handleAddExpense = (e: React.FormEvent) => {
69
+ e.preventDefault();
70
+ if (!newExpense.amount || !newExpense.description) return;
71
+
72
+ const transaction: Transaction = {
73
+ id: Date.now().toString(),
74
+ date: new Date().toISOString().split('T')[0],
75
+ amount: parseFloat(newExpense.amount),
76
+ category: newExpense.category,
77
+ payer: newExpense.payer,
78
+ description: newExpense.description,
79
+ type: 'EXPENSE'
80
+ };
81
+
82
+ setTransactions([transaction, ...transactions]);
83
+ setShowExpenseForm(false);
84
+ setNewExpense({
85
+ amount: '',
86
+ description: '',
87
+ category: CATEGORIES.EXPENSE[0],
88
+ payer: FamilyMember.PAPA
89
+ });
90
+ };
91
+
92
+ const contextData = {
93
+ transactions,
94
+ events,
95
+ familyMembers: Object.keys(FamilyMember),
96
+ currentDate: '2026-03-15'
97
+ };
98
+
99
+ const renderContent = () => {
100
+ switch (activeTab) {
101
+ case 'dashboard':
102
+ return <Dashboard transactions={transactions} events={events} />;
103
+ case 'finance':
104
+ return (
105
+ <div className="p-4 md:p-8 max-w-5xl mx-auto pb-32">
106
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
107
+ <div>
108
+ <h2 className="text-3xl font-bold text-slate-900">Gestione Finanze</h2>
109
+ <p className="text-slate-500">Monitoraggio entrate e uscite della famiglia.</p>
110
+ </div>
111
+ <button
112
+ onClick={() => setShowExpenseForm(true)}
113
+ className="bg-slate-900 text-white px-6 py-3 rounded-2xl font-bold shadow-xl hover:bg-slate-800 transition-all active:scale-95 flex items-center justify-center gap-2"
114
+ >
115
+ <span className="text-xl">+</span> Aggiungi Nuova Spesa
116
+ </button>
117
+ </div>
118
+
119
+ {showExpenseForm && (
120
+ <div className="fixed inset-0 bg-slate-900/40 backdrop-blur-sm z-[100] flex items-center justify-center p-4">
121
+ <div className="bg-white rounded-3xl p-8 max-w-md w-full shadow-2xl">
122
+ <h3 className="text-xl font-bold mb-6 text-slate-800">Registra Uscita</h3>
123
+ <form onSubmit={handleAddExpense} className="space-y-4">
124
+ <div>
125
+ <label className="block text-xs font-bold text-slate-400 uppercase mb-1">Importo (€)</label>
126
+ <input
127
+ type="number"
128
+ required
129
+ value={newExpense.amount}
130
+ onChange={e => setNewExpense({...newExpense, amount: e.target.value})}
131
+ className="w-full bg-slate-50 border-none rounded-xl p-3 focus:ring-2 focus:ring-slate-200 outline-none"
132
+ placeholder="0.00"
133
+ />
134
+ </div>
135
+ <div>
136
+ <label className="block text-xs font-bold text-slate-400 uppercase mb-1">Descrizione</label>
137
+ <input
138
+ type="text"
139
+ required
140
+ value={newExpense.description}
141
+ onChange={e => setNewExpense({...newExpense, description: e.target.value})}
142
+ className="w-full bg-slate-50 border-none rounded-xl p-3 focus:ring-2 focus:ring-slate-200 outline-none"
143
+ placeholder="es. Cena ristorante"
144
+ />
145
+ </div>
146
+ <div className="grid grid-cols-2 gap-4">
147
+ <div>
148
+ <label className="block text-xs font-bold text-slate-400 uppercase mb-1">Categoria</label>
149
+ <select
150
+ value={newExpense.category}
151
+ onChange={e => setNewExpense({...newExpense, category: e.target.value})}
152
+ className="w-full bg-slate-50 border-none rounded-xl p-3 focus:ring-2 focus:ring-slate-200 outline-none"
153
+ >
154
+ {CATEGORIES.EXPENSE.map(cat => <option key={cat} value={cat}>{cat}</option>)}
155
+ </select>
156
+ </div>
157
+ <div>
158
+ <label className="block text-xs font-bold text-slate-400 uppercase mb-1">Pagato da</label>
159
+ <select
160
+ value={newExpense.payer}
161
+ onChange={e => setNewExpense({...newExpense, payer: e.target.value as FamilyMember})}
162
+ className="w-full bg-slate-50 border-none rounded-xl p-3 focus:ring-2 focus:ring-slate-200 outline-none"
163
+ >
164
+ {Object.values(FamilyMember).map(m => <option key={m} value={m}>{m}</option>)}
165
+ </select>
166
+ </div>
167
+ </div>
168
+ <div className="flex gap-3 pt-4">
169
+ <button
170
+ type="button"
171
+ onClick={() => setShowExpenseForm(false)}
172
+ className="flex-1 px-4 py-3 rounded-xl font-bold text-slate-500 hover:bg-slate-100 transition-all"
173
+ >
174
+ Annulla
175
+ </button>
176
+ <button
177
+ type="submit"
178
+ className="flex-1 bg-slate-900 text-white px-4 py-3 rounded-xl font-bold shadow-lg hover:bg-slate-800 transition-all"
179
+ >
180
+ Conferma
181
+ </button>
182
+ </div>
183
+ </form>
184
+ </div>
185
+ </div>
186
+ )}
187
+
188
+ <div className="space-y-4">
189
+ {transactions.map(t => (
190
+ <div key={t.id} className="bg-white p-5 rounded-2xl shadow-sm border border-slate-100 flex justify-between items-center group hover:border-slate-300 transition-all">
191
+ <div className="flex items-center gap-4">
192
+ <div className={`w-12 h-12 rounded-2xl flex items-center justify-center text-xl ${t.type === 'INCOME' ? 'bg-emerald-50 text-emerald-600' : 'bg-rose-50 text-rose-600'}`}>
193
+ {t.type === 'INCOME' ? '↓' : '↑'}
194
+ </div>
195
+ <div>
196
+ <p className="font-bold text-slate-800">{t.description}</p>
197
+ <p className="text-xs text-slate-400 font-medium">{t.payer} • {t.category} • {new Date(t.date).toLocaleDateString('it-IT')}</p>
198
+ </div>
199
+ </div>
200
+ <p className={`text-lg font-bold ${t.type === 'INCOME' ? 'text-emerald-600' : 'text-slate-900'}`}>
201
+ {t.type === 'INCOME' ? '+' : '-'} € {t.amount.toLocaleString('it-IT')}
202
+ </p>
203
+ </div>
204
+ ))}
205
+ </div>
206
+ </div>
207
+ );
208
+ case 'calendar':
209
+ return (
210
+ <div className="p-8 max-w-5xl mx-auto">
211
+ <h2 className="text-3xl font-bold text-slate-900 mb-8">Calendario 2026</h2>
212
+ <div className="grid grid-cols-1 gap-4">
213
+ {events.map(e => (
214
+ <div key={e.id} className="bg-white p-6 rounded-2xl shadow-sm border border-slate-100 flex items-start justify-between">
215
+ <div>
216
+ <p className="text-sm font-bold text-slate-400 uppercase mb-1">{new Date(e.start).toLocaleDateString('it-IT', { weekday: 'long', day: 'numeric', month: 'long' })}</p>
217
+ <h3 className="text-xl font-bold text-slate-800">{e.title}</h3>
218
+ <p className="text-sm text-slate-500 mt-1">{new Date(e.start).toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' })}</p>
219
+ </div>
220
+ <div className="flex -space-x-2">
221
+ {e.participants.map(p => (
222
+ <div key={p} className="w-8 h-8 rounded-full border-2 border-white bg-slate-200 flex items-center justify-center text-[8px] font-bold" title={p}>
223
+ {p.substring(0, 1)}
224
+ </div>
225
+ ))}
226
+ </div>
227
+ </div>
228
+ ))}
229
+ </div>
230
+ </div>
231
+ );
232
+ case 'assistant':
233
+ return <AssistantChat context={contextData} />;
234
+ default:
235
+ return <Dashboard transactions={transactions} events={events} />;
236
+ }
237
+ };
238
+
239
+ return (
240
+ <div className="flex flex-col md:flex-row h-screen overflow-hidden">
241
+ <Navigation activeTab={activeTab} setActiveTab={setActiveTab} />
242
+ <main className="flex-1 overflow-y-auto bg-slate-50/50">
243
+ {renderContent()}
244
+ </main>
245
+ </div>
246
+ );
247
+ };
248
+
249
+ export default App;