feat: Setup project with Vite and React

Initializes the Condopay frontend project using Vite, React, and TypeScript. Includes basic project structure, dependencies, and configuration for Tailwind CSS and React Router.
This commit is contained in:
2025-12-06 18:55:48 +01:00
parent 559c340f22
commit 79e249b638
26 changed files with 2364 additions and 8 deletions

102
pages/FamilyList.tsx Normal file
View File

@@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { CondoService } from '../services/mockDb';
import { Family, AppSettings } from '../types';
import { Search, ChevronRight, UserCircle } from 'lucide-react';
export const FamilyList: React.FC = () => {
const [families, setFamilies] = useState<Family[]>([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [settings, setSettings] = useState<AppSettings | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
CondoService.seedPayments();
const [fams, sets] = await Promise.all([
CondoService.getFamilies(),
CondoService.getSettings()
]);
setFamilies(fams);
setSettings(sets);
} catch (e) {
console.error("Error fetching data", e);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
const filteredFamilies = families.filter(f =>
f.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
f.unitNumber.toLowerCase().includes(searchTerm.toLowerCase())
);
if (loading) {
return <div className="flex justify-center items-center h-64 text-slate-400">Caricamento in corso...</div>;
}
return (
<div className="space-y-6">
{/* Responsive Header */}
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<div>
<h2 className="text-2xl font-bold text-slate-800">Elenco Condomini</h2>
<p className="text-slate-500 text-sm md:text-base">{settings?.condoName || 'Gestione Condominiale'}</p>
</div>
<div className="relative w-full md:w-80 lg:w-96">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Search className="h-5 w-5 text-slate-400" />
</div>
<input
type="text"
className="block w-full pl-10 pr-3 py-2.5 border border-slate-300 rounded-xl leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition duration-150 ease-in-out sm:text-sm shadow-sm"
placeholder="Cerca nome o interno..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
</div>
{/* List */}
<div className="bg-white shadow-sm rounded-xl overflow-hidden border border-slate-200">
<ul className="divide-y divide-slate-100">
{filteredFamilies.length === 0 ? (
<li className="p-8 text-center text-slate-500 flex flex-col items-center gap-2">
<Search className="w-8 h-8 text-slate-300" />
<span>Nessuna famiglia trovata.</span>
</li>
) : (
filteredFamilies.map((family) => (
<li key={family.id} className="hover:bg-slate-50 transition-colors active:bg-slate-100">
<Link to={`/family/${family.id}`} className="block p-4 sm:p-5">
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-3 sm:gap-4 overflow-hidden">
<div className="bg-blue-100 p-2 sm:p-2.5 rounded-full flex-shrink-0">
<UserCircle className="w-6 h-6 sm:w-8 sm:h-8 text-blue-600" />
</div>
<div className="min-w-0">
<p className="text-base sm:text-lg font-semibold text-blue-600 truncate">
{family.name}
</p>
<p className="flex items-center text-sm text-slate-500 truncate">
Interno: <span className="font-medium text-slate-700 ml-1">{family.unitNumber}</span>
</p>
</div>
</div>
<div className="flex-shrink-0">
<ChevronRight className="h-5 w-5 text-slate-400" />
</div>
</div>
</Link>
</li>
))
)}
</ul>
</div>
</div>
);
};