Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 187 additions & 15 deletions frontend/src/page/CreateInvoice.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ import TokenPicker, { ToggleSwitch } from "@/components/TokenPicker";
import { CopyButton } from "@/components/ui/copyButton";
import CountryPicker from "@/components/CountryPicker";
import { useTokenList } from "@/hooks/useTokenList";
import {
getFromStorage,
saveToStorage,
clearStorage,
StorageKeys,
} from "@/utils/localStorage";

function CreateInvoice() {
const { data: walletClient } = useWalletClient();
Expand All @@ -65,6 +71,20 @@ function CreateInvoice() {
const [userCountry, setUserCountry] = useState("");
const [clientCountry, setClientCountry] = useState("");

// User form fields
const [userFname, setUserFname] = useState("");
const [userLname, setUserLname] = useState("");
const [userEmail, setUserEmail] = useState("");
const [userCity, setUserCity] = useState("");
const [userPostalcode, setUserPostalcode] = useState("");

// Client form fields
const [clientFname, setClientFname] = useState("");
const [clientLname, setClientLname] = useState("");
const [clientEmail, setClientEmail] = useState("");
const [clientCity, setClientCity] = useState("");
const [clientPostalcode, setClientPostalcode] = useState("");

// Token selection state
const [selectedToken, setSelectedToken] = useState(null);
const [customTokenAddress, setCustomTokenAddress] = useState("");
Expand All @@ -89,8 +109,67 @@ function CreateInvoice() {
]);

const [totalAmountDue, setTotalAmountDue] = useState(0);
const [isInitialLoad, setIsInitialLoad] = useState(true);

// Load data from localStorage on mount
useEffect(() => {
const savedData = getFromStorage(StorageKeys.CREATE_INVOICE);
if (savedData) {
// Restore dates
if (savedData.dueDate) {
setDueDate(new Date(savedData.dueDate));
}
if (savedData.issueDate) {
setIssueDate(new Date(savedData.issueDate));
}

// Restore user info
if (savedData.userFname) setUserFname(savedData.userFname);
if (savedData.userLname) setUserLname(savedData.userLname);
if (savedData.userEmail) setUserEmail(savedData.userEmail);
if (savedData.userCountry) setUserCountry(savedData.userCountry);
if (savedData.userCity) setUserCity(savedData.userCity);
if (savedData.userPostalcode) setUserPostalcode(savedData.userPostalcode);

// Restore client info (only if no URL params)
const urlClientAddress = searchParams.get("clientAddress");
if (!urlClientAddress) {
if (savedData.clientAddress) setClientAddress(savedData.clientAddress);
if (savedData.clientFname) setClientFname(savedData.clientFname);
if (savedData.clientLname) setClientLname(savedData.clientLname);
if (savedData.clientEmail) setClientEmail(savedData.clientEmail);
if (savedData.clientCountry) setClientCountry(savedData.clientCountry);
if (savedData.clientCity) setClientCity(savedData.clientCity);
if (savedData.clientPostalcode)
setClientPostalcode(savedData.clientPostalcode);
}

// Restore token selection
if (savedData.selectedToken) {
setSelectedToken(savedData.selectedToken);
setUseCustomToken(false);
}
if (savedData.useCustomToken && savedData.customTokenAddress) {
setUseCustomToken(true);
setCustomTokenAddress(savedData.customTokenAddress);
if (savedData.verifiedToken) {
setVerifiedToken(savedData.verifiedToken);
setTokenVerificationState("success");
}
}

// Restore item data
if (savedData.itemData && savedData.itemData.length > 0) {
setItemData(savedData.itemData);
}
}
setIsInitialLoad(false);
}, []);

// Handle URL params (should override localStorage)
useEffect(() => {
if (isInitialLoad) return;

console.log("account address : ", account.address);
const urlClientAddress = searchParams.get("clientAddress");
const urlTokenAddress = searchParams.get("tokenAddress");
Expand Down Expand Up @@ -157,7 +236,7 @@ function CreateInvoice() {
};

processUrlToken();
}, [searchParams, walletClient, tokens, loadingTokens, account.address]);
}, [searchParams, walletClient, tokens, loadingTokens, account.address, isInitialLoad]);

useEffect(() => {
const total = itemData.reduce((sum, item) => {
Expand Down Expand Up @@ -192,6 +271,64 @@ function CreateInvoice() {
setShowWalletAlert(!isConnected);
}, [isConnected]);

// Save form data to localStorage (debounced)
useEffect(() => {
if (isInitialLoad) return;

const saveData = () => {
const dataToSave = {
dueDate: dueDate?.toISOString(),
issueDate: issueDate?.toISOString(),
clientAddress,
userFname,
userLname,
userEmail,
userCountry,
userCity,
userPostalcode,
clientFname,
clientLname,
clientEmail,
clientCountry,
clientCity,
clientPostalcode,
selectedToken,
customTokenAddress,
useCustomToken,
verifiedToken,
itemData,
};

saveToStorage(StorageKeys.CREATE_INVOICE, dataToSave);
};

// Debounce save operations
const timeoutId = setTimeout(saveData, 500);
return () => clearTimeout(timeoutId);
}, [
dueDate,
issueDate,
clientAddress,
userFname,
userLname,
userEmail,
userCountry,
userCity,
userPostalcode,
clientFname,
clientLname,
clientEmail,
clientCountry,
clientCity,
clientPostalcode,
selectedToken,
customTokenAddress,
useCustomToken,
verifiedToken,
itemData,
isInitialLoad,
]);

const handleItemData = (e, index) => {
const { name, value } = e.target;

Expand Down Expand Up @@ -417,6 +554,7 @@ const verifyToken = async (address, targetChainId = null) => {
throw new Error("Missing chainId: wallet connected but chain not configured");
}

const chainId = account?.chainId;
const contractAddress = import.meta.env[
`VITE_CONTRACT_ADDRESS_${account.chainId}`
];
Expand All @@ -436,6 +574,10 @@ const verifyToken = async (address, targetChainId = null) => {
);

const receipt = await tx.wait();

// Clear localStorage on successful submission
clearStorage(StorageKeys.CREATE_INVOICE);

setTimeout(() => navigate("/dashboard/sent"), 4000);
} catch (err) {
console.error("Encryption or transaction failed:", err);
Expand All @@ -450,20 +592,20 @@ const verifyToken = async (address, targetChainId = null) => {
const formData = new FormData(e.target);

const data = {
userAddress: formData.get("userAddress"),
userFname: formData.get("userFname"),
userLname: formData.get("userLname"),
userEmail: formData.get("userEmail"),
userCountry: userCountry || formData.get("userCountry") || "",
userCity: formData.get("userCity"),
userPostalcode: formData.get("userPostalcode"),
clientAddress: formData.get("clientAddress"),
clientFname: formData.get("clientFname"),
clientLname: formData.get("clientLname"),
clientEmail: formData.get("clientEmail"),
clientCountry: clientCountry || formData.get("clientCountry") || "",
clientCity: formData.get("clientCity"),
clientPostalcode: formData.get("clientPostalcode"),
userAddress: account?.address?.toString(),
userFname,
userLname,
userEmail,
userCountry,
userCity,
userPostalcode,
clientAddress,
clientFname,
clientLname,
clientEmail,
clientCountry,
clientCity,
clientPostalcode,
itemData,
};
await createInvoiceRequest(data);
Expand Down Expand Up @@ -595,6 +737,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Your First Name"
className="w-full mt-1 border-gray-300 text-black "
name="userFname"
value={userFname}
onChange={(e) => setUserFname(e.target.value)}
/>
</div>
<div className="flex-1">
Expand All @@ -606,6 +750,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Your Last Name"
className="w-full mt-1 border-gray-300 text-black"
name="userLname"
value={userLname}
onChange={(e) => setUserLname(e.target.value)}
/>
</div>
</div>
Expand All @@ -620,6 +766,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Email"
className="w-full mt-1 border-gray-300 text-black"
name="userEmail"
value={userEmail}
onChange={(e) => setUserEmail(e.target.value)}
/>
</div>
<div className="flex-1">
Expand Down Expand Up @@ -653,6 +801,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="City"
className="w-full mt-1 border-gray-300 text-black"
name="userCity"
value={userCity}
onChange={(e) => setUserCity(e.target.value)}
/>
</div>
<div className="flex-1">
Expand All @@ -664,6 +814,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Postal Code"
className="w-full mt-1 border-gray-300 text-black"
name="userPostalcode"
value={userPostalcode}
onChange={(e) => setUserPostalcode(e.target.value)}
/>
</div>
</div>
Expand Down Expand Up @@ -693,6 +845,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Client First Name"
className="w-full mt-1 border-gray-300 text-black"
name="clientFname"
value={clientFname}
onChange={(e) => setClientFname(e.target.value)}
/>
</div>
<div className="flex-1">
Expand All @@ -704,6 +858,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Client Last Name"
className="w-full mt-1 border-gray-300 text-black"
name="clientLname"
value={clientLname}
onChange={(e) => setClientLname(e.target.value)}
/>
</div>
</div>
Expand All @@ -718,6 +874,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Email"
className="w-full mt-1 border-gray-300 text-black"
name="clientEmail"
value={clientEmail}
onChange={(e) => setClientEmail(e.target.value)}
/>
</div>
<div className="flex-1">
Expand Down Expand Up @@ -751,6 +909,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="City"
className="w-full mt-1 border-gray-300 text-black"
name="clientCity"
value={clientCity}
onChange={(e) => setClientCity(e.target.value)}
/>
</div>
<div className="flex-1">
Expand All @@ -762,6 +922,8 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Postal Code"
className="w-full mt-1 border-gray-300 text-black"
name="clientPostalcode"
value={clientPostalcode}
onChange={(e) => setClientPostalcode(e.target.value)}
/>
</div>
</div>
Expand Down Expand Up @@ -1025,6 +1187,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Enter Description"
className="w-full border-gray-300 text-black"
name="description"
value={itemData[index].description || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1039,6 +1202,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black"
name="qty"
value={itemData[index].qty || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1051,6 +1215,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black"
name="unitPrice"
value={itemData[index].unitPrice || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1066,6 +1231,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black"
name="discount"
value={itemData[index].discount || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1078,6 +1244,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black"
name="tax"
value={itemData[index].tax || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand Down Expand Up @@ -1139,6 +1306,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="Enter Description"
className="w-full border-gray-300 text-black"
name="description"
value={itemData[index].description || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1148,6 +1316,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black py-2"
name="qty"
value={itemData[index].qty || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1157,6 +1326,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black py-2"
name="unitPrice"
value={itemData[index].unitPrice || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1166,6 +1336,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black py-2"
name="discount"
value={itemData[index].discount || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand All @@ -1175,6 +1346,7 @@ const verifyToken = async (address, targetChainId = null) => {
placeholder="0"
className="w-full border-gray-300 text-black py-2"
name="tax"
value={itemData[index].tax || ""}
onChange={(e) => handleItemData(e, index)}
/>
</div>
Expand Down
Loading