Merge pull request 'Remove duplicate utility code via shared formatting module' (#1) from dead-code/remove-duplicate-utilities into master

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-03-20 08:31:11 -07:00
9 changed files with 53 additions and 94 deletions

View File

@@ -3,32 +3,10 @@ import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend,
Cell, ResponsiveContainer, ReferenceLine,
} from 'recharts';
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
];
import { MONTH_NAMES, PALETTE, fmt, formatCurrencyShort } from '../utils/formatting.js';
const MONTH_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const PALETTE = ['#3b82f6', '#8b5cf6', '#ec4899', '#f97316', '#22c55e', '#14b8a6', '#eab308', '#64748b'];
function fmt(value) {
if (value == null) return '—';
const num = Number(value);
if (isNaN(num)) return '—';
const abs = Math.abs(num).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
return num < 0 ? `-$${abs}` : `$${abs}`;
}
function formatCurrencyShort(value) {
if (value == null || isNaN(value)) return '';
const abs = Math.abs(value);
const sign = value < 0 ? '-' : '';
if (abs >= 1000) return `${sign}$${(abs / 1000).toFixed(1)}k`;
return `${sign}$${abs.toFixed(0)}`;
}
function surplusClass(value) {
if (value == null || isNaN(Number(value))) return '';
return Number(value) >= 0 ? 'text-success' : 'text-danger';

View File

@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import { formatCurrency, ordinal } from '../utils/formatting.js';
const CATEGORIES = [
'Housing', 'Utilities', 'Subscriptions', 'Insurance',
@@ -14,19 +15,6 @@ const EMPTY_FORM = {
variable_amount: false,
};
function formatCurrency(value) {
const num = parseFloat(value);
if (isNaN(num)) return '$0.00';
return num.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
}
function ordinal(n) {
const int = parseInt(n, 10);
if (isNaN(int)) return n;
const suffix = ['th', 'st', 'nd', 'rd'];
const v = int % 100;
return int + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
}
function Bills() {
const [bills, setBills] = useState([]);

View File

@@ -1,9 +1,5 @@
import { useState, useEffect } from 'react';
function fmt(value) {
const num = parseFloat(value) || 0;
return '$' + num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
import { fmt } from '../utils/formatting.js';
function ProgressBar({ paid, total }) {
const pct = total > 0 ? Math.min(100, (paid / total) * 100) : 0;

View File

@@ -3,25 +3,7 @@ import {
PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer,
BarChart, Bar, XAxis, YAxis, CartesianGrid,
} from 'recharts';
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
];
function formatCurrency(value) {
const num = parseFloat(value) || 0;
return '$' + num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function formatCurrencyShort(value) {
const num = parseFloat(value) || 0;
if (Math.abs(num) >= 1000) return '$' + (num / 1000).toFixed(1) + 'k';
return '$' + num.toFixed(0);
}
// Accessible palette that works in light and dark
const PALETTE = ['#3b82f6', '#8b5cf6', '#ec4899', '#f97316', '#22c55e', '#14b8a6', '#eab308', '#64748b'];
import { MONTH_NAMES, PALETTE, formatCurrency, formatCurrencyShort } from '../utils/formatting.js';
function StatCard({ label, value, valueClass }) {
return (

View File

@@ -1,20 +1,5 @@
import { useState, useEffect } from 'react';
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
];
function ordinal(n) {
const s = ['th', 'st', 'nd', 'rd'];
const v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
}
function formatCurrency(value) {
const num = parseFloat(value) || 0;
return '$' + num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
import { MONTH_NAMES, formatCurrency, ordinal } from '../utils/formatting.js';
function formatPayDate(dateStr) {
const [year, month, day] = dateStr.split('-').map(Number);

View File

@@ -0,0 +1,34 @@
export const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
];
// Accessible palette that works in light and dark
export const PALETTE = ['#3b82f6', '#8b5cf6', '#ec4899', '#f97316', '#22c55e', '#14b8a6', '#eab308', '#64748b'];
export function formatCurrency(value) {
const num = parseFloat(value) || 0;
return '$' + num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
export function formatCurrencyShort(value) {
if (value == null || isNaN(value)) return '';
const abs = Math.abs(value);
const sign = value < 0 ? '-' : '';
if (abs >= 1000) return `${sign}$${(abs / 1000).toFixed(1)}k`;
return `${sign}$${abs.toFixed(0)}`;
}
export function ordinal(n) {
const s = ['th', 'st', 'nd', 'rd'];
const v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
}
export function fmt(value) {
if (value == null) return '—';
const num = Number(value);
if (isNaN(num)) return '—';
const abs = Math.abs(num).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
return num < 0 ? `-$${abs}` : `$${abs}`;
}

12
server/src/constants.js Normal file
View File

@@ -0,0 +1,12 @@
'use strict';
const CONFIG_KEYS = [
'paycheck1_day',
'paycheck2_day',
'paycheck1_gross',
'paycheck1_net',
'paycheck2_gross',
'paycheck2_net',
];
module.exports = { CONFIG_KEYS };

View File

@@ -1,15 +1,7 @@
const express = require('express');
const router = express.Router();
const { pool } = require('../db');
const CONFIG_KEYS = [
'paycheck1_day',
'paycheck2_day',
'paycheck1_gross',
'paycheck1_net',
'paycheck2_gross',
'paycheck2_net',
];
const { CONFIG_KEYS } = require('../constants');
const DEFAULTS = {
paycheck1_day: 1,

View File

@@ -2,15 +2,7 @@ const express = require('express');
const router = express.Router();
const { pool } = require('../db');
const { calcPaymentAmount } = require('./financing');
const CONFIG_KEYS = [
'paycheck1_day',
'paycheck2_day',
'paycheck1_gross',
'paycheck1_net',
'paycheck2_gross',
'paycheck2_net',
];
const { CONFIG_KEYS } = require('../constants');
const CONFIG_DEFAULTS = {
paycheck1_day: 1,