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:
@@ -105,12 +105,29 @@ export default function ServicesPage() {
|
||||
</div>
|
||||
<div>
|
||||
<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 className="text-right">
|
||||
<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>
|
||||
)
|
||||
|
||||
@@ -22,11 +22,24 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
|
||||
|
||||
const [type, setType] = useState('electricity')
|
||||
const [amount, setAmount] = useState('')
|
||||
const [usage, setUsage] = useState('')
|
||||
const [period, setPeriod] = useState(new Date().toISOString().slice(0, 7)) // YYYY-MM
|
||||
const [date, setDate] = useState(new Date().toISOString().split('T')[0])
|
||||
|
||||
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) => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -35,6 +48,8 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
|
||||
addServiceBill({
|
||||
type: type as any,
|
||||
amount: parseFloat(amount),
|
||||
usage: usage ? parseFloat(usage) : undefined,
|
||||
unit: unit || undefined,
|
||||
date: new Date(date).toISOString(),
|
||||
period: period,
|
||||
notes: ''
|
||||
@@ -42,6 +57,7 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
|
||||
|
||||
// Reset
|
||||
setAmount('')
|
||||
setUsage('')
|
||||
onClose()
|
||||
}
|
||||
|
||||
@@ -81,22 +97,42 @@ export function AddServiceModal({ isOpen, onClose }: AddServiceModalProps) {
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Amount */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Monto Factura</label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 font-semibold">$</span>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="0.00"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
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"
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Amount */}
|
||||
<div className={cn("space-y-2", !showUsage && "col-span-2")}>
|
||||
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Monto</label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 font-semibold">$</span>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="0.00"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
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"
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
</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 className="grid grid-cols-2 gap-4">
|
||||
|
||||
@@ -66,6 +66,8 @@ export interface ServiceBill {
|
||||
id: string
|
||||
type: 'electricity' | 'water' | 'gas' | 'internet'
|
||||
amount: number
|
||||
usage?: number
|
||||
unit?: string
|
||||
date: string
|
||||
period: string // YYYY-MM
|
||||
isPaid: boolean
|
||||
|
||||
Reference in New Issue
Block a user