Spread the love

import React, { useMemo, useState } from „react“; import { motion } from „framer-motion“; import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, BarChart, Bar, XAxis, YAxis, Legend, CartesianGrid } from „recharts“; import { Bike, Bus, Leaf, PlugZap, Droplets, Flame, Home, Car, ShoppingBag, Soup, Waves, Wallet, Settings2, Plus, RefreshCw } from „lucide-react“; // shadcn/ui import { Card, CardContent, CardHeader, CardTitle } from „@/components/ui/card“; import { Button } from „@/components/ui/button“; import { Input } from „@/components/ui/input“; import { Label } from „@/components/ui/label“; import { Slider } from „@/components/ui/slider“; import { Switch } from „@/components/ui/switch“; import { Tabs, TabsContent, TabsList, TabsTrigger } from „@/components/ui/tabs“; import { Separator } from „@/components/ui/separator“; import { Badge } from „@/components/ui/badge“; // –––––––– // Помощни типове и данни // –––––––– type LineItem = { key: string; label: string; icon?: React.ReactNode; monthly: number; // базов разход в лева (лв.) category: string; editable?: boolean; }; type Action = { key: string; label: string; desc: string; enabled: boolean; intensity?: number; // 0..100 (%), ако е с плъзгач }; const CURRENCY = „лв.“; const COLORS = [ „#0ea5e9“, „#22c55e“, „#eab308“, „#f97316“, „#a855f7“, „#ef4444“, „#14b8a6“, „#94a3b8“, ]; // Предефинирани сценарии (примерни стойности – редактират се от потребителя) const PRESETS: Record = { „Сам човек в голям град“: [ { key: „rent“, label: „Наем/Кредит“, category: „Дом“, monthly: 1200, icon: }, { key: „electricity“, label: „Електроенергия“, category: „Комунални“, monthly: 140, icon: }, { key: „heating“, label: „Отопление/Газ“, category: „Комунални“, monthly: 160, icon: }, { key: „water“, label: „Вода“, category: „Комунални“, monthly: 35, icon: }, { key: „internet“, label: „Интернет/ТВ“, category: „Комуникации“, monthly: 45, icon: }, { key: „mobile“, label: „Мобилен план“, category: „Комуникации“, monthly: 30, icon: }, { key: „fuel“, label: „Гориво/Паркиране“, category: „Транспорт“, monthly: 260, icon: }, { key: „public“, label: „Обществен транспорт“, category: „Транспорт“, monthly: 60, icon: }, { key: „bike“, label: „Поддръжка велосипед/тротинетка“, category: „Транспорт“, monthly: 15, icon: }, { key: „groceries“, label: „Храна – магазин“, category: „Храна“, monthly: 420, icon: }, { key: „eatingout“, label: „Храна навън/кафета“, category: „Храна“, monthly: 180, icon: }, { key: „clothes“, label: „Дрехи/лични“, category: „Пазаруване“, monthly: 120, icon: }, { key: „household“, label: „Дом/консумативи“, category: „Пазаруване“, monthly: 90, icon: }, { key: „subs“, label: „Абонаменти/стрийминг/софтуер“, category: „Забавления“, monthly: 40, icon: }, { key: „leisure“, label: „Свободно време/събития“, category: „Забавления“, monthly: 120, icon: }, { key: „health“, label: „Здраве/лекарства“, category: „Здраве“, monthly: 60, icon: }, ], „Двама възрастни“: [ { key: „rent“, label: „Наем/Кредит“, category: „Дом“, monthly: 1600, icon: }, { key: „electricity“, label: „Електроенергия“, category: „Комунални“, monthly: 190, icon: }, { key: „heating“, label: „Отопление/Газ“, category: „Комунални“, monthly: 210, icon: }, { key: „water“, label: „Вода“, category: „Комунални“, monthly: 50, icon: }, { key: „internet“, label: „Интернет/ТВ“, category: „Комуникации“, monthly: 55, icon: }, { key: „mobile“, label: „Мобилни планове (2)“, category: „Комуникации“, monthly: 60, icon: }, { key: „fuel“, label: „Гориво/Паркиране“, category: „Транспорт“, monthly: 340, icon: }, { key: „public“, label: „Обществен транспорт“, category: „Транспорт“, monthly: 100, icon: }, { key: „bike“, label: „Поддръжка велосипеди“, category: „Транспорт“, monthly: 20, icon: }, { key: „groceries“, label: „Храна – магазин“, category: „Храна“, monthly: 650, icon: }, { key: „eatingout“, label: „Храна навън/кафета“, category: „Храна“, monthly: 260, icon: }, { key: „clothes“, label: „Дрехи/лични“, category: „Пазаруване“, monthly: 200, icon: }, { key: „household“, label: „Дом/консумативи“, category: „Пазаруване“, monthly: 140, icon: }, { key: „subs“, label: „Абонаменти/стрийминг/софтуер“, category: „Забавления“, monthly: 60, icon: }, { key: „leisure“, label: „Свободно време/събития“, category: „Забавления“, monthly: 200, icon: }, { key: „health“, label: „Здраве/лекарства“, category: „Здраве“, monthly: 100, icon: }, ], „Семейство с дете“: [ { key: „rent“, label: „Наем/Кредит“, category: „Дом“, monthly: 1800, icon: }, { key: „electricity“, label: „Електроенергия“, category: „Комунални“, monthly: 210, icon: }, { key: „heating“, label: „Отопление/Газ“, category: „Комунални“, monthly: 240, icon: }, { key: „water“, label: „Вода“, category: „Комунални“, monthly: 65, icon: }, { key: „internet“, label: „Интернет/ТВ“, category: „Комуникации“, monthly: 55, icon: }, { key: „mobile“, label: „Мобилни планове (2-3)“, category: „Комуникации“, monthly: 80, icon: }, { key: „fuel“, label: „Гориво/Паркиране“, category: „Транспорт“, monthly: 420, icon: }, { key: „public“, label: „Обществен транспорт“, category: „Транспорт“, monthly: 120, icon: }, { key: „bike“, label: „Поддръжка велосипеди/детски“, category: „Транспорт“, monthly: 25, icon: }, { key: „groceries“, label: „Храна – магазин“, category: „Храна“, monthly: 900, icon: }, { key: „eatingout“, label: „Храна навън/кафета“, category: „Храна“, monthly: 320, icon: }, { key: „clothes“, label: „Дрехи/лични“, category: „Пазаруване“, monthly: 260, icon: }, { key: „household“, label: „Дом/консумативи (вкл. бебешки)“, category: „Пазаруване“, monthly: 220, icon: }, { key: „subs“, label: „Абонаменти/стрийминг/софтуер“, category: „Забавления“, monthly: 70, icon: }, { key: „leisure“, label: „Свободно време/събития“, category: „Забавления“, monthly: 260, icon: }, { key: „health“, label: „Здраве/лекарства“, category: „Здраве“, monthly: 140, icon: }, ], }; // Еко-действия: дефинираме кои редове засягат и как (процент намаление) const ACTION_RULES: Record = { energy_saving: { affects: [„electricity“], maxPct: 25 }, heat_down: { affects: [„heating“], maxPct: 15 }, water_saving: { affects: [„water“], maxPct: 20 }, transit_shift: { affects: [„fuel“], maxPct: 90 }, bike_commute: { affects: [„fuel“], maxPct: 40 }, eat_plant: { affects: [„groceries“, „eatingout“], maxPct: 20 }, zero_waste: { affects: [„household“], maxPct: 25 }, thrifting: { affects: [„clothes“], maxPct: 50 }, subs_trim: { affects: [„subs“], maxPct: 60 }, dine_less: { affects: [„eatingout“], maxPct: 70 }, wfh: { affects: [„fuel“, „eatingout“], maxPct: 40 }, }; const DEFAULT_ACTIONS: Action[] = [ { key: „energy_saving“, label: „Енергийно-ефективни навици (LED, изключване на уреди)“, desc: „Намалява електроенергията“, enabled: true, intensity: 60 }, { key: „heat_down“, label: „Термостат -1 до -2°C“, desc: „Намалява отоплението“, enabled: false, intensity: 40 }, { key: „water_saving“, label: „Пестене на вода (перлатори, кратък душ)“, desc: „Намалява водата“, enabled: true, intensity: 40 }, { key: „transit_shift“, label: „Повече обществен транспорт“, desc: „Малко/никакво гориво“, enabled: false, intensity: 30 }, { key: „bike_commute“, label: „Колоездене/пеша за близки дистанции“, desc: „Още по-малко гориво“, enabled: true, intensity: 40 }, { key: „wfh“, label: „Работа от вкъщи няколко дни“, desc: „По-малко гориво и обяд навън“, enabled: false, intensity: 30 }, { key: „eat_plant“, label: „Повече растителна храна“, desc: „Намалява храната и навън“, enabled: true, intensity: 25 }, { key: „dine_less“, label: „По-рядко хранене навън“, desc: „Намалява разхода навън“, enabled: false, intensity: 30 }, { key: „zero_waste“, label: „Нулеви отпадъци/насипни продукти“, desc: „Намалява консумативи“, enabled: true, intensity: 30 }, { key: „thrifting“, label: „Втора употреба/ремонт на дрехи“, desc: „Намалява дрехи“, enabled: true, intensity: 50 }, { key: „subs_trim“, label: „Оптимизация на абонаменти“, desc: „Намалява абонаменти“, enabled: true, intensity: 50 }, ]; function formatCurrency(x: number) { return `${x.toFixed(0)} ${CURRENCY}`; } export default function EcoSavingsCalculatorBG() { const [items, setItems] = useState(PRESETS[„Сам човек в голям град“]); const [actions, setActions] = useState(DEFAULT_ACTIONS); const [customName, setCustomName] = useState(„“); const [customValue, setCustomValue] = useState(„“); const [customCategory, setCustomCategory] = useState(„Други“); const [preset, setPreset] = useState(„Сам човек в голям град“); const categories = useMemo( () => Array.from(new Set(items.map((i) => i.category))), [items] ); const baseTotal = useMemo( () => items.reduce((sum, i) => sum + i.monthly, 0), [items] ); const savingsByAction = useMemo(() => { const map: Record = {}; actions.forEach((a) => { if (!a.enabled) return; const rule = ACTION_RULES[a.key]; if (!rule) return; const pct = ((a.intensity ?? 0) / 100) * rule.maxPct; // реално прилаган процент rule.affects.forEach((k) => { const line = items.find((it) => it.key === k); if (!line) return; const save = (line.monthly * pct) / 100; map[k] = (map[k] || 0) + save; }); }); return map; // по ключ на ред }, [actions, items]); const ecoItems = useMemo(() => { return items.map((i) => ({ …i, monthly: Math.max(0, i.monthly – (savingsByAction[i.key] || 0)), })); }, [items, savingsByAction]); const ecoTotal = useMemo( () => ecoItems.reduce((sum, i) => sum + i.monthly, 0), [ecoItems] ); const totalSavings = Math.max(0, baseTotal – ecoTotal); function applyPreset(name: string) { setPreset(name); setItems(PRESETS[name].map((x) => ({ …x }))); } function updateItem(key: string, value: number) { setItems((prev) => prev.map((i) => (i.key === key ? { …i, monthly: value } : i))); } function addCustomItem() { const v = Number(customValue); if (!customName || isNaN(v)) return; const newItem: LineItem = { key: `${Date.now()}`, label: customName, category: customCategory || „Други“, monthly: Math.max(0, v), editable: true, }; setItems((prev) => […prev, newItem]); setCustomName(„“); setCustomValue(„“); } function resetActions() { setActions(DEFAULT_ACTIONS.map((a) => ({ …a }))); } const chartData = useMemo(() => { const byCatBase: Record = {}; const byCatEco: Record = {}; items.forEach((i) => (byCatBase[i.category] = (byCatBase[i.category] || 0) + i.monthly)); ecoItems.forEach((i) => (byCatEco[i.category] = (byCatEco[i.category] || 0) + i.monthly)); return categories.map((c) => ({ category: c, „База“: byCatBase[c] || 0, „Еко“: byCatEco[c] || 0 })); }, [items, ecoItems, categories]); const pieData = useMemo(() => { return [ { name: „Базов бюджет“, value: baseTotal }, { name: „Еко бюджет“, value: ecoTotal }, { name: „Спестявания“, value: totalSavings }, ]; }, [baseTotal, ecoTotal, totalSavings]); return (
Еко Спестявания: колко можете да спестите месечно?

Настройте реалните си разходи и включете природосъобразни навици. Калкулаторът ще изчисли потенциалните спестявания на месечна и годишна база.

{/* Лява колона: Настройки и разходи */}
1) Базови разходи
{categories.map((c) => ( {c} ))} {categories.map((c) => (
{items.filter((i) => i.category === c).map((it) => (
{it.icon} {it.label}
updateItem(it.key, Number(e.target.value))} /> {CURRENCY}
))}
))}
setCustomName(e.target.value)} />
setCustomCategory(e.target.value)} />
setCustomValue(e.target.value)} />
2) Еко-действия

Включете навици и настройки. Плъзгачът управлява интензитета (реален ефект до максималния процент).

{actions.map((a, idx) => (
setActions((prev) => prev.map((x) => x.key === a.key ? { …x, enabled: v } : x))} /> {a.label}

{a.desc} · до {ACTION_RULES[a.key].maxPct}%

setActions((prev) => prev.map((x) => x.key === a.key ? { …x, intensity: v[0] } : x))} />
))}
Забележка: Процентите са ориентировъчни и зависят от жилище, навици и градска среда. Всички стойности са напълно редактиратеми.


Spread the love