Skip to content

Commit 20e1554

Browse files
authored
feat: add receipt export and viewing on request scan (#142)
1 parent 5eb3818 commit 20e1554

File tree

13 files changed

+185
-84
lines changed

13 files changed

+185
-84
lines changed

packages/invoice-dashboard/src/lib/dashboard/invoice-view.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
// Utils
2121
import { formatDate } from "@requestnetwork/shared-utils/formatDate";
2222
import { calculateItemTotal } from "@requestnetwork/shared-utils/invoiceTotals";
23+
import { exportToPDF } from "@requestnetwork/shared-utils/generateInvoice";
24+
import { getCurrencyFromManager } from "@requestnetwork/shared-utils/getCurrency";
2325
// Types
2426
import type { WalletState } from "@requestnetwork/shared-types/web3Onboard";
2527
2628
import { onMount } from "svelte";
2729
import { formatUnits } from "viem";
28-
import { exportToPDF, walletClientToSigner } from "../../utils";
29-
import { getCurrencyFromManager } from "../../utils/getCurrency";
30+
import { walletClientToSigner } from "../../utils";
3031
3132
export let config;
3233
export let wallet: WalletState | undefined;

packages/invoice-dashboard/src/lib/view-requests.svelte

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@
2525
// Utils
2626
import { config as defaultConfig } from "@requestnetwork/shared-utils/config";
2727
import { initializeCurrencyManager } from "@requestnetwork/shared-utils/initCurrencyManager";
28+
import { exportToPDF } from "@requestnetwork/shared-utils/generateInvoice";
29+
import { getCurrencyFromManager } from "@requestnetwork/shared-utils/getCurrency";
2830
2931
import { CurrencyManager } from "@requestnetwork/currency";
3032
import type { RequestNetwork } from "@requestnetwork/request-client.js";
3133
import { Types } from "@requestnetwork/request-client.js";
3234
import { onMount } from "svelte";
3335
import { formatUnits } from "viem";
34-
import { capitalize, debounce, exportToPDF, formatAddress } from "../utils";
35-
import { getCurrencyFromManager } from "../utils/getCurrency";
36+
import { capitalize, debounce, formatAddress } from "../utils";
37+
3638
import { Drawer, InvoiceView } from "./dashboard";
3739
3840
export let config: IConfig;
@@ -515,8 +517,8 @@
515517
<span
516518
>{formatAddress(
517519
currentTab === "Pay"
518-
? (request.payee?.value ?? "")
519-
: (request.payer?.value ?? "")
520+
? request.payee?.value ?? ""
521+
: request.payer?.value ?? ""
520522
)}</span
521523
>
522524
<Copy
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export { debounce } from "./debounce";
22
export { formatAddress } from "./formatAddress";
3-
export { exportToPDF } from "./generateInvoice";
43
export { publicClientToProvider, walletClientToSigner } from "./wallet-utils";
54
export { capitalize } from "./capitalize";

packages/payment-widget/src/lib/components/payment-complete.svelte

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
11
<script lang="ts">
22
import { fade } from "svelte/transition";
3+
import { exportToPDF } from "@requestnetwork/shared-utils/generateInvoice";
4+
import { getCurrencyFromManager } from "@requestnetwork/shared-utils/getCurrency";
5+
import { initializeCurrencyManager } from "@requestnetwork/shared-utils/initCurrencyManager";
6+
import Toaster from "@requestnetwork/shared-components/sonner.svelte";
7+
import { toast } from "svelte-sonner";
8+
9+
export let createdRequest: any;
10+
export let enablePdfReceipt: boolean = true;
11+
export let enableRequestScanLink: boolean = true;
12+
export let sellerLogo: string = "";
13+
14+
async function handleDownloadReceipt() {
15+
if (createdRequest) {
16+
try {
17+
const currencyManager = initializeCurrencyManager([]);
18+
19+
const currencyData = createdRequest?.inMemoryInfo?.requestData;
20+
21+
await exportToPDF(
22+
currencyData,
23+
getCurrencyFromManager(currencyData.currencyInfo, currencyManager),
24+
sellerLogo
25+
);
26+
} catch (error) {
27+
toast.error(`Failed to export PDF`, {
28+
description: `${error}`,
29+
action: {
30+
label: "X",
31+
onClick: () => console.info("Close"),
32+
},
33+
});
34+
}
35+
}
36+
}
337
</script>
438

39+
<Toaster />
540
<div class="payment-complete" transition:fade={{ duration: 300 }}>
641
<div class="checkmark-container">
742
<svg
@@ -19,9 +54,26 @@
1954
</div>
2055
<h2>Payment Complete</h2>
2156
<p>Thank you for your payment. Your transaction was successful.</p>
57+
58+
{#if enablePdfReceipt || (enableRequestScanLink && createdRequest)}
59+
<div class="buttons-container">
60+
{#if enablePdfReceipt}
61+
<button on:click={handleDownloadReceipt}>Download Receipt</button>
62+
{/if}
63+
{#if enableRequestScanLink && createdRequest}
64+
<a
65+
target="_blank"
66+
rel="noopener noreferrer"
67+
href={`https://scan.request.network/request/${createdRequest.requestId}`}
68+
>
69+
View on Request Scan
70+
</a>
71+
{/if}
72+
</div>
73+
{/if}
2274
</div>
2375

24-
<style>
76+
<style lang="scss">
2577
.payment-complete {
2678
display: flex;
2779
flex-direction: column;
@@ -41,6 +93,42 @@
4193
border-radius: 50%;
4294
}
4395
96+
.buttons-container {
97+
display: flex;
98+
gap: 16px;
99+
margin-top: 24px;
100+
101+
button,
102+
a {
103+
padding: 10px 20px;
104+
border-radius: 6px;
105+
font-size: 14px;
106+
font-weight: 500;
107+
text-decoration: none;
108+
transition: background-color 0.3s ease;
109+
}
110+
111+
button {
112+
background-color: #0bb489;
113+
color: white;
114+
border: none;
115+
cursor: pointer;
116+
117+
&:hover {
118+
background-color: darken(#0bb489, 10%);
119+
}
120+
}
121+
122+
a {
123+
background-color: #f5f5f5;
124+
color: #333;
125+
126+
&:hover {
127+
background-color: darken(#f5f5f5, 10%);
128+
}
129+
}
130+
}
131+
44132
h2 {
45133
margin-top: 1rem;
46134
font-size: 1.5rem;

packages/payment-widget/src/lib/components/payment-confirmation.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
export let invoiceNumber: string | undefined;
4040
export let feeAddress: string;
4141
export let feeAmountInUSD: number;
42+
export let createdRequest: any;
4243
4344
const COUNTDOWN_INTERVAL = 30;
4445
@@ -275,6 +276,8 @@
275276
persistRequest,
276277
});
277278

279+
createdRequest = request;
280+
278281
if (onPaymentSuccess) {
279282
onPaymentSuccess(request);
280283
}

packages/payment-widget/src/lib/payment-widget.svelte

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
export let invoiceNumber: string | undefined = undefined;
4040
export let feeAddress: string = ethers.constants.AddressZero;
4141
export let feeAmountInUSD: number = 0;
42+
export let enablePdfReceipt: boolean = true;
43+
export let enableRequestScanLink: boolean = true;
4244
4345
// State
4446
let web3Modal: Web3Modal | null = null;
@@ -47,7 +49,7 @@
4749
let selectedCurrency: Currency | null = null;
4850
let connectionCheckInterval: ReturnType<typeof setInterval> | null = null;
4951
let currentPaymentStep: PaymentStep = "currency";
50-
52+
let createdRequest: any;
5153
let scrollPosition = 0;
5254
5355
// Effects
@@ -246,12 +248,18 @@
246248
onPaymentError={onError}
247249
bind:currentPaymentStep
248250
bind:isConnected
251+
bind:createdRequest
249252
{sellerInfo}
250253
buyerInfo={currentBuyerInfo}
251254
{invoiceNumber}
252255
/>
253256
{:else}
254-
<PaymentComplete />
257+
<PaymentComplete
258+
{createdRequest}
259+
{enablePdfReceipt}
260+
{enableRequestScanLink}
261+
sellerLogo={sellerInfo.logo}
262+
/>
255263
{/if}
256264
</Modal>
257265
</section>

packages/payment-widget/src/lib/react/PaymentWidget.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface PaymentWidgetProps {
2323
invoiceNumber?: string;
2424
feeAddress?: string;
2525
feeAmountInUSD?: number;
26+
enablePdfReceipt?: boolean;
27+
enableRequestScanLink?: boolean;
2628
}
2729

2830
/**

packages/payment-widget/src/lib/utils/request.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export const handleRequestPayment = async ({
276276
skipPersistence: true,
277277
});
278278

279-
const inMemoryRequest =
279+
let inMemoryRequest =
280280
await inMemoryRequestNetwork.createRequest(requestParameters);
281281

282282
const signer = await ethersProvider!.getSigner();
@@ -328,6 +328,14 @@ export const handleRequestPayment = async ({
328328
await persistingRequestNetwork.persistRequest(inMemoryRequest);
329329
}
330330

331+
if (inMemoryRequest?.inMemoryInfo?.requestData) {
332+
inMemoryRequest.inMemoryInfo.requestData = {
333+
...inMemoryRequest.inMemoryInfo.requestData,
334+
payer: requestParameters.requestInfo.payer,
335+
payee: requestParameters.requestInfo.payee,
336+
};
337+
}
338+
331339
return inMemoryRequest;
332340
};
333341

shared/components/app.css

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,37 +35,6 @@
3535

3636
--radius: 0.5rem;
3737
}
38-
39-
.dark {
40-
--background: 222.2 84% 4.9%;
41-
--foreground: 210 40% 98%;
42-
43-
--muted: 217.2 32.6% 17.5%;
44-
--muted-foreground: 215 20.2% 65.1%;
45-
46-
--popover: 222.2 84% 4.9%;
47-
--popover-foreground: 210 40% 98%;
48-
49-
--card: 222.2 84% 4.9%;
50-
--card-foreground: 210 40% 98%;
51-
52-
--border: 217.2 32.6% 17.5%;
53-
--input: 217.2 32.6% 17.5%;
54-
55-
--primary: 210 40% 98%;
56-
--primary-foreground: 222.2 47.4% 11.2%;
57-
58-
--secondary: 217.2 32.6% 17.5%;
59-
--secondary-foreground: 210 40% 98%;
60-
61-
--accent: 217.2 32.6% 17.5%;
62-
--accent-foreground: 210 40% 98%;
63-
64-
--destructive: 0 62.8% 30.6%;
65-
--destructive-foreground: 210 40% 98%;
66-
67-
--ring: hsl(212.7, 26.8%, 83.9);
68-
}
6938
}
7039

7140
@layer base {

shared/components/sonner.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
Toaster as Sonner,
44
type ToasterProps as SonnerProps,
55
} from "svelte-sonner";
6-
import { mode } from "mode-watcher";
76
87
type $$Props = SonnerProps;
98
import "./app.css";
109
</script>
1110

1211
<Sonner
13-
theme={$mode}
1412
class="toaster group"
1513
toastOptions={{
1614
classes: {

0 commit comments

Comments
 (0)