|
23 | 23 | initializeCreateInvoiceCurrencyManager,
|
24 | 24 | } from "@requestnetwork/shared-utils/index";
|
25 | 25 | // Components
|
| 26 | + import Toaster from "@requestnetwork/shared-components/sonner.svelte"; |
| 27 | + import Share from "@requestnetwork/shared-icons/share.svelte"; |
26 | 28 | import { InvoiceForm, InvoiceView } from "./invoice";
|
27 | 29 | import Button from "@requestnetwork/shared-components/button.svelte";
|
28 | 30 | import Status from "@requestnetwork/shared-components/status.svelte";
|
29 | 31 | import Modal from "@requestnetwork/shared-components/modal.svelte";
|
30 | 32 | import { EncryptionTypes, CipherProviderTypes } from "@requestnetwork/types";
|
31 | 33 | import { onDestroy, onMount, tick } from "svelte";
|
32 | 34 | import { CurrencyManager } from "@requestnetwork/currency";
|
| 35 | + import { toast } from "svelte-sonner"; |
33 | 36 |
|
34 | 37 | interface CipherProvider extends CipherProviderTypes.ICipherProvider {
|
35 | 38 | disconnectWallet: () => void;
|
|
39 | 42 | export let wagmiConfig: WagmiConfig;
|
40 | 43 | export let requestNetwork: RequestNetwork | null | undefined;
|
41 | 44 | export let currencies: string[] = [];
|
| 45 | + export let singleInvoicePath = "/invoice"; |
42 | 46 | let cipherProvider: CipherProvider | undefined;
|
43 | 47 |
|
44 | 48 | let account: GetAccountReturnType | undefined =
|
|
61 | 65 |
|
62 | 66 | let defaultCurrencies: any[] = [];
|
63 | 67 |
|
| 68 | + let showSuccessDialog = false; |
| 69 | + let createdRequestId = ""; |
| 70 | +
|
64 | 71 | onMount(async () => {
|
65 | 72 | currencyManager = await initializeCreateInvoiceCurrencyManager(currencies);
|
66 | 73 |
|
|
283 | 290 |
|
284 | 291 | const hanldeCreateNewInvoice = () => {
|
285 | 292 | removeAllStatuses();
|
| 293 | + handleCloseSuccessDialog(); |
286 | 294 | formData = getInitialFormData();
|
287 | 295 | };
|
288 | 296 |
|
|
340 | 348 | addToStatus(APP_STATUS.PERSISTING_ON_CHAIN);
|
341 | 349 | await request.waitForConfirmation();
|
342 | 350 | addToStatus(APP_STATUS.REQUEST_CONFIRMED);
|
| 351 | +
|
| 352 | + // Show success dialog after confirmation |
| 353 | + createdRequestId = request.requestId; |
| 354 | + removeAllStatuses(); |
| 355 | + showSuccessDialog = true; |
343 | 356 | } catch (error: any) {
|
344 | 357 | if (error.message.includes("Transaction confirmation not received")) {
|
345 | 358 | isTimeout = true;
|
|
351 | 364 | }
|
352 | 365 | }
|
353 | 366 | };
|
| 367 | +
|
| 368 | + const handleCloseSuccessDialog = () => { |
| 369 | + showSuccessDialog = false; |
| 370 | + }; |
354 | 371 | </script>
|
355 | 372 |
|
356 | 373 | <div
|
|
392 | 409 | onClose={handleCloseInvoiceModal}
|
393 | 410 | >
|
394 | 411 | <Status config={activeConfig} statuses={appStatus} />
|
395 |
| - <div class="modal-footer"> |
396 |
| - <Button |
397 |
| - type="button" |
398 |
| - onClick={() => handleGoToDashboard(activeConfig.dashboardLink)} |
399 |
| - text="Go to dashboard" |
400 |
| - disabled={!appStatus.includes(APP_STATUS.REQUEST_CONFIRMED)} |
401 |
| - /> |
402 |
| - <Button |
403 |
| - type="button" |
404 |
| - onClick={hanldeCreateNewInvoice} |
405 |
| - text="Create a new invoice" |
406 |
| - disabled={!appStatus.includes(APP_STATUS.REQUEST_CONFIRMED)} |
407 |
| - /> |
| 412 | + </Modal> |
| 413 | + <Modal |
| 414 | + title="Invoice Created Successfully!" |
| 415 | + config={activeConfig} |
| 416 | + isOpen={showSuccessDialog} |
| 417 | + onClose={handleCloseSuccessDialog} |
| 418 | + > |
| 419 | + <div class="success-modal"> |
| 420 | + <div class="checkmark-container"> |
| 421 | + <svg |
| 422 | + class="checkmark" |
| 423 | + xmlns="http://www.w3.org/2000/svg" |
| 424 | + viewBox="0 0 52 52" |
| 425 | + > |
| 426 | + <circle |
| 427 | + class="checkmark__circle" |
| 428 | + cx="26" |
| 429 | + cy="26" |
| 430 | + r="25" |
| 431 | + fill="none" |
| 432 | + /> |
| 433 | + <path |
| 434 | + class="checkmark__check" |
| 435 | + fill="none" |
| 436 | + d="M14.1 27.2l7.1 7.2 16.7-16.8" |
| 437 | + /> |
| 438 | + </svg> |
| 439 | + </div> |
| 440 | + <p class="success-message"> |
| 441 | + Your invoice has been sent to<br /> |
| 442 | + {formData.payeeAddress} |
| 443 | + </p> |
| 444 | + <p class="share-tip"> |
| 445 | + Did you know that sharing your invoice via link cuts payment times by |
| 446 | + 77%? Give it a try. |
| 447 | + </p> |
| 448 | + |
| 449 | + <div class="share-buttons"> |
| 450 | + <div |
| 451 | + class="copy-button-wrapper" |
| 452 | + on:click={() => { |
| 453 | + const shareUrl = `${window.location.origin}${singleInvoicePath}/${createdRequestId}`; |
| 454 | + navigator.clipboard.writeText(shareUrl); |
| 455 | + toast.success("Share link copied to clipboard!"); |
| 456 | + }} |
| 457 | + > |
| 458 | + Copy the Link |
| 459 | + <Share /> |
| 460 | + </div> |
| 461 | + </div> |
| 462 | + |
| 463 | + <div class="action-buttons"> |
| 464 | + <button |
| 465 | + class="primary-button" |
| 466 | + on:click={() => handleGoToDashboard(activeConfig.dashboardLink)} |
| 467 | + > |
| 468 | + Back to Dashboard |
| 469 | + </button> |
| 470 | + <button class="primary-button" on:click={hanldeCreateNewInvoice}> |
| 471 | + Create New Invoice |
| 472 | + </button> |
| 473 | + <button |
| 474 | + class="primary-button" |
| 475 | + on:click={() => |
| 476 | + (window.location.href = `${window.location.origin}${singleInvoicePath}/${createdRequestId}`)} |
| 477 | + > |
| 478 | + View Invoice |
| 479 | + </button> |
| 480 | + </div> |
408 | 481 | </div>
|
409 | 482 | </Modal>
|
410 | 483 | <Modal
|
|
437 | 510 | />
|
438 | 511 | </div>
|
439 | 512 | </Modal>
|
| 513 | + <Toaster /> |
440 | 514 | </div>
|
441 | 515 |
|
442 |
| -<style> |
| 516 | +<style lang="scss"> |
443 | 517 | @font-face {
|
444 | 518 | font-family: "Montserrat";
|
445 | 519 | src: url("./fonts/Montserrat-VariableFont_wght.ttf") format("truetype");
|
|
500 | 574 | width: fit-content !important;
|
501 | 575 | height: fit-content !important;
|
502 | 576 | }
|
| 577 | +
|
| 578 | + .success-modal { |
| 579 | + display: flex; |
| 580 | + flex-direction: column; |
| 581 | + align-items: center; |
| 582 | + justify-content: center; |
| 583 | + width: 100%; |
| 584 | + max-width: 600px; |
| 585 | + padding: 32px; |
| 586 | + text-align: center; |
| 587 | + background: white; |
| 588 | + border-radius: 8px; |
| 589 | + } |
| 590 | +
|
| 591 | + .success-modal h2 { |
| 592 | + font-size: 24px; |
| 593 | + margin-bottom: 24px; |
| 594 | + color: #333; |
| 595 | + } |
| 596 | +
|
| 597 | + .checkmark-container { |
| 598 | + width: 80px; |
| 599 | + height: 80px; |
| 600 | + display: flex; |
| 601 | + justify-content: center; |
| 602 | + align-items: center; |
| 603 | + background-color: #4caf50; |
| 604 | + border-radius: 50%; |
| 605 | + margin-bottom: 26px; |
| 606 | + } |
| 607 | +
|
| 608 | + .checkmark { |
| 609 | + width: 56px; |
| 610 | + height: 56px; |
| 611 | + border-radius: 50%; |
| 612 | + display: block; |
| 613 | + stroke-width: 3; |
| 614 | + stroke: #fff; |
| 615 | + stroke-miterlimit: 10; |
| 616 | + animation: scale 0.3s ease-in-out 0.9s both; |
| 617 | + } |
| 618 | +
|
| 619 | + .checkmark__circle { |
| 620 | + stroke-dasharray: 166; |
| 621 | + stroke-dashoffset: 166; |
| 622 | + stroke-width: 3; |
| 623 | + stroke-miterlimit: 10; |
| 624 | + stroke: #fff; |
| 625 | + fill: none; |
| 626 | + animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards; |
| 627 | + } |
| 628 | +
|
| 629 | + .checkmark__check { |
| 630 | + transform-origin: 50% 50%; |
| 631 | + stroke-dasharray: 48; |
| 632 | + stroke-dashoffset: 48; |
| 633 | + animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards; |
| 634 | + } |
| 635 | +
|
| 636 | + .success-message { |
| 637 | + margin-bottom: 24px; |
| 638 | + color: #666; |
| 639 | + } |
| 640 | +
|
| 641 | + .share-tip { |
| 642 | + margin-bottom: 32px; |
| 643 | + color: #666; |
| 644 | + font-size: 14px; |
| 645 | + } |
| 646 | +
|
| 647 | + .share-buttons { |
| 648 | + display: flex; |
| 649 | + justify-content: center; |
| 650 | + margin-bottom: 32px; |
| 651 | + } |
| 652 | +
|
| 653 | + .copy-button-wrapper { |
| 654 | + display: flex; |
| 655 | + justify-content: space-between; |
| 656 | + align-items: center; |
| 657 | + cursor: pointer; |
| 658 | + background: #f6f6f7; |
| 659 | + padding: 12px 24px; |
| 660 | + border-radius: 8px; |
| 661 | + width: 180px; |
| 662 | + font-size: 14px; |
| 663 | + } |
| 664 | +
|
| 665 | + .action-buttons { |
| 666 | + display: flex; |
| 667 | + gap: 16px; |
| 668 | + justify-content: center; |
| 669 | + } |
| 670 | +
|
| 671 | + .action-button { |
| 672 | + padding: 12px 24px; |
| 673 | + border: none; |
| 674 | + border-radius: 8px; |
| 675 | + font-weight: 500; |
| 676 | + cursor: pointer; |
| 677 | + transition: all 0.2s; |
| 678 | + } |
| 679 | +
|
| 680 | + .primary-button { |
| 681 | + border: none; |
| 682 | + background: transparent; |
| 683 | + width: fit-content; |
| 684 | + color: var(--mainColor); |
| 685 | + cursor: pointer; |
| 686 | + } |
| 687 | +
|
| 688 | + .primary-button:hover { |
| 689 | + opacity: 0.9; |
| 690 | + } |
| 691 | +
|
| 692 | + @media (max-width: 600px) { |
| 693 | + .success-modal { |
| 694 | + padding: 24px; |
| 695 | + } |
| 696 | +
|
| 697 | + .action-buttons { |
| 698 | + flex-direction: column; |
| 699 | + } |
| 700 | + } |
| 701 | +
|
| 702 | + @keyframes stroke { |
| 703 | + 100% { |
| 704 | + stroke-dashoffset: 0; |
| 705 | + } |
| 706 | + } |
| 707 | +
|
| 708 | + @keyframes scale { |
| 709 | + 0%, |
| 710 | + 100% { |
| 711 | + transform: none; |
| 712 | + } |
| 713 | + 50% { |
| 714 | + transform: scale3d(1.1, 1.1, 1); |
| 715 | + } |
| 716 | + } |
503 | 717 | </style>
|
0 commit comments