157 lines
4.5 KiB
TypeScript
157 lines
4.5 KiB
TypeScript
import { useCallback, useRef, useState } from "preact/hooks";
|
|
import {
|
|
encryptDataWithStoredPassword,
|
|
getEncryptionPassword,
|
|
} from "../helpers.ts";
|
|
|
|
interface AddIncomeModalProps {
|
|
userId?: string;
|
|
}
|
|
|
|
export default function AddIncomeModal(
|
|
{ userId }: AddIncomeModalProps,
|
|
) {
|
|
const formRef = useRef<HTMLFormElement>(null);
|
|
const encryptedDataRef = useRef<HTMLInputElement>(null);
|
|
const sourceRef = useRef<HTMLInputElement>(null);
|
|
const amountRef = useRef<HTMLInputElement>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const today = new Date().toISOString().split("T")[0];
|
|
|
|
// Only encrypt on submit, not on every keystroke - prevents V8 overload
|
|
const handleEncryptData = useCallback(async () => {
|
|
// Get current values from the form inputs
|
|
const source = sourceRef.current?.value || "";
|
|
const amount = amountRef.current?.value || "";
|
|
|
|
// Check if encryption is available and encrypt the sensitive fields
|
|
if (userId && (source || amount)) {
|
|
const encryptionPassword = getEncryptionPassword(userId);
|
|
|
|
if (encryptionPassword) {
|
|
try {
|
|
const dataToEncrypt = {
|
|
source: source,
|
|
amount: parseFloat(amount) || 0,
|
|
};
|
|
|
|
const encryptedData = await encryptDataWithStoredPassword(
|
|
userId,
|
|
JSON.stringify(dataToEncrypt),
|
|
);
|
|
|
|
if (encryptedData && encryptedDataRef.current) {
|
|
// Set encrypted data
|
|
encryptedDataRef.current.value = encryptedData;
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to encrypt income data:", error);
|
|
}
|
|
}
|
|
}
|
|
}, [userId]);
|
|
|
|
const handleAddClick = async () => {
|
|
setLoading(true);
|
|
// Encrypt data before submission
|
|
await handleEncryptData();
|
|
|
|
// Get current values from the form inputs
|
|
const source = sourceRef.current?.value || "";
|
|
const amount = amountRef.current?.value || "";
|
|
|
|
// Clear plaintext fields if encryption was successful
|
|
if (userId && (source || amount)) {
|
|
const encryptionPassword = getEncryptionPassword(userId);
|
|
|
|
if (encryptionPassword && encryptedDataRef.current?.value) {
|
|
if (sourceRef.current) sourceRef.current.value = "";
|
|
if (amountRef.current) amountRef.current.value = "";
|
|
}
|
|
}
|
|
|
|
// Submit the form using formRef
|
|
setTimeout(() => {
|
|
if (formRef.current) {
|
|
formRef.current.submit();
|
|
}
|
|
}, 100);
|
|
};
|
|
|
|
return (
|
|
<div class="bg-white rounded-lg p-8 shadow-lg max-w-md w-full">
|
|
<h3 class="text-xl font-bold mb-4">Add Income</h3>
|
|
<form
|
|
ref={formRef}
|
|
method="POST"
|
|
action="/api/create/income"
|
|
class="flex flex-col gap-4"
|
|
>
|
|
<input
|
|
type="hidden"
|
|
name="sender"
|
|
value="/calendar/every-income?modal=off"
|
|
/>
|
|
<input
|
|
ref={encryptedDataRef}
|
|
type="hidden"
|
|
name="encrypted_data"
|
|
value=""
|
|
/>
|
|
<input
|
|
ref={sourceRef}
|
|
class="border rounded px-3 py-2"
|
|
placeholder="Source"
|
|
name="source"
|
|
required
|
|
/>
|
|
<input
|
|
ref={amountRef}
|
|
class="border rounded px-3 py-2"
|
|
placeholder="Amount"
|
|
type="number"
|
|
step="0.01"
|
|
name="amount"
|
|
required
|
|
/>
|
|
<select
|
|
class="w-full border p-2 rounded"
|
|
name="frequency"
|
|
required
|
|
defaultValue="Monthly"
|
|
>
|
|
<option value="Biweekly">Biweekly</option>
|
|
<option value="Weekly">Weekly</option>
|
|
<option value="Monthly">Monthly</option>
|
|
<option value="Quarterly">Quarterly</option>
|
|
<option value="Yearly">Yearly</option>
|
|
</select>
|
|
<input
|
|
class="border rounded px-3 py-2"
|
|
placeholder="Received On (YYYY-MM-DD)"
|
|
type="date"
|
|
name="received_date"
|
|
required
|
|
defaultValue={today}
|
|
/>
|
|
</form>
|
|
<div class="flex gap-2 justify-end mt-4">
|
|
<a
|
|
href="/calendar/every-income"
|
|
class="px-4 py-2 rounded bg-gray-300 hover:bg-gray-400 text-center inline-block"
|
|
>
|
|
Cancel
|
|
</a>
|
|
<button
|
|
type="button"
|
|
onClick={handleAddClick}
|
|
class="px-4 py-2 rounded bg-green-600 text-white hover:bg-green-700"
|
|
disabled={loading}
|
|
>
|
|
{loading ? "Adding..." : "Add"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|