diff --git a/apps/web/src/common/dialog-controller.tsx b/apps/web/src/common/dialog-controller.tsx index ecf81b082..33a6eff46 100644 --- a/apps/web/src/common/dialog-controller.tsx +++ b/apps/web/src/common/dialog-controller.tsx @@ -211,8 +211,9 @@ export function showError(title: string, message: string) { export function showMultiDeleteConfirmation(length: number) { return confirm({ title: `Delete ${length} items?`, - message: `These items will be **kept in your Trash for ${db.settings?.getTrashCleanupInterval() || 7 - } days** after which they will be permanently deleted.`, + message: `These items will be **kept in your Trash for ${ + db.settings?.getTrashCleanupInterval() || 7 + } days** after which they will be permanently deleted.`, positiveButtonText: "Yes", negativeButtonText: "No" }); @@ -331,6 +332,12 @@ export function showMoveNoteDialog(noteIds: string[]) { )); } +export function showBillingHistoryDialog() { + return showDialog("BillingHistoryDialog", (Dialog, perform) => ( + perform(res)} /> + )); +} + function getDialogData(type: string) { switch (type) { case "create_vault": diff --git a/apps/web/src/components/dialogs/billing-history-dialog.tsx b/apps/web/src/components/dialogs/billing-history-dialog.tsx new file mode 100644 index 000000000..f33307c75 --- /dev/null +++ b/apps/web/src/components/dialogs/billing-history-dialog.tsx @@ -0,0 +1,145 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { useEffect, useState } from "react"; +import { Perform } from "../../common/dialog-controller"; +import Dialog from "./dialog"; +import { db } from "../../common/db"; +import { Loading } from "../icons"; +import { Flex, Link, Text } from "@theme-ui/components"; +import { formatDate } from "@notesnook/core/utils/date"; + +type Transaction = { + order_id: string; + checkout_id: string; + amount: string; + currency: string; + status: keyof typeof TransactionStatusToText; + created_at: Date; + passthrough: null; + product_id: number; + is_subscription: boolean; + is_one_off: boolean; + subscription: Subscription; + user: User; + receipt_url: string; +}; + +type Subscription = { + subscription_id: number; + status: string; +}; + +type User = { + user_id: number; + email: string; + marketing_consent: boolean; +}; + +const TransactionStatusToText = { + completed: "Completed", + refunded: "Refunded", + partially_refunded: "Partially refunded", + disputed: "Disputed" +}; + +export type BillingHistoryDialogProps = { + onClose: Perform; +}; + +export default function BillingHistoryDialog(props: BillingHistoryDialogProps) { + const [transactions, setTransactions] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(); + + useEffect(() => { + (async function () { + try { + setError(undefined); + setIsLoading(true); + + const transactions = await db.subscriptions?.transactions(); + if (!transactions) return; + setTransactions(transactions); + } catch (e) { + if (e instanceof Error) setError(e); + } finally { + setIsLoading(false); + } + })(); + }, []); + + return ( + props.onClose(false)} + negativeButton={{ text: "Close", onClick: () => props.onClose(false) }} + width={600} + > + {isLoading ? ( + + ) : error ? ( + + + {error.message} +
+ {error.stack} +
+
+ ) : ( + + {transactions.map((transaction) => ( + + + Order #{transaction.order_id} + + {formatDate(new Date(transaction.created_at).getTime())} •{" "} + {TransactionStatusToText[transaction.status]} + + + + + {transaction.amount} {transaction.currency} + + + View receipt + + + + ))} + + )} +
+ ); +} diff --git a/apps/web/src/components/dialogs/index.ts b/apps/web/src/components/dialogs/index.ts index f9f5cb3b7..342ebfe03 100644 --- a/apps/web/src/components/dialogs/index.ts +++ b/apps/web/src/components/dialogs/index.ts @@ -57,6 +57,9 @@ const EmailChangeDialog = React.lazy(() => import("./email-change-dialog")); const LanguageSelectorDialog = React.lazy( () => import("./language-selector-dialog") ); +const BillingHistoryDialog = React.lazy( + () => import("./billing-history-dialog") +); export const Dialogs = { AddNotebookDialog, @@ -85,5 +88,6 @@ export const Dialogs = { AddReminderDialog, ReminderPreviewDialog, EmailChangeDialog, - LanguageSelectorDialog + LanguageSelectorDialog, + BillingHistoryDialog }; diff --git a/apps/web/src/views/settings.js b/apps/web/src/views/settings.js index 801883102..ef0283dad 100644 --- a/apps/web/src/views/settings.js +++ b/apps/web/src/views/settings.js @@ -44,7 +44,8 @@ import { showPromptDialog, showEmailChangeDialog, showLanguageSelectorDialog, - confirm + confirm, + showBillingHistoryDialog } from "../common/dialog-controller"; import { TaskManager } from "../common/task-manager"; import { SUBSCRIPTION_STATUS } from "../common/constants"; @@ -1391,6 +1392,12 @@ function AccountStatus(props) { tip="If you are eligible for a refund, your account will be immediately downgraded to Basic and your funds will be transferred to your account within 24 hours." /> +