diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml index ba6be25f..112f3e27 100644 --- a/.github/workflows/npm-publish.yaml +++ b/.github/workflows/npm-publish.yaml @@ -18,6 +18,7 @@ jobs: - '@requestnetwork/create-invoice-form' - '@requestnetwork/invoice-dashboard' - '@requestnetwork/payment-widget' + - '@requestnetwork/single-invoice' steps: - name: Checkout repository 🛎️ uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index ed6e2172..8748ae94 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ node_modules .turbo dist .svelte-kit +vite.congig.ts.timestamp* \ No newline at end of file diff --git a/README.md b/README.md index 35937e7d..8e9c4b3f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Usage depends on the component. See packages/\/README.md | [@requestnetwork/create-invoice-form](packages/create-invoice-form/README.md) | [![npm version](https://badge.fury.io/js/%40requestnetwork%2Fcreate-invoice-form.svg)](https://badge.fury.io/js/%40requestnetwork%2Fcreate-invoice-form) | | [@requestnetwork/invoice-dashboard](packages/invoice-dashboard/README.md) | [![npm version](https://badge.fury.io/js/%40requestnetwork%2Finvoice-dashboard.svg)](https://badge.fury.io/js/%40requestnetwork%2Finvoice-dashboard) | | [@requestnetwork/payment-widget](packages/payment-widget/README.md) | [![npm version](https://badge.fury.io/js/%40requestnetwork%2Fpayment-widget.svg)](https://badge.fury.io/js/%40requestnetwork%2Fpayment-widget) | +| [@requestnetwork/single-invoice](packages/single-invoice/README.md) | [![npm version](https://badge.fury.io/js/%40requestnetwork%2Fsingle-invoice.svg)](https://badge.fury.io/js/%40requestnetwork%2Fsingle-invoice) | | [@requestnetwork/shared](packages/shared/README.md) | [![npm version](https://badge.fury.io/js/%40requestnetwork%2Fshared.svg)](https://badge.fury.io/js/%40requestnetwork%2Fshared) | @@ -45,7 +46,7 @@ npm run link:all --app-path=../rn-checkout cd # Use local packages instead of the deployed ones -npm link @requestnetwork/create-invoice-form @requestnetwork/invoice-dashboard +npm link @requestnetwork/create-invoice-form @requestnetwork/invoice-dashboard @requestnetwork/single-invoice npm link @requestnetwork/payment-widget ``` diff --git a/package-lock.json b/package-lock.json index e32a86da..52edcd3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2964,6 +2964,10 @@ "resolved": "shared/utils", "link": true }, + "node_modules/@requestnetwork/single-invoice": { + "resolved": "packages/single-invoice", + "link": true + }, "node_modules/@requestnetwork/smart-contracts": { "version": "0.43.0", "resolved": "https://registry.npmjs.org/@requestnetwork/smart-contracts/-/smart-contracts-0.43.0.tgz", @@ -11436,6 +11440,37 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "packages/single-invoice": { + "name": "@requestnetwork/single-invoice", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@requestnetwork/payment-detection": "0.49.0", + "@requestnetwork/payment-processor": "0.52.0", + "@requestnetwork/request-client.js": "0.54.0", + "@wagmi/core": "^2.15.2", + "ethers": "^5.7.2", + "viem": "^2.21.53", + "wagmi": "^2.13.3" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/kit": "^1.27.4", + "@sveltejs/package": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^2.5.2", + "publint": "^0.1.9", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "svelte": "^4.0.5", + "svelte-check": "^3.6.0", + "typescript": "^5.0.0", + "vite": "^4.4.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "shared/components": { "name": "@requestnetwork/shared-components", "license": "MIT", diff --git a/package.json b/package.json index 3120de69..7467394f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "build:dashboard": "turbo run build --filter=@requestnetwork/invoice-dashboard", "build:stakeholder": "turbo run build --filter=@requestnetwork/add-stakeholder", "build:payment-widget": "turbo run build --filter=@requestnetwork/payment-widget", + "build:single-invoice": "turbo run build --filter=@requestnetwork/single-invoice", "link:react": "npm link $npm_config_app_path/node_modules/react $npm_config_app_path/node_modules/react-dom", "link:all": "npm run link:react --app-path=$npm_config_app_path && for d in packages/*; do (cd $d && npm link); done", "unlink:all": "for d in packages/*; do (cd $d && npm unlink); done" diff --git a/packages/create-invoice-form/src/lib/create-invoice-form.svelte b/packages/create-invoice-form/src/lib/create-invoice-form.svelte index 603ff7a9..5f12fb06 100644 --- a/packages/create-invoice-form/src/lib/create-invoice-form.svelte +++ b/packages/create-invoice-form/src/lib/create-invoice-form.svelte @@ -16,13 +16,12 @@ import { CurrencyTypes } from "@requestnetwork/types"; // Utils import { getInitialFormData, prepareRequestParams } from "./utils"; - import { config as defaultConfig } from "@requestnetwork/shared-utils/config"; - import { calculateInvoiceTotals } from "@requestnetwork/shared-utils/invoiceTotals"; import { + config as defaultConfig, + calculateInvoiceTotals, getCurrencySupportedNetworksForConversion, initializeCreateInvoiceCurrencyManager, - initializeCurrencyManager, - } from "@requestnetwork/shared-utils/initCurrencyManager"; + } from "@requestnetwork/shared-utils/index"; // Components import { InvoiceForm, InvoiceView } from "./invoice"; import Button from "@requestnetwork/shared-components/button.svelte"; diff --git a/packages/create-invoice-form/src/lib/invoice/form-view.svelte b/packages/create-invoice-form/src/lib/invoice/form-view.svelte index c5578509..12896448 100644 --- a/packages/create-invoice-form/src/lib/invoice/form-view.svelte +++ b/packages/create-invoice-form/src/lib/invoice/form-view.svelte @@ -11,9 +11,11 @@ import { CurrencyTypes } from "@requestnetwork/types"; // Utils - import { config as defaultConfig } from "@requestnetwork/shared-utils/config"; - import { calculateItemTotal } from "@requestnetwork/shared-utils/invoiceTotals"; - import { formatDate } from "@requestnetwork/shared-utils/formatDate"; + import { + formatDate, + calculateItemTotal, + config as defaultConfig, + } from "@requestnetwork/shared-utils/index"; export let defaultCurrencies; export let config: IConfig; diff --git a/packages/create-invoice-form/src/lib/invoice/form.svelte b/packages/create-invoice-form/src/lib/invoice/form.svelte index 8f489a53..8f901659 100644 --- a/packages/create-invoice-form/src/lib/invoice/form.svelte +++ b/packages/create-invoice-form/src/lib/invoice/form.svelte @@ -15,9 +15,12 @@ import type { IConfig, CustomFormData } from "@requestnetwork/shared-types"; // Utils - import { calculateItemTotal } from "@requestnetwork/shared-utils/invoiceTotals"; - import { checkAddress } from "@requestnetwork/shared-utils/checkEthAddress"; - import { inputDateFormat } from "@requestnetwork/shared-utils/formatDate"; + import { + checkAddress, + inputDateFormat, + calculateItemTotal, + } from "@requestnetwork/shared-utils/index"; + import { CurrencyTypes, CipherProviderTypes } from "@requestnetwork/types"; import isEmail from "validator/es/lib/isEmail"; diff --git a/packages/create-invoice-form/src/lib/react/CreateInvoiceForm.d.ts b/packages/create-invoice-form/src/lib/react/CreateInvoiceForm.d.ts index de0404b9..bc5a1885 100644 --- a/packages/create-invoice-form/src/lib/react/CreateInvoiceForm.d.ts +++ b/packages/create-invoice-form/src/lib/react/CreateInvoiceForm.d.ts @@ -8,7 +8,7 @@ export interface CreateInvoiceFormProps { config: IConfig; wagmiConfig: WagmiConfig; requestNetwork: RequestNetwork | null | undefined; - currencies: string[]; + currencies?: string[]; } /** diff --git a/packages/invoice-dashboard/README.md b/packages/invoice-dashboard/README.md index 27415024..66e69e3e 100644 --- a/packages/invoice-dashboard/README.md +++ b/packages/invoice-dashboard/README.md @@ -167,11 +167,7 @@ export const config: IConfig = { ## Chains and Currencies -| Chain | Currencies | -| -------- | ---------------------- | -| Ethereum | USDC, USDT, DAI | -| Polygon | USDC, USDT, DAI, USDCe | -| Sepolia | USDC, FAU | +For a list of supported chains and currencies see [Token List](https://docs.request.network/building-blocks/token-list) ## Additional Information diff --git a/packages/invoice-dashboard/src/lib/dashboard/invoice-view.svelte b/packages/invoice-dashboard/src/lib/dashboard/invoice-view.svelte index 856a6343..a985134a 100644 --- a/packages/invoice-dashboard/src/lib/dashboard/invoice-view.svelte +++ b/packages/invoice-dashboard/src/lib/dashboard/invoice-view.svelte @@ -22,16 +22,18 @@ import Tooltip from "@requestnetwork/shared-components/tooltip.svelte"; // Icons import Download from "@requestnetwork/shared-icons/download.svelte"; + import Share from "@requestnetwork/shared-icons/share.svelte"; // Utils - import { formatDate } from "@requestnetwork/shared-utils/formatDate"; - import { checkStatus } from "@requestnetwork/shared-utils/checkStatus"; - import { calculateItemTotal } from "@requestnetwork/shared-utils/invoiceTotals"; - import { exportToPDF } from "@requestnetwork/shared-utils/generateInvoice"; - import { getCurrencyFromManager } from "@requestnetwork/shared-utils/getCurrency"; - import { onMount } from "svelte"; + import { + exportToPDF, + formatDate, + checkStatus, + getEthersSigner, + calculateItemTotal, + getCurrencyFromManager, + getConversionPaymentValues, + } from "@requestnetwork/shared-utils/index"; import { formatUnits } from "viem"; - import { getConversionPaymentValues } from "../../utils/getConversionPaymentValues"; - import { getEthersSigner } from "../../utils"; interface EntityInfo { value: string; @@ -49,6 +51,7 @@ export let currencyManager: any; export let isRequestPayed: boolean; export let wagmiConfig: any; + export let singleInvoicePath: string; let network: string | undefined = request?.currencyInfo?.network || "mainnet"; let currency: CurrencyTypes.CurrencyDefinition | undefined = @@ -512,7 +515,12 @@ zksyncera: "0x144", base: "0x2105", }; - return networkIds[network]; + + const networkId = networkIds[network]; + if (!networkId) { + console.warn(`Unknown network: ${network}`); + } + return networkId; } // FIXME: Add rounding functionality @@ -599,6 +607,15 @@ })); } } + + $: { + const hexStringChain = "0x" + account?.chainId?.toString(16); + const networkId = getNetworkIdFromNetworkName(network || "mainnet"); + + correctChain = + hexStringChain.toLowerCase() === String(networkId).toLowerCase() || + Number(hexStringChain) === Number(networkId); + }
+ {#if singleInvoicePath} + + { + const shareUrl = `${window.location.origin}${singleInvoicePath}/${request.requestId}`; + navigator.clipboard.writeText(shareUrl); + toast.success("Share link copied to clipboard!"); + }} + /> + + {/if}

From:

@@ -948,6 +976,7 @@ .invoice-number svg { width: 13px; height: 13px; + cursor: pointer; } .invoice-address { diff --git a/packages/invoice-dashboard/src/lib/react/InvoiceDashboard.d.ts b/packages/invoice-dashboard/src/lib/react/InvoiceDashboard.d.ts index 9067cf18..17a97049 100644 --- a/packages/invoice-dashboard/src/lib/react/InvoiceDashboard.d.ts +++ b/packages/invoice-dashboard/src/lib/react/InvoiceDashboard.d.ts @@ -8,6 +8,7 @@ export interface InvoiceDashboardProps { config: IConfig; wagmiConfig: WagmiConfig; requestNetwork: RequestNetwork | null | undefined; + singleInvoicePath: string; } /** * InvoiceDashboard is a React component that integrates with the Request Network to manage and display invoices. @@ -24,6 +25,7 @@ export interface InvoiceDashboardProps { * config={config} * wagmiConfig={wagmiConfig} * requestNetwork={requestNetwork} + * singleInvoicePath={singleInvoicePath} * /> */ declare const InvoiceDashboard: React.FC; diff --git a/packages/invoice-dashboard/src/lib/view-requests.svelte b/packages/invoice-dashboard/src/lib/view-requests.svelte index 59d23d0b..0d0a7204 100644 --- a/packages/invoice-dashboard/src/lib/view-requests.svelte +++ b/packages/invoice-dashboard/src/lib/view-requests.svelte @@ -36,18 +36,22 @@ import type { IConfig } from "@requestnetwork/shared-types"; import type { RequestNetwork } from "@requestnetwork/request-client.js"; // Utils - import { config as defaultConfig } from "@requestnetwork/shared-utils/config"; - import { initializeCurrencyManager } from "@requestnetwork/shared-utils/initCurrencyManager"; - import { exportToPDF } from "@requestnetwork/shared-utils/generateInvoice"; - import { getCurrencyFromManager } from "@requestnetwork/shared-utils/getCurrency"; + import { + debounce, + checkStatus, + formatAddress, + getEthersSigner, + exportToPDF, + getCurrencyFromManager, + config as defaultConfig, + initializeCurrencyManager, + } from "@requestnetwork/shared-utils/index"; + import { formatUnits } from "viem"; import { CurrencyManager } from "@requestnetwork/currency"; import { onDestroy, onMount, tick } from "svelte"; - import { formatUnits } from "viem"; - import { debounce, formatAddress, getEthersSigner } from "../utils"; import { Drawer, InvoiceView } from "./dashboard"; import { getPaymentNetworkExtension } from "@requestnetwork/payment-detection"; import { CipherProviderTypes, CurrencyTypes } from "@requestnetwork/types"; - import { checkStatus } from "@requestnetwork/shared-utils/checkStatus"; import { ethers } from "ethers"; interface CipherProvider extends CipherProviderTypes.ICipherProvider { @@ -63,6 +67,7 @@ export let config: IConfig; export let wagmiConfig: WagmiConfig; export let requestNetwork: RequestNetwork | null | undefined; + export let singleInvoicePath: string; let cipherProvider: CipherProvider | undefined; @@ -903,6 +908,7 @@ bind:currencyManager config={activeConfig} request={activeRequest} + {singleInvoicePath} /> {/if} diff --git a/packages/invoice-dashboard/src/utils/capitalize.ts b/packages/invoice-dashboard/src/utils/capitalize.ts deleted file mode 100644 index ac21f51c..00000000 --- a/packages/invoice-dashboard/src/utils/capitalize.ts +++ /dev/null @@ -1 +0,0 @@ -export const capitalize = (str: string) => (str && str[0].toUpperCase() + str.slice(1)) || "" diff --git a/packages/invoice-dashboard/src/utils/formatAddress.ts b/packages/invoice-dashboard/src/utils/formatAddress.ts deleted file mode 100644 index 9b91118e..00000000 --- a/packages/invoice-dashboard/src/utils/formatAddress.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getAddress } from "viem"; - -export const formatAddress = ( - address: string, - first: number = 6, - last: number = 4 -): string | '-' => { - if (!address || address.length === 0) { - // Consider using a proper logging service - console.warn("[formatAddress] No address provided"); - return '-'; - } else { - try { - const checksumAddress = getAddress(address); - return `${checksumAddress.slice(0, first)}...${checksumAddress.slice(-last)}`; - } catch (error) { - console.error("Invalid address: ", error); - return '-'; - } - } -}; diff --git a/packages/invoice-dashboard/src/utils/index.ts b/packages/invoice-dashboard/src/utils/index.ts deleted file mode 100644 index 97101620..00000000 --- a/packages/invoice-dashboard/src/utils/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { debounce } from "./debounce"; -export { formatAddress } from "./formatAddress"; -export { publicClientToProvider, getEthersSigner } from "./wallet-utils"; -export { capitalize } from "./capitalize"; diff --git a/packages/payment-widget/src/lib/components/payment-complete.svelte b/packages/payment-widget/src/lib/components/payment-complete.svelte index 73e9fca3..d0d3b659 100644 --- a/packages/payment-widget/src/lib/components/payment-complete.svelte +++ b/packages/payment-widget/src/lib/components/payment-complete.svelte @@ -1,8 +1,11 @@ + +
+
+ +
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ +
+
+ +
+ + +
+ +
+ +
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
DESCRIPTIONQTYUNIT PRICEDISCOUNTTAXAMOUNT
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+ + diff --git a/packages/single-invoice/src/lib/react/SingleInvoice.d.ts b/packages/single-invoice/src/lib/react/SingleInvoice.d.ts new file mode 100644 index 00000000..959f3452 --- /dev/null +++ b/packages/single-invoice/src/lib/react/SingleInvoice.d.ts @@ -0,0 +1,36 @@ +import React from "react"; +import { Config as WagmiConfig } from "@wagmi/core"; +import type { IConfig } from "@requestnetwork/shared-types"; +import type { RequestNetwork } from "@requestnetwork/request-client.js"; + +export interface SingleInvoiceProps { + /** The unique identifier of the request/invoice to display */ + requestId: string; + /** Configuration object for the Request Network integration */ + config: IConfig; + /** Wagmi configuration for Web3 wallet interactions */ + wagmiConfig: WagmiConfig; + /** Instance of the Request Network client */ + requestNetwork: RequestNetwork | null | undefined; +} + +/** + * SingleInvoice is a React component that displays and manages a single invoice from the Request Network. + * + * This component wraps the web component 'single-invoice' and handles the proper passing and updating + * of props to ensure the invoice display stays in sync with the provided data. + * + * @param {SingleInvoiceProps} props - The component props + * @returns {JSX.Element} + * + * @example + * + */ +declare const SingleInvoice: React.FC; + +export default SingleInvoice; diff --git a/packages/single-invoice/src/lib/react/SingleInvoice.jsx b/packages/single-invoice/src/lib/react/SingleInvoice.jsx new file mode 100644 index 00000000..f1a986d4 --- /dev/null +++ b/packages/single-invoice/src/lib/react/SingleInvoice.jsx @@ -0,0 +1,19 @@ +// @ts-nocheck +import React, { useLayoutEffect, useRef } from "react"; +import("../web-component"); + +export const SingleInvoice = (props) => { + const widgetRef = useRef(null); + + useLayoutEffect(() => { + if (widgetRef.current) { + Object.entries(props).forEach(([key, value]) => { + widgetRef.current[key] = value; + }); + } + }, [props, widgetRef?.current]); + + return React.createElement("single-invoice", { ref: widgetRef }); +}; + +export default SingleInvoice; diff --git a/packages/single-invoice/src/lib/react/global.d.ts b/packages/single-invoice/src/lib/react/global.d.ts new file mode 100644 index 00000000..a0e3e3cc --- /dev/null +++ b/packages/single-invoice/src/lib/react/global.d.ts @@ -0,0 +1,8 @@ +declare namespace JSX { + interface IntrinsicElements { + "single-invoice": React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLElement + >; + } +} diff --git a/packages/single-invoice/src/lib/single-invoice.svelte b/packages/single-invoice/src/lib/single-invoice.svelte new file mode 100644 index 00000000..5771e08e --- /dev/null +++ b/packages/single-invoice/src/lib/single-invoice.svelte @@ -0,0 +1,1778 @@ + + + + +{#if loading} + +{:else if !requestId} +
Error: Missing required Request ID
+{:else if request} +
+
+
+

+ Issued on: {formatDate(request?.contentData?.creationDate || "-")} +

+

+ Due by: {formatDate( + request?.contentData?.paymentTerms?.dueDate || "-" + )} +

+
+

+ Invoice #{request?.contentData?.invoiceNumber || "-"} + + + { + try { + await exportToPDF( + request, + currency, + paymentCurrencies, + config.logo + ); + } catch (error) { + toast.error(`Failed to export PDF`, { + description: `${error}`, + action: { + label: "X", + onClick: () => console.info("Close"), + }, + }); + console.error("Failed to export PDF:", error); + } + }} + /> + +

+
+

From:

+

{request?.payee?.value || "-"}

+
+ {#if sellerInfo.length > 0} +
+ {#each sellerInfo as { value, isCompany, isEmail }} +

+ {#if isEmail} + + {:else if isCompany} + {value} + {:else} + {value} + {/if} +

+ {/each} +
+ {/if} +
+
+

Billed to:

+

{request?.payer?.value || "-"}

+
+ {#if buyerInfo.length > 0} +
+ {#each buyerInfo as { value, isCompany, isEmail }} +

+ {#if isEmail} + + {:else if isCompany} + {value} + {:else} + {value} + {/if} +

+ {/each} +
+ {/if} + +

+ Payment Chain: + {paymentCurrencies && paymentCurrencies.length > 0 + ? paymentCurrencies[0]?.network || "Unknown" + : ""} +

+

+ Invoice Currency: + {currency?.symbol || "Unknown"} +

+ +

+ Settlement Currency: + {paymentCurrencies && paymentCurrencies.length > 0 + ? paymentCurrencies[0]?.symbol || "Unknown" + : ""} +

+ + {#if request?.contentData?.invoiceItems} +
+ + + + + + + + + + + + + {#each firstItems as item, index (index)} + + + + + + + + + {/each} + +
DescriptionQtyUnit PriceDiscountTaxAmount
+

{item.name || "-"}

+
{item.quantity || "-"} + {#if unknownCurrency} + Unknown + {:else} + {item.unitPrice + ? formatUnits(item.unitPrice, currency?.decimals ?? 18) + : "-"} + {/if} + + {item.discount + ? formatUnits(item.discount, currency?.decimals ?? 18) + : "-"} + {Number(item.tax.amount || "-")} + {#if unknownCurrency} + Unknown + {:else} + {truncateNumberString( + formatUnits( + calculateItemTotal(item), + currency?.decimals ?? 18 + ), + 2 + )} + {/if} +
+
+ {#if otherItems.length > 0} + +
+ + + + + + + + + + + + + {#each otherItems as item, index (index)} + + + + + + + + + {/each} +
+

+ {item.name || "-"} +

+
{item.quantity || "-"} + {#if unknownCurrency} + Unknown + {:else} + {item.unitPrice + ? formatUnits( + item.unitPrice, + currency?.decimals ?? 18 + ) + : "-"} + {/if} + + {item.discount + ? formatUnits(item.discount, currency?.decimals ?? 18) + : "-"} + {Number(item.tax.amount || "-")} + {#if unknownCurrency} + Unknown + {:else} + {truncateNumberString( + formatUnits( + calculateItemTotal(item), + currency?.decimals ?? 18 + ), + 2 + )} + {/if} +
+
+
+ {/if} + {/if} + {#if request?.contentData?.note} +
+

+ Memo:
+ {request.contentData?.note || "-"} +

+
+ {/if} +
+ {#if request?.contentData?.miscellaneous?.labels} + {#each request?.contentData?.miscellaneous?.labels as label, index (index)} +
+ {label || "-"} +
+ {/each} + {/if} +
+ {#if shouldShowStatuses} +
+
+ {#if statuses?.length > 0} +
    + {#each statuses as status, index} +
  • + + {#if status.done} + + + + {:else} + + + + {/if} + + {status.message} +
    +
  • + {/each} +
+ {/if} +
+
+ {/if} +
+ {#if !isPayee && !unsupportedNetwork && !isPaid && !isRequestPayed && !isSigningTransaction && !unknownCurrency} + {#if !hasEnoughBalance && correctChain} +
+ Insufficient funds: {Number(userBalance).toFixed(4)} + {paymentCurrencies[0]?.symbol || "-"} +
+ {/if} +
+ + {#if unsupportedNetwork} +
Unsupported payment network!
+ {/if} +
+
+{:else} +
No invoice found
+{/if} + +{#if loadSessionSignatures} + + + +{/if} + + diff --git a/packages/single-invoice/svelte.config.js b/packages/single-invoice/svelte.config.js new file mode 100644 index 00000000..ff60aa7c --- /dev/null +++ b/packages/single-invoice/svelte.config.js @@ -0,0 +1,19 @@ +import adapter from "@sveltejs/adapter-auto"; +import { vitePreprocess } from "@sveltejs/kit/vite"; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + alias: { + $src: "./src", + $utils: "./src/utils", + }, + }, + compilerOptions: { + customElement: true, + }, +}; + +export default config; diff --git a/packages/single-invoice/tsconfig.json b/packages/single-invoice/tsconfig.json new file mode 100644 index 00000000..7766439c --- /dev/null +++ b/packages/single-invoice/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "jsx": "react", + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "typeRoots": ["./src/types", "./node_modules/@types"], + "baseUrl": ".", + "paths": { + "$src/*": ["src/*"], + "$utils/*": ["src/lib/utils/*"] + } + }, + "include": ["src/**/*", "src/*.d.ts"] +} diff --git a/packages/single-invoice/tsconfig.react.json b/packages/single-invoice/tsconfig.react.json new file mode 100644 index 00000000..f4c55ad0 --- /dev/null +++ b/packages/single-invoice/tsconfig.react.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/react", + "declaration": true, + "jsx": "react-jsx", + "lib": ["ES2017", "DOM"], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "allowJs": true, + "noImplicitAny": false, + "strict": false + }, + "include": ["src/lib/react/**/*"] +} diff --git a/packages/single-invoice/vite.config.ts b/packages/single-invoice/vite.config.ts new file mode 100644 index 00000000..80864b9d --- /dev/null +++ b/packages/single-invoice/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [sveltekit()], +}); diff --git a/packages/single-invoice/vite.wc.config.ts b/packages/single-invoice/vite.wc.config.ts new file mode 100644 index 00000000..41f3e837 --- /dev/null +++ b/packages/single-invoice/vite.wc.config.ts @@ -0,0 +1,37 @@ +import { defineConfig } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; + +export default defineConfig({ + define: { + global: "globalThis", + }, + plugins: [ + svelte({ + compilerOptions: { + customElement: true, + }, + }), + ], + build: { + emptyOutDir: false, + sourcemap: true, + target: "modules", + lib: { + entry: "./src/lib/index.ts", + name: "<>", + fileName: "web-component", + }, + commonjsOptions: { + transformMixedEsModules: true, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "ReactDOM", + }, + }, + }, + }, +}); diff --git a/shared/icons/share.svelte b/shared/icons/share.svelte new file mode 100644 index 00000000..e5bb19a6 --- /dev/null +++ b/shared/icons/share.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/packages/invoice-dashboard/src/utils/chainlink.ts b/shared/utils/chainlink.ts similarity index 100% rename from packages/invoice-dashboard/src/utils/chainlink.ts rename to shared/utils/chainlink.ts diff --git a/packages/invoice-dashboard/src/utils/conversion.ts b/shared/utils/conversion.ts similarity index 100% rename from packages/invoice-dashboard/src/utils/conversion.ts rename to shared/utils/conversion.ts diff --git a/packages/invoice-dashboard/src/utils/debounce.ts b/shared/utils/debounce.ts similarity index 100% rename from packages/invoice-dashboard/src/utils/debounce.ts rename to shared/utils/debounce.ts diff --git a/shared/utils/formatAddress.ts b/shared/utils/formatAddress.ts index 91fab1ee..7b1fe993 100644 --- a/shared/utils/formatAddress.ts +++ b/shared/utils/formatAddress.ts @@ -1,11 +1,20 @@ -import { checkAddress } from "./checkEthAddress"; +import { getAddress } from "viem"; +import { checkAddress } from "@requestnetwork/shared-utils/checkEthAddress"; export const formatAddress = ( address: string, first: number = 6, last: number = 4 ): string => { - if (!address) return ""; + if (!address) { + return ""; + } - return `${address.slice(0, first)}...${address.slice(-last)}`; + if (!checkAddress(address)) { + console.error("Invalid address!"); + } + + const checksumAddress = getAddress(address); + + return `${checksumAddress.slice(0, first)}...${checksumAddress.slice(-last)}`; }; diff --git a/packages/invoice-dashboard/src/utils/getConversionPaymentValues.ts b/shared/utils/getConversionPaymentValues.ts similarity index 99% rename from packages/invoice-dashboard/src/utils/getConversionPaymentValues.ts rename to shared/utils/getConversionPaymentValues.ts index f9d306d9..695ccffa 100644 --- a/packages/invoice-dashboard/src/utils/getConversionPaymentValues.ts +++ b/shared/utils/getConversionPaymentValues.ts @@ -34,6 +34,7 @@ export const formatUnits = ( export const toFixedDecimal = (numberToFormat: number, decimals?: number) => { const MAX_DECIMALS = decimals !== undefined ? decimals : 5; + return Number(numberToFormat.toFixed(MAX_DECIMALS)); }; @@ -58,6 +59,7 @@ export const bigAmountify = ( token: Pick ): BigNumber => { let [whole, decimals] = amount.toString().split("."); + let pow = "0"; let powSign = true; @@ -78,6 +80,7 @@ export const bigAmountify = ( ? wholeBn.add(decimalsBn).mul(power) : wholeBn.add(decimalsBn).div(power); } + return wholeBn; }; @@ -110,6 +113,7 @@ export const getConversionPaymentValues = async ({ baseAmount * Number(conversionRate), selectedPaymentCurrency ); + const safeConversionAmount = bigAmountify( baseAmount * Number(conversionRate) * @@ -134,7 +138,6 @@ export const getConversionPaymentValues = async ({ const hasEnoughForSlippage = userBalance.gte(safeConversionAmount); const hasEnough = userBalance.gte(minConversionAmount); const isRisky = hasEnough && !hasEnoughForSlippage; - const slippageLevel = isRisky ? "risky" : ("safe" as SlippageLevel); const conversionAmount = isRisky ? userBalance : safeConversionAmount; @@ -152,6 +155,7 @@ export const getConversionPaymentValues = async ({ ), currency: selectedPaymentCurrency, }; + const safeBalance = { value: amountToFixedDecimal( safeConversionAmount, diff --git a/shared/utils/index.ts b/shared/utils/index.ts new file mode 100644 index 00000000..7365b970 --- /dev/null +++ b/shared/utils/index.ts @@ -0,0 +1,41 @@ +export { capitalize } from "./capitalize"; +export { getChainlinkRate } from "./chainlink"; +export { checkAddress } from "./checkEthAddress"; +export { checkStatus } from "./checkStatus"; +export { config } from "./config"; +export { + getConversionRate, + getSlippageMargin, + lowVolatilityTokens, +} from "./conversion"; +export { + currencyConversionPairs, + convertToCurrencyManagerFormat, + formattedCurrencyConversionPairs, +} from "./currencyConversionPairs"; +export { debounce } from "./debounce"; +export { formatAddress } from "./formatAddress"; +export { formatDate, inputDateFormat } from "./formatDate"; +export { loadScript, exportToPDF } from "./generateInvoice"; +export { + formatUnits, + toFixedDecimal, + amountToFixedDecimal, + bigAmountify, + getConversionPaymentValues, + type SlippageLevel, +} from "./getConversionPaymentValues"; +export { getCurrencyFromManager } from "./getCurrency"; +export { getNetworkIcon } from "./getNetworkIcon"; +export { + initializeCurrencyManager, + initializeCurrencyManagerWithCurrencyIDS, + initializeCreateInvoiceCurrencyManager, + getCurrencySupportedNetworksForConversion, +} from "./initCurrencyManager"; +export { calculateInvoiceTotals, calculateItemTotal } from "./invoiceTotals"; +export { + publicClientToProvider, + clientToSigner, + getEthersSigner, +} from "./wallet-utils"; diff --git a/packages/invoice-dashboard/src/utils/wallet-utils.ts b/shared/utils/wallet-utils.ts similarity index 100% rename from packages/invoice-dashboard/src/utils/wallet-utils.ts rename to shared/utils/wallet-utils.ts