Skip to content
Merged
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
70 changes: 57 additions & 13 deletions packages/invoice-dashboard/src/lib/dashboard/invoice-view.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -373,24 +373,68 @@
paymentCurrencies: any[],
signer: any
) => {
const approvalCheckers: { [key: string]: () => Promise<boolean> } = {
[Types.Extension.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT]: () =>
hasErc20Approval(requestData!, address!, signer),
[Types.Extension.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY]: () =>
hasErc20ApprovalForProxyConversion(
try {
if (!paymentNetworkExtension?.id || !address || !signer) {
console.log("Missing required arguments for approval check:", {
network: paymentNetworkExtension?.id,
address,
signer: !!signer,
});
return false;
}

// Skip approval check if payment is not required
if (requestData?.balance?.balance >= requestData?.expectedAmount) {
console.log("Payment already completed, skipping approval check");
return true;
}

// Validate payment currency
if (!paymentCurrencies[0]?.address) {
console.error("Invalid payment currency:", paymentCurrencies[0]);
return false;
}

// Check if we're on the correct network
const chainId = await signer.getChainId();
const expectedChainId = getNetworkIdFromNetworkName(network);
const expectedChainIdNumber = parseInt(expectedChainId, 16);

if (chainId !== expectedChainIdNumber) {
console.error("Wrong network:", {
current: `0x${chainId.toString(16)}`,
expected: expectedChainId,
});
return false;
}

if (
paymentNetworkExtension.id ===
Types.Extension.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT
) {
return await hasErc20Approval(requestData!, address!, signer).catch(
() => false
);
}

if (
paymentNetworkExtension.id ===
Types.Extension.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY
) {
return await hasErc20ApprovalForProxyConversion(
requestData!,
address!,
paymentCurrencies[0]?.address,
signer,
requestData.expectedAmount
),
};
).catch(() => false);
}

return (
(paymentNetworkExtension?.id &&
(await approvalCheckers[paymentNetworkExtension.id]?.())) ||
false
);
return false;
} catch (error) {
console.error("General approval check error:", error);
return false;
}
};

async function approve() {
Expand Down Expand Up @@ -485,7 +529,7 @@
async function checkBalance() {
try {
if (!address || !paymentCurrencies[0] || !network) {
console.log("Missing required parameters for balance check:", {
console.error("Missing required parameters for balance check:", {
address,
paymentCurrency: paymentCurrencies[0],
network,
Expand Down
235 changes: 176 additions & 59 deletions packages/invoice-dashboard/src/lib/view-requests.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import DashboardSkeleton from "@requestnetwork/shared-components/dashboard-skeleton.svelte";
import { toast } from "svelte-sonner";
import Modal from "@requestnetwork/shared-components/modal.svelte";
import SearchableDropdownCheckbox from "@requestnetwork/shared-components/searchable-checkbox-dropdown.svelte";
// Icons
import ChevronDown from "@requestnetwork/shared-icons/chevron-down.svelte";
import ChevronLeft from "@requestnetwork/shared-icons/chevron-left.svelte";
Expand Down Expand Up @@ -106,6 +107,27 @@
let sortOrder = "desc";
let sortColumn = "timestamp";

let selectedNetworks: string[] = [];
let networkOptions: { value: string; checked: boolean }[] = [];

let selectedTxTypes: string[] = [];
let txTypeOptions = [
{ value: "IN", checked: false },
{ value: "OUT", checked: false },
];

let selectedStatuses: string[] = [];
let statusOptions = [
{ value: "paid", checked: false },
{ value: "partially paid", checked: false },
{ value: "accepted", checked: false },
{ value: "awaiting payment", checked: false },
{ value: "canceled", checked: false },
{ value: "rejected", checked: false },
{ value: "overdue", checked: false },
{ value: "pending", checked: false },
];

const handleWalletConnection = async () => {
account = getAccount(wagmiConfig);
await loadRequests(sliderValueForDecryption, account, requestNetwork);
Expand Down Expand Up @@ -175,9 +197,23 @@
type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
value: account?.address,
});

requests = requestsData
?.map((request) => request.getData())
.sort((a, b) => b.timestamp - a.timestamp);

const uniqueNetworks = new Set<string>();
requests?.forEach((request) => {
const network = request.currencyInfo.network;
if (network) {
uniqueNetworks.add(network);
}
});

networkOptions = Array.from(uniqueNetworks).map((network) => ({
value: network,
checked: selectedNetworks.includes(network),
}));
} catch (error) {
console.error("Failed to fetch requests:", error);
} finally {
Expand Down Expand Up @@ -233,13 +269,29 @@

$: filteredRequests = requests?.filter((request) => {
const terms = searchQuery.toLowerCase();
const network = request.currencyInfo.network;
const txType = signer === request.payer?.value ? "OUT" : "IN";
const status = checkStatus(request).toLowerCase();

const networkMatch =
selectedNetworks.length === 0 ||
(network && selectedNetworks.includes(network));

const txTypeMatch =
selectedTxTypes.length === 0 || selectedTxTypes.includes(txType);

const statusMatch =
selectedStatuses.length === 0 || selectedStatuses.includes(status);

if (
currentTab === "All" ||
(currentTab === "Get Paid" &&
request.payee?.value?.toLowerCase() === signer?.toLowerCase()) ||
(currentTab === "Pay" &&
request.payer?.value?.toLowerCase() === signer?.toLowerCase())
networkMatch &&
txTypeMatch &&
statusMatch &&
(currentTab === "All" ||
(currentTab === "Get Paid" &&
request.payee?.value?.toLowerCase() === signer?.toLowerCase()) ||
(currentTab === "Pay" &&
request.payer?.value?.toLowerCase() === signer?.toLowerCase()))
) {
const invoiceMatches = request.contentData?.invoiceNumber
?.toString()
Expand Down Expand Up @@ -374,6 +426,7 @@
const handleSearchChange = (event: Event) => {
const { value } = event.target as HTMLInputElement;
searchQuery = value;
currentPage = 1;
};

const handleSort = (column: string) => {
Expand Down Expand Up @@ -410,38 +463,66 @@
return;

loading = true;
if (sliderValue === "on") {
try {
const signer = await getEthersSigner(wagmiConfig);
if (signer && currentAccount?.address) {
loadSessionSignatures =
localStorage?.getItem("lit-wallet-sig") === null;
await cipherProvider?.getSessionSignatures(
signer,
currentAccount.address,
window.location.host,
"Sign in to Lit Protocol through Request Network"
);
cipherProvider?.enableDecryption(true);
localStorage?.setItem("isDecryptionEnabled", JSON.stringify(true));
const previousNetworks = [...selectedNetworks]; // Store current selection

try {
if (sliderValue === "on") {
try {
const signer = await getEthersSigner(wagmiConfig);
if (signer && currentAccount?.address) {
loadSessionSignatures =
localStorage?.getItem("lit-wallet-sig") === null;
await cipherProvider?.getSessionSignatures(
signer,
currentAccount.address,
window.location.host,
"Sign in to Lit Protocol through Request Network"
);
cipherProvider?.enableDecryption(true);
localStorage?.setItem("isDecryptionEnabled", JSON.stringify(true));
}
} catch (error) {
console.error("Failed to enable decryption:", error);
toast.error("Failed to enable decryption.");
return;
} finally {
loadSessionSignatures = false;
}
} catch (error) {
console.error("Failed to enable decryption:", error);
toast.error("Failed to enable decryption.");
loading = false;
return;
} finally {
loadSessionSignatures = false;
} else {
cipherProvider?.enableDecryption(false);
localStorage?.setItem("isDecryptionEnabled", JSON.stringify(false));
}
} else {
cipherProvider?.enableDecryption(false);
localStorage?.setItem("isDecryptionEnabled", JSON.stringify(false));
await getRequests(currentAccount, currentRequestNetwork);
selectedNetworks = previousNetworks; // Restore selection
} finally {
loading = false;
}
await getRequests(currentAccount, currentRequestNetwork);
loading = false;
};

$: loadRequests(sliderValueForDecryption, account, requestNetwork);

const handleNetworkSelection = async (networks: string[]) => {
selectedNetworks = networks;
currentPage = 1;
if (networks.length === 0 && selectedNetworks.length > 0) {
loading = true;
try {
await getRequests(account!, requestNetwork!);
} finally {
loading = false;
}
}
};

const handleTxTypeSelection = (types: string[]) => {
selectedTxTypes = types;
currentPage = 1;
};

const handleStatusSelection = (statuses: string[]) => {
selectedStatuses = statuses;
currentPage = 1;
};
</script>

<div
Expand Down Expand Up @@ -485,35 +566,59 @@
</div>
<div style="display: flex; flex-direction: column;">
<div class="search-wrapper">
<div class="search-wrapper" style="gap: 10px;">
<Input
placeholder="Search..."
width="w-[300px]"
handleInput={handleSearchChange}
>
<div slot="icon">
<Search />
</div>
</Input>
{#if cipherProvider}
<div class="width: fit-content;">
<Switch
bind:value={sliderValueForDecryption}
label="Show encrypted requests"
fontSize={14}
design="slider"
/>
</div>
{/if}
<Input
placeholder="Search..."
width="w-[300px]"
handleInput={handleSearchChange}
>
<div slot="icon">
<Search />
</div>
</Input>
{#if cipherProvider}
<div class="switch-wrapper">
<Switch
bind:value={sliderValueForDecryption}
label="Show encrypted requests"
fontSize={14}
design="slider"
/>
</div>
{/if}

<div class="dropdown-controls">
<SearchableDropdownCheckbox
config={activeConfig}
options={statusOptions}
placeholder="Filter by Status"
onchange={handleStatusSelection}
searchPlaceholder="Search statuses..."
type="status"
/>
<SearchableDropdownCheckbox
config={activeConfig}
options={txTypeOptions}
placeholder="Filter by Type"
onchange={handleTxTypeSelection}
type="transaction"
noSearch={true}
/>
<SearchableDropdownCheckbox
config={activeConfig}
options={networkOptions}
placeholder="Filter by Chain"
onchange={handleNetworkSelection}
searchPlaceholder="Search chains..."
type="network"
/>
<Dropdown
config={activeConfig}
type="checkbox"
options={columnOptions}
placeholder="Select Columns"
onchange={handleColumnChange}
/>
</div>

<Dropdown
config={activeConfig}
type="checkbox"
options={columnOptions}
placeholder="Select Columns"
onchange={handleColumnChange}
/>
</div>
<div class="table-wrapper">
<table>
Expand Down Expand Up @@ -921,6 +1026,7 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}

@media only screen and (max-width: 880px) {
Expand Down Expand Up @@ -1107,4 +1213,15 @@
margin-bottom: 0.5rem;
color: #4b5563;
}

.dropdown-controls {
display: flex;
align-items: center;
margin-left: auto;
gap: 8px;
}

.switch-wrapper {
margin-left: 8px;
}
</style>
Loading
Loading