diff --git a/public/cardbrands/default.png b/public/cardbrands/default.png new file mode 100644 index 0000000..f9f021e Binary files /dev/null and b/public/cardbrands/default.png differ diff --git a/public/cardbrands/discover.png b/public/cardbrands/discover.png new file mode 100644 index 0000000..ac653f0 Binary files /dev/null and b/public/cardbrands/discover.png differ diff --git a/public/cardbrands/maestro.png b/public/cardbrands/maestro.png new file mode 100644 index 0000000..5aba59b Binary files /dev/null and b/public/cardbrands/maestro.png differ diff --git a/public/cardbrands/mastercard.png b/public/cardbrands/mastercard.png new file mode 100644 index 0000000..b274d7e Binary files /dev/null and b/public/cardbrands/mastercard.png differ diff --git a/public/cardbrands/paypal.png b/public/cardbrands/paypal.png new file mode 100644 index 0000000..26a0f99 Binary files /dev/null and b/public/cardbrands/paypal.png differ diff --git a/public/cardbrands/visa.png b/public/cardbrands/visa.png new file mode 100644 index 0000000..7d21c22 Binary files /dev/null and b/public/cardbrands/visa.png differ diff --git a/public/logo192.png b/public/logo192.png deleted file mode 100644 index fc44b0a..0000000 Binary files a/public/logo192.png and /dev/null differ diff --git a/public/logo512.png b/public/logo512.png deleted file mode 100644 index a4e47a6..0000000 Binary files a/public/logo512.png and /dev/null differ diff --git a/src/features/Account/Account.tsx b/src/features/Account/Account.tsx index 728d477..f692380 100644 --- a/src/features/Account/Account.tsx +++ b/src/features/Account/Account.tsx @@ -1,9 +1,7 @@ import { Box, Tab, Tabs } from "@mui/material"; -import Button from "@mui/material/Button"; import { styled } from "@mui/material/styles"; import { Elements } from "@stripe/react-stripe-js"; import { loadStripe } from "@stripe/stripe-js"; -import { Auth } from "aws-amplify"; import { Layout } from "components/Layout"; import { useSeatsAvailable, @@ -19,6 +17,7 @@ import { formatPath } from "../../helpers/helpers"; import { useSubscriptionInfo } from "../../hooks/subscription"; import { client as api } from "../../services/APIService"; import { Intent } from "../../types/intent"; +import { SubscriptionStatus } from "../../types/subscription"; import { Billing } from "./Billing"; import { Modal as AddUser } from "./Modal"; import { columns } from "./TableColumns"; @@ -60,7 +59,7 @@ export const Account = () => { const result = useUsers(); const user = useUser(); const users = useTableUsers(result); - const info = useSubscriptionInfo(); + const subInfo = useSubscriptionInfo(); const tab = searchParams.get("t"); @@ -96,20 +95,23 @@ export const Account = () => { useEffect(() => { const fn = async () => { - const pi = await api.post( - "/subscriptions/payment-intent", - { - currency: "eur", - amount: 1000, - } - ); - - console.log(pi); - setOptions({ clientSecret: pi.client_secret }); + if ( + !subInfo || + subInfo.subscription.statusId != SubscriptionStatus.Cleared + ) { + const pi = await api.post( + "/subscriptions/payment-intent", + { + currency: "eur", + amount: 1000, + } + ); + setOptions({ clientSecret: pi.client_secret }); + } setTab(getTabIndex(tab)); }; fn(); - }, [tab]); + }, [subInfo, tab]); const toggleModal = () => { setOpen(!isOpen); @@ -145,7 +147,9 @@ export const Account = () => { +
+ { - {seatsResult.maxSeats == 3 ? ( - <> -

Basic Plan

- - {info && } - - Upgrade to Premium - - - ) : ( -

Premium Plan

- )} +
+ { emptyText={"No users"} /> + {options && user && ( { - return ( -
- {props.defaultPaymentMethod ? ( -
-

{PremiumPlan["eur"].plan}

- -
- - -
- ) : ( -
no data
- )} -
- ); -}; - -const StyledTable = styled(Table)` - margin: var(--p48) 0; - th { - font-family: ProximaNova-Extrabold; - font-size: var(--p14); - color: var(--gray7); - } -`; diff --git a/src/features/Account/Billing/Billing.tsx b/src/features/Account/Billing/Billing.tsx new file mode 100644 index 0000000..0fd0889 --- /dev/null +++ b/src/features/Account/Billing/Billing.tsx @@ -0,0 +1,142 @@ +import { Hive } from "@mui/icons-material"; +import Button from "@mui/material/Button"; +import { styled } from "@mui/material/styles"; +import Table from "rc-table"; +import React from "react"; + +import { + SubscriptionInfo, + SubscriptionStatus, +} from "../../../types/subscription"; +import { PremiumPlan } from "../constants"; +import { transactionColumns } from "../TableColumns"; +import { components } from "../TableRow"; + +interface BillingProps { + onToggle: () => void; + subInfo: SubscriptionInfo | undefined; +} + +export const Billing = ({ onToggle, subInfo }: BillingProps) => { + const handleUpgrade = () => { + onToggle(); + }; + + if (!subInfo || subInfo.subscription.statusId != SubscriptionStatus.Cleared) { + return ( +
+ + +

Basic Plan

+
+ + + Upgrade to Premium + +
+ ); + } + + return ( +
+ + +

Premium Plan

+
+

Payment Method

+ + Card + {subInfo.paymentMethod.billing_details.name} + + + +
+ ); +}; + +function getCardIcon(brand: string | undefined): string { + let icon: string; + switch (brand) { + case "visa": + icon = "visa.png"; + break; + case "discover": + icon = "discover.png"; + break; + case "mastercard": + icon = "mastercard.png"; + break; + case "paypal": + icon = "paypal.png"; + break; + case "maestro": + icon = "maestro.png"; + break; + default: + icon = "default.png"; + } + return icon; +} + +const StyledTable = styled(Table)` + margin: var(--p48) 0; + th { + font-family: ProximaNova-Extrabold; + font-size: var(--p14); + color: var(--gray7); + } +`; + +const StyledPremiumButton = styled(Button)` + background: var(--blue7); + + &:hover { + background: var(--blue8); + } +`; + +const StyledPaymentMethod = styled("div")` + display: flex; + flex-direction: row; + width: 500px; + + justify-content: space-between; + align-items: center; + aside { + display: flex; + align-items: center; + } + + img { + width: var(--p64); + border: 1px solid #eee; + border-radius: 8px; + margin: var(--p8); + } +`; + +const StyledHeader = styled("div")` + display: flex; + flex-direction: row; + align-items: center; + + svg { + color: var(--gray7); + margin-right: var(--p8); + } +`; diff --git a/src/features/Account/Billing/index.ts b/src/features/Account/Billing/index.ts new file mode 100644 index 0000000..5c2cad1 --- /dev/null +++ b/src/features/Account/Billing/index.ts @@ -0,0 +1 @@ +export { Billing } from "./Billing"; diff --git a/src/features/Account/TableColumns.tsx b/src/features/Account/TableColumns.tsx index be356ad..53a3c7f 100644 --- a/src/features/Account/TableColumns.tsx +++ b/src/features/Account/TableColumns.tsx @@ -1,5 +1,6 @@ import { styled } from "@mui/material/styles"; import React from "react"; +import { Link } from "react-router-dom"; import { DeleteDialog } from "./DeleteDialog"; @@ -14,7 +15,7 @@ export const columns = [ _: any, index: number ) => ( - + @@ -22,7 +23,7 @@ export const columns = [ {value.role && {value.role}} - + ), }, { @@ -30,7 +31,7 @@ export const columns = [ dataIndex: "email", key: "email", width: 200, - render: (value: any) => {value}, + render: (value: any) => {value}, }, { title: "Created At", @@ -38,7 +39,7 @@ export const columns = [ key: "createdAt", width: 200, render: (value: any) => ( - {new Date(value).toLocaleDateString("en-US")} + {new Date(value).toLocaleDateString("en-US")} ), }, { @@ -47,7 +48,7 @@ export const columns = [ key: "actions", width: 200, render: (_: any, d: any, __: number) => ( - + Remove @@ -55,7 +56,7 @@ export const columns = [ {" "} from your account. Are you sure you want to proceed? - + ), }, ]; @@ -63,40 +64,56 @@ export const columns = [ export const transactionColumns = [ { title: "Date", - dataIndex: "id", - key: "id", - width: 250, - render: (value: string) => {value}, + dataIndex: "createdAt", + key: "createdAt", + width: 200, + render: (createdAt: string) => { + const date = new Date(createdAt); + const [month, day, year] = [ + date.toLocaleString("default", { month: "long" }), + date.getDate(), + date.getFullYear(), + ]; + return ( + + + {month}, {day} {year} + + + ); + }, }, { title: "Description", - dataIndex: "lastFour", - key: "lastFour", - width: 150, - render: (value: string) => {value}, + dataIndex: "invoice", + key: "invoice", + width: 200, + render: (invoice: string) => Invoice for Premium Plan, }, { title: "Amount", dataIndex: "amount", key: "amount", width: 150, - render: (value: number) => {value}, + render: (amount: number) => { + const value = amount / 100; + const formatter = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }); + + return {formatter.format(value)}; + }, }, { title: "Actions", - dataIndex: "actions", - key: "actions", + dataIndex: "", + key: "", width: 200, - render: (_: any, d: any, __: number) => ( - - - Remove - - {` ${d.user.firstName} ${d.user.lastName} `} - {" "} - from your account. Are you sure you want to proceed? - - + render: () => ( + + PDF + ), }, ]; @@ -114,10 +131,8 @@ const HighlightRed = styled("span")` color: var(--red5); `; -const StyledCell = styled(Cell)` - display: flex; - justify-content: flex-start; - align-content: center; +const StyledDate = styled("div")` + padding-left: var(--p8); `; const StyledImage = styled("aside")` diff --git a/src/features/Account/TableRow.tsx b/src/features/Account/TableRow.tsx index 5bf4189..a44cd9e 100644 --- a/src/features/Account/TableRow.tsx +++ b/src/features/Account/TableRow.tsx @@ -12,7 +12,7 @@ const StyledRow = styled("tr")` padding: 0; div.table-cell { - box-shadow: 0 3px 6px rgb(0 0 0 / 10%), 0 10px 20px rgb(0 0 0 / 15%); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 3px 6px rgba(0, 0, 0, 0.15); } div.table-value { display: flex; diff --git a/src/features/Account/constants.ts b/src/features/Account/constants.ts index b8a868b..50d4c73 100644 --- a/src/features/Account/constants.ts +++ b/src/features/Account/constants.ts @@ -1,7 +1,7 @@ export const PremiumPlan = { eur: { - plan: "price_1OA00qIbOZLMWfd33rqyLFaL", amount: 1000, + plan: "price_1OA00qIbOZLMWfd33rqyLFaL", productId: "prod_OxvsvV2FW9nBhs", }, }; diff --git a/src/index.css b/src/index.css index 05e394c..948e673 100644 --- a/src/index.css +++ b/src/index.css @@ -199,14 +199,15 @@ h1 { } h2 { - font-size: var(--p18); + font-size: var(--p20); color: var(--gray7); font-family: ProximaNova-Bold; } h3 { font-size: var(--p16); - font-family: ProximaNova-Semibold; + color: var(--gray7); + font-family: ProximaNova-Bold; } header h1, header h2, diff --git a/src/types/subscription.ts b/src/types/subscription.ts index cfb1ec4..95738b2 100644 --- a/src/types/subscription.ts +++ b/src/types/subscription.ts @@ -1,16 +1,25 @@ import { PaymentMethod } from "@stripe/stripe-js"; +export enum SubscriptionStatus { + // Subscription Status Cleared represents a successfully cleared subscription. + Cleared, + // Subscription Status Refunded represents a refunded subscription. + Refunded, + // Subscription Status Cancelled represents a cancelled subscription. + Cancelled, +} + export interface SubscriptionInfo { subscription: Subscription; transactions: Transaction[]; - defaultPaymentMethod: PaymentMethod; + paymentMethod: PaymentMethod; } export interface Subscription { id: string; plan: string; transactionId: string; - statusId: string; + statusId: number; amount: number; tenantId: string; customerId: string;