Skip to content

Commit 922e370

Browse files
author
Evan Tobin
committed
add the ability to have pretax deductions like 401k
1 parent 0480e44 commit 922e370

5 files changed

Lines changed: 44 additions & 15 deletions

File tree

src/components/PayPeriodCalendar.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
4141
null,
4242
);
4343
const [editingAmount, setEditingAmount] = useState<string>("");
44+
const [editingDeductions, setEditingDeductions] = useState<string>("");
4445
const [currentMonth, setCurrentMonth] = useState<Date>(() => {
4546
// Initialize with the first month of the visiting dates
4647
if (formData.visitingDates?.start) {
@@ -202,6 +203,7 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
202203
const newPayPeriod: PayPeriod = {
203204
id: Date.now(),
204205
netPay: "",
206+
pretaxDeductions: "",
205207
payPeriodStart: toISODateString(startDate),
206208
payPeriodEnd: toISODateString(endDate),
207209
};
@@ -235,6 +237,7 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
235237
if (payPeriod) {
236238
setSelectedPeriod(day.payPeriodId);
237239
setEditingAmount(payPeriod.netPay.toString());
240+
setEditingDeductions(payPeriod.pretaxDeductions?.toString() || "");
238241
}
239242
}
240243
};
@@ -248,10 +251,12 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
248251
);
249252
if (periodIndex !== -1) {
250253
draft.payPeriods[periodIndex].netPay = editingAmount;
254+
draft.payPeriods[periodIndex].pretaxDeductions = editingDeductions;
251255
}
252256
});
253257
setSelectedPeriod(null);
254258
setEditingAmount("");
259+
setEditingDeductions("");
255260
handleToastOnly({ text: "Pay period amount updated.", type: "success" });
256261
}
257262
};
@@ -445,14 +450,17 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
445450
<div className="font-medium text-zinc-700 dark:text-zinc-300">
446451
{day.date.getDate()}
447452
</div>
448-
{hasPayPeriod && (
449-
<div className="text-[10px] text-zinc-600 dark:text-zinc-400 mt-0.5">
450-
$
451-
{formData.payPeriods?.find(
452-
(p) => p.id === day.payPeriodId,
453-
)?.netPay || "0"}
454-
</div>
455-
)}
453+
{hasPayPeriod && (() => {
454+
const pp = formData.payPeriods?.find((p) => p.id === day.payPeriodId);
455+
const gross = Number(pp?.netPay || 0);
456+
const deductions = Number(pp?.pretaxDeductions || 0);
457+
const net = gross - deductions;
458+
return (
459+
<div className="text-[10px] text-zinc-600 dark:text-zinc-400 mt-0.5">
460+
${net.toFixed(2)}
461+
</div>
462+
);
463+
})()}
456464
</div>
457465
);
458466
})}
@@ -481,8 +489,10 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
481489
</span>
482490
</div>
483491
<div className="flex items-center gap-2">
484-
<span className="font-medium text-zinc-800 dark:text-zinc-200">
485-
${period.netPay || "0"}
492+
<span className="text-[10px] text-zinc-600 dark:text-zinc-400">
493+
Gross: ${period.netPay || "0"}
494+
{Number(period.pretaxDeductions || 0) > 0 && ` - $${period.pretaxDeductions} = `}
495+
{Number(period.pretaxDeductions || 0) > 0 && `$${(Number(period.netPay || 0) - Number(period.pretaxDeductions || 0)).toFixed(2)}`}
486496
</span>
487497
<button
488498
onClick={() => handleDeletePeriod(period.id)}
@@ -523,7 +533,7 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
523533
<div className="space-y-4">
524534
<div>
525535
<label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
526-
Amount ($)
536+
Gross Amount ($)
527537
</label>
528538
<input
529539
type="number"
@@ -534,6 +544,18 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
534544
autoFocus
535545
/>
536546
</div>
547+
<div>
548+
<label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
549+
Pretax Deductions ($)
550+
</label>
551+
<input
552+
type="number"
553+
step="0.01"
554+
className="w-full px-3 py-2 border border-zinc-300 dark:border-zinc-600 rounded-md bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white"
555+
value={editingDeductions}
556+
onChange={(e) => setEditingDeductions(e.target.value)}
557+
/>
558+
</div>
537559
<div className="flex gap-2">
538560
<button
539561
onClick={handleSaveAmount}
@@ -545,6 +567,7 @@ export const PayPeriodCalendar: React.FC<PayPeriodCalendarProps> = () => {
545567
onClick={() => {
546568
setSelectedPeriod(null);
547569
setEditingAmount("");
570+
setEditingDeductions("");
548571
}}
549572
className="flex-1 bg-zinc-300 dark:bg-zinc-600 text-zinc-700 dark:text-zinc-300 px-4 py-2 rounded-md hover:bg-zinc-400 dark:hover:bg-zinc-500"
550573
>

src/components/PayPeriodsSection.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const PayPeriodsSection: React.FC<PayPeriodsSectionProps> = ({
4848
payPeriods.push({
4949
id: `biweekly_${periodId}`,
5050
netPay: '',
51+
pretaxDeductions: '',
5152
payPeriodStart: current.toISOString().slice(0, 10),
5253
payPeriodEnd: periodEnd.toISOString().slice(0, 10)
5354
});
@@ -103,6 +104,7 @@ export const PayPeriodsSection: React.FC<PayPeriodsSectionProps> = ({
103104
payPeriods.push({
104105
id: `semi_monthly_1_${year}_${month + 1}`,
105106
netPay: '',
107+
pretaxDeductions: '',
106108
payPeriodStart: period1Start.toISOString().slice(0, 10),
107109
payPeriodEnd: period1End.toISOString().slice(0, 10)
108110
});
@@ -113,6 +115,7 @@ export const PayPeriodsSection: React.FC<PayPeriodsSectionProps> = ({
113115
payPeriods.push({
114116
id: `semi_monthly_2_${year}_${month + 1}`,
115117
netPay: '',
118+
pretaxDeductions: '',
116119
payPeriodStart: period2Start.toISOString().slice(0, 10),
117120
payPeriodEnd: period2End.toISOString().slice(0, 10)
118121
});

src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export const exampleData: FormData = {
1212
visitingDates: { start: "2025-01-01", end: "2025-05-31" },
1313
daysInOtherStates: { "FL": ["2025-01-16", "2025-01-17", "2025-02-10"], "AZ": [] },
1414
payPeriods: [
15-
{ id: 1, netPay: 8500.00, payPeriodStart: "2025-01-15", payPeriodEnd: "2025-01-31" },
16-
{ id: 2, netPay: 8650.00, payPeriodStart: "2025-02-01", payPeriodEnd: "2025-02-15" }
15+
{ id: 1, netPay: 8500.00, pretaxDeductions: 500, payPeriodStart: "2025-01-15", payPeriodEnd: "2025-01-31" },
16+
{ id: 2, netPay: 8650.00, pretaxDeductions: 500, payPeriodStart: "2025-02-01", payPeriodEnd: "2025-02-15" }
1717
],
1818
bonuses: [
1919
{ id: 1, amount: 1000, state: "MN", date: "2025-01-20", type: "services-rendered" },

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface VisitingDates {
88
export interface PayPeriod {
99
id: number | string;
1010
netPay: number | string;
11+
pretaxDeductions: number | string;
1112
payPeriodStart: string;
1213
payPeriodEnd: string;
1314
}

src/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function calculateAllocation(
5151
daysInOtherStates: DaysInOtherStates,
5252
globalBonuses: Bonus[]
5353
): CalculationResult {
54-
const { netPay, payPeriodStart, payPeriodEnd } = data;
54+
const { netPay, pretaxDeductions, payPeriodStart, payPeriodEnd } = data;
5555
const ppStart = parseUTCDate(payPeriodStart);
5656
const ppEnd = parseUTCDate(payPeriodEnd);
5757
const primaryVisitStart = parseUTCDate(visitingDates.start);
@@ -103,7 +103,9 @@ export function calculateAllocation(
103103
}
104104

105105
// Bonuses are not subtracted from period pay; they are separate gross pay, allocated by date
106-
const regularPay = parseFloat(String(netPay || 0));
106+
const grossPay = parseFloat(String(netPay || 0));
107+
const deductions = parseFloat(String(pretaxDeductions || 0));
108+
const regularPay = grossPay - deductions;
107109
const dailyRate = totalWorkedDays > 0 ? regularPay / totalWorkedDays : 0;
108110

109111
const allocations: Record<string, { days: number; regularPay: number; bonus: number; total: number }> = {};

0 commit comments

Comments
 (0)