PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / budget-planner / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/budget-planner/frontend.js

263 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 var DEFAULT_CATEGORIES = [
5 { name:'Housing', amount:1200, icon:'🏠' },
6 { name:'Food', amount:400, icon:'🍔' },
7 { name:'Transport', amount:250, icon:'🚗' },
8 { name:'Utilities', amount:150, icon:'💡' },
9 { name:'Healthcare', amount:100, icon:'🏥' },
10 { name:'Entertainment', amount:200, icon:'🎬' }
11 ];
12
13 var BUDGETING_TIPS = [
14 'The 50/30/20 rule: spend 50% on needs, 30% on wants, and save 20%.',
15 'Build a 3–6 month emergency fund before investing.',
16 'Track every expense — even small ones add up over a month.',
17 'Pay yourself first: move savings out on payday before spending.',
18 'Review subscriptions quarterly — cancel what you no longer use.'
19 ];
20
21 function typoCssVarsForEl(typo, prefix, el) {
22 if (!typo) return;
23 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
24 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
25 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
26 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
27 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
28 var su = typo.sizeUnit || 'px';
29 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
30 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
31 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
32 var lhu = typo.lineHeightUnit || '';
33 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
34 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
35 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
36 var lsu = typo.letterSpacingUnit || 'px';
37 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
38 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
39 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
40 var wsu = typo.wordSpacingUnit || 'px';
41 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
42 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
43 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
44 }
45
46 document.querySelectorAll('.bkbg-bp-app').forEach(function(root) {
47 var opts;
48 try { opts = JSON.parse(root.getAttribute('data-opts') || '{}'); }
49 catch(e) { return; }
50
51 typoCssVarsForEl(opts.typoTitle, '--bkbg-bp-title-', root);
52 typoCssVarsForEl(opts.typoSubtitle, '--bkbg-bp-sub-', root);
53
54 var o = {
55 title: opts.title || 'Monthly Budget Planner',
56 subtitle: opts.subtitle || '',
57 currency: opts.currency || '$',
58 showSavingsGoal: opts.showSavingsGoal !== false,
59 showPieBreakdown:opts.showPieBreakdown !== false,
60 showTips: opts.showTips !== false,
61 defaultIncome: parseFloat(opts.defaultIncome) || 5000,
62 defaultSavings: parseFloat(opts.defaultSavings) || 500,
63 accentColor: opts.accentColor || '#22c55e',
64 expenseColor: opts.expenseColor || '#f43f5e',
65 savingsColor: opts.savingsColor || '#8b5cf6',
66 surplusColor: opts.surplusColor || '#0ea5e9',
67 sectionBg: opts.sectionBg || '#f0fdf4',
68 cardBg: opts.cardBg || '#ffffff',
69 inputBg: opts.inputBg || '#f9fafb',
70 titleColor: opts.titleColor || '#111827',
71 subtitleColor: opts.subtitleColor || '#6b7280',
72 labelColor: opts.labelColor || '#374151',
73 titleFontSize: parseInt(opts.titleFontSize) || 26,
74 contentMaxWidth: parseInt(opts.contentMaxWidth) || 740
75 };
76
77 var income = o.defaultIncome;
78 var savingsGoal= o.defaultSavings;
79 var categories = DEFAULT_CATEGORIES.map(function(c){ return { name:c.name, amount:c.amount, icon:c.icon }; });
80
81 function cur(n) { return o.currency + Number(n).toLocaleString(undefined, {minimumFractionDigits:0, maximumFractionDigits:2}); }
82
83 // ── Build shell ────────────────────────────────────────────────
84 root.innerHTML =
85 '<div class="bkbg-bp-wrap" style="background:' + o.sectionBg + ';max-width:' + o.contentMaxWidth + 'px;--bp-accent:' + o.accentColor + '">' +
86 (o.title ? '<h3 class="bkbg-bp-title" style="color:' + o.titleColor + '">' + o.title + '</h3>' : '') +
87 (o.subtitle ? '<p class="bkbg-bp-subtitle" style="color:' + o.subtitleColor + '">' + o.subtitle + '</p>' : '') +
88
89 // Income
90 '<div class="bkbg-bp-income-card" style="background:' + o.cardBg + '">' +
91 '<span class="bkbg-bp-income-icon">💰</span>' +
92 '<div class="bkbg-bp-income-info">' +
93 '<div class="bkbg-bp-income-caption">Monthly Income</div>' +
94 '<div class="bkbg-bp-income-value" id="bp-income-disp" style="color:' + o.accentColor + '">' + cur(income) + '</div>' +
95 '</div>' +
96 '<input id="bp-income-in" type="number" class="bkbg-bp-income-input" value="' + income + '" min="0" step="100" style="background:' + o.inputBg + ';color:' + o.labelColor + '">' +
97 '</div>' +
98
99 // Categories section
100 '<div class="bkbg-bp-section-title" style="color:' + o.labelColor + '">Expense Categories</div>' +
101 '<div class="bkbg-bp-categories" id="bp-cats"></div>' +
102
103 // Add category row
104 '<div class="bkbg-bp-add-row">' +
105 '<input id="bp-new-name" type="text" class="bkbg-bp-add-input" placeholder="Category name (e.g. Gym)" style="background:' + o.inputBg + ';color:' + o.labelColor + '">' +
106 '<input id="bp-new-amount" type="number" class="bkbg-bp-add-input" placeholder="Amount" min="0" step="10" style="background:' + o.inputBg + ';color:' + o.labelColor + ';max-width:100px">' +
107 '<button id="bp-add-cat" class="bkbg-bp-add-btn">+ Add</button>' +
108 '</div>' +
109
110 // Savings goal
111 (o.showSavingsGoal ?
112 '<div class="bkbg-bp-savings-card" style="background:' + o.cardBg + '">' +
113 '<span style="font-size:22px">🎯</span>' +
114 '<div style="flex:1"><div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:#6b7280;margin-bottom:3px">Savings Goal</div>' +
115 '<div id="bp-sav-disp" style="font-size:20px;font-weight:700;color:' + o.savingsColor + '">' + cur(savingsGoal) + '</div></div>' +
116 '<input id="bp-sav-in" type="number" class="bkbg-bp-income-input" value="' + savingsGoal + '" min="0" step="50" style="background:' + o.inputBg + ';color:' + o.labelColor + '">' +
117 '</div>'
118 : '') +
119
120 // Summary
121 '<div class="bkbg-bp-summary" id="bp-summary"></div>' +
122
123 // Allocation bar
124 (o.showPieBreakdown ? '<div class="bkbg-bp-alloc" id="bp-alloc"></div>' : '') +
125
126 // Tips
127 (o.showTips ?
128 '<div class="bkbg-bp-tips">' +
129 '<div class="bkbg-bp-tips-title">💡 Budgeting Tips</div>' +
130 '<ul class="bkbg-bp-tips-list">' +
131 BUDGETING_TIPS.map(function(t){ return '<li>' + t + '</li>'; }).join('') +
132 '</ul></div>'
133 : '') +
134 '</div>';
135
136 var catsEl = root.querySelector('#bp-cats');
137 var summaryEl = root.querySelector('#bp-summary');
138 var allocEl = root.querySelector('#bp-alloc');
139 var incomeIn = root.querySelector('#bp-income-in');
140 var incomeDisp= root.querySelector('#bp-income-disp');
141 var savIn = root.querySelector('#bp-sav-in');
142 var savDisp = root.querySelector('#bp-sav-disp');
143 var newName = root.querySelector('#bp-new-name');
144 var newAmt = root.querySelector('#bp-new-amount');
145 var addBtn = root.querySelector('#bp-add-cat');
146
147 // ── Render categories ──────────────────────────────────────────
148 function renderCategories() {
149 var totalExp = categories.reduce(function(s,c){ return s + (parseFloat(c.amount)||0); }, 0);
150 catsEl.innerHTML = categories.map(function(cat, idx) {
151 var pct = income > 0 ? Math.min(100, (cat.amount/income)*100) : 0;
152 return '<div class="bkbg-bp-cat-row" style="background:' + o.cardBg + '">' +
153 '<span class="bkbg-bp-cat-icon">' + (cat.icon||'📋') + '</span>' +
154 '<span class="bkbg-bp-cat-name" style="color:' + o.labelColor + '">' + escHtml(cat.name) + '</span>' +
155 '<div class="bkbg-bp-cat-bar-wrap">' +
156 '<div class="bkbg-bp-cat-bar-fill" style="width:' + pct.toFixed(1) + '%;background:' + o.expenseColor + '"></div>' +
157 '</div>' +
158 '<input class="bkbg-bp-cat-input" type="number" data-idx="' + idx + '" value="' + cat.amount + '" min="0" step="10" style="background:' + o.inputBg + ';color:' + o.labelColor + '">' +
159 '<button class="bkbg-bp-del-cat" data-idx="' + idx + '" style="background:none;border:none;font-size:16px;cursor:pointer;color:#9ca3af;padding:0 2px" title="Remove">✕</button>' +
160 '</div>';
161 }).join('');
162
163 // bind inputs
164 catsEl.querySelectorAll('.bkbg-bp-cat-input').forEach(function(inp) {
165 inp.addEventListener('input', function() {
166 var idx = parseInt(inp.getAttribute('data-idx'));
167 categories[idx].amount = parseFloat(inp.value) || 0;
168 renderSummary();
169 });
170 });
171 catsEl.querySelectorAll('.bkbg-bp-del-cat').forEach(function(btn) {
172 btn.addEventListener('click', function() {
173 var idx = parseInt(btn.getAttribute('data-idx'));
174 categories.splice(idx, 1);
175 renderCategories();
176 renderSummary();
177 });
178 });
179 }
180
181 // ── Render summary ─────────────────────────────────────────────
182 function renderSummary() {
183 var totalExp = categories.reduce(function(s,c){ return s + (parseFloat(c.amount)||0); }, 0);
184 var sav = o.showSavingsGoal ? savingsGoal : 0;
185 var surplus = income - totalExp - sav;
186 var surColor = surplus >= 0 ? o.surplusColor : o.expenseColor;
187 var surLabel = surplus >= 0 ? 'Surplus' : 'Deficit';
188
189 var cards = [
190 { label:'Total Income', value: cur(income), color: o.accentColor },
191 { label:'Total Expenses', value: cur(totalExp), color: o.expenseColor },
192 { label:'Savings Goal', value: cur(sav), color: o.savingsColor, hide: !o.showSavingsGoal },
193 { label: surLabel, value: (surplus>=0?'+':'-') + cur(Math.abs(surplus)), color: surColor }
194 ].filter(function(c){ return !c.hide; });
195
196 summaryEl.innerHTML = cards.map(function(c) {
197 return '<div class="bkbg-bp-sum-card" style="background:' + o.cardBg + ';border-left-color:' + c.color + '">' +
198 '<div class="bkbg-bp-sum-label">' + c.label + '</div>' +
199 '<div class="bkbg-bp-sum-value" style="color:' + c.color + '">' + c.value + '</div>' +
200 '</div>';
201 }).join('');
202
203 // Update income display
204 if (incomeDisp) incomeDisp.textContent = cur(income);
205 if (savDisp) savDisp.textContent = cur(savingsGoal);
206
207 // Allocation bar
208 if (allocEl && o.showPieBreakdown) {
209 var expPct = income > 0 ? Math.min(100, (totalExp/income)*100) : 0;
210 var savPct = income > 0 ? Math.min(100-expPct, (sav/income)*100): 0;
211 var surPct = Math.max(0, 100 - expPct - savPct);
212 allocEl.innerHTML =
213 '<div class="bkbg-bp-alloc-heading" style="color:' + o.labelColor + '">Income Allocation</div>' +
214 '<div class="bkbg-bp-alloc-bar">' +
215 '<div class="bkbg-bp-alloc-seg" style="width:' + expPct.toFixed(1) + '%;background:' + o.expenseColor + '"></div>' +
216 '<div class="bkbg-bp-alloc-seg" style="width:' + savPct.toFixed(1) + '%;background:' + o.savingsColor + '"></div>' +
217 '<div class="bkbg-bp-alloc-seg" style="width:' + surPct.toFixed(1) + '%;background:' + o.surplusColor + '"></div>' +
218 '</div>' +
219 '<div class="bkbg-bp-alloc-legend">' +
220 '<span style="color:' + o.expenseColor + '">● Expenses ' + Math.round(expPct) + '%</span>' +
221 '<span style="color:' + o.savingsColor + '">● Savings ' + Math.round(savPct) + '%</span>' +
222 '<span style="color:' + o.surplusColor + '">● Surplus ' + Math.round(surPct) + '%</span>' +
223 '</div>';
224 }
225 }
226
227 function escHtml(s) {
228 return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
229 }
230
231 // ── Event bindings ─────────────────────────────────────────────
232 if (incomeIn) {
233 incomeIn.addEventListener('input', function() {
234 income = parseFloat(incomeIn.value) || 0;
235 renderCategories();
236 renderSummary();
237 });
238 }
239 if (savIn) {
240 savIn.addEventListener('input', function() {
241 savingsGoal = parseFloat(savIn.value) || 0;
242 renderSummary();
243 });
244 }
245 if (addBtn) {
246 addBtn.addEventListener('click', function() {
247 var name = newName ? newName.value.trim() : '';
248 var amt = newAmt ? parseFloat(newAmt.value) || 0 : 0;
249 if (!name) { if (newName) newName.focus(); return; }
250 categories.push({ name: name, amount: amt, icon: '📋' });
251 if (newName) newName.value = '';
252 if (newAmt) newAmt.value = '';
253 renderCategories();
254 renderSummary();
255 });
256 }
257
258 // ── Initial render ─────────────────────────────────────────────
259 renderCategories();
260 renderSummary();
261 });
262 })();
263