feat: add consumption tracking for services

Enhanced services module with comprehensive consumption tracking:
- Add usage field to ServiceBill interface (optional number)
- Add unit field to ServiceBill interface (kW, m³, or empty for internet)
- Updated AddServiceModal with consumption input field
- Auto-detect unit based on service type (electricity=kW, gas/water=m³, internet=none)
- Responsive grid layout for amount and consumption inputs
- Display consumption and unit in history list
- Show price per unit in history (amount/usage)
- Improved date display with responsive layout

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
renato97
2026-01-29 01:00:21 +00:00
parent d27aa6a9a7
commit 4ba5841839
3 changed files with 72 additions and 17 deletions

View File

@@ -105,12 +105,29 @@ export default function ServicesPage() {
</div> </div>
<div> <div>
<p className="text-white font-medium capitalize">{service?.label || bill.type}</p> <p className="text-white font-medium capitalize">{service?.label || bill.type}</p>
<p className="text-xs text-slate-500 capitalize">{new Date(bill.date).toLocaleDateString('es-AR', { dateStyle: 'long' })}</p> <div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<p className="text-xs text-slate-500 capitalize">{new Date(bill.date).toLocaleDateString('es-AR', { dateStyle: 'long' })}</p>
{bill.usage && (
<>
<span className="hidden sm:inline text-slate-700"></span>
<p className="text-xs text-slate-400">
Consumo: <span className="text-slate-300 font-medium">{bill.usage} {bill.unit}</span>
</p>
</>
)}
</div>
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right">
<p className="text-white font-mono font-medium">{formatCurrency(bill.amount)}</p> <p className="text-white font-mono font-medium">{formatCurrency(bill.amount)}</p>
<p className="text-xs text-slate-500 uppercase">{bill.period}</p> <div className="flex flex-col items-end">
<p className="text-xs text-slate-500 uppercase">{bill.period}</p>
{bill.usage && bill.amount && (
<p className="text-[10px] text-cyan-500/80 font-mono">
{formatCurrency(bill.amount / bill.usage)} / {bill.unit}
</p>
)}
</div>
</div> </div>
</div> </div>
) )

View File

@@ -22,11 +22,24 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
const [type, setType] = useState('electricity') const [type, setType] = useState('electricity')
const [amount, setAmount] = useState('') const [amount, setAmount] = useState('')
const [usage, setUsage] = useState('')
const [period, setPeriod] = useState(new Date().toISOString().slice(0, 7)) // YYYY-MM const [period, setPeriod] = useState(new Date().toISOString().slice(0, 7)) // YYYY-MM
const [date, setDate] = useState(new Date().toISOString().split('T')[0]) const [date, setDate] = useState(new Date().toISOString().split('T')[0])
if (!isOpen) return null if (!isOpen) return null
const getUnit = (serviceType: string) => {
switch (serviceType) {
case 'electricity': return 'kW'
case 'gas': return 'm³'
case 'water': return 'm³'
default: return ''
}
}
const unit = getUnit(type)
const showUsage = type !== 'internet'
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
@@ -35,6 +48,8 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
addServiceBill({ addServiceBill({
type: type as any, type: type as any,
amount: parseFloat(amount), amount: parseFloat(amount),
usage: usage ? parseFloat(usage) : undefined,
unit: unit || undefined,
date: new Date(date).toISOString(), date: new Date(date).toISOString(),
period: period, period: period,
notes: '' notes: ''
@@ -42,6 +57,7 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
// Reset // Reset
setAmount('') setAmount('')
setUsage('')
onClose() onClose()
} }
@@ -81,22 +97,42 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
})} })}
</div> </div>
{/* Amount */} <div className="grid grid-cols-2 gap-4">
<div className="space-y-2"> {/* Amount */}
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Monto Factura</label> <div className={cn("space-y-2", !showUsage && "col-span-2")}>
<div className="relative"> <label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Monto</label>
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 font-semibold">$</span> <div className="relative">
<input <span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 font-semibold">$</span>
type="number" <input
step="0.01" type="number"
placeholder="0.00" step="0.01"
value={amount} placeholder="0.00"
onChange={(e) => setAmount(e.target.value)} value={amount}
className="w-full pl-8 pr-4 py-3 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white text-lg font-mono outline-none" onChange={(e) => setAmount(e.target.value)}
required className="w-full pl-8 pr-4 py-3 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white text-lg font-mono outline-none"
autoFocus required
/> autoFocus
/>
</div>
</div> </div>
{/* Usage */}
{showUsage && (
<div className="space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Consumo ({unit})</label>
<div className="relative">
<input
type="number"
step="0.01"
placeholder="0"
value={usage}
onChange={(e) => setUsage(e.target.value)}
className="w-full px-4 py-3 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white text-lg font-mono outline-none"
/>
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-slate-500 text-sm font-medium">{unit}</span>
</div>
</div>
)}
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">

View File

@@ -66,6 +66,8 @@ export interface ServiceBill {
id: string id: string
type: 'electricity' | 'water' | 'gas' | 'internet' type: 'electricity' | 'water' | 'gas' | 'internet'
amount: number amount: number
usage?: number
unit?: string
date: string date: string
period: string // YYYY-MM period: string // YYYY-MM
isPaid: boolean isPaid: boolean