From c8943a30d99ec3e19d5b09e38ae6a0666c934800 Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 17 Jan 2022 14:33:07 +0100 Subject: [PATCH 01/15] progress on new components --- .../denomination-badege.stories.tsx | 31 ++++++++ .../atoms/denomination-badge/index.tsx | 25 +++++++ .../atoms/status-indicator/index.tsx | 27 +++++++ .../status-indicator.stories.tsx | 24 ++++++ src/components/fundamentals/button/index.tsx | 2 +- .../fundamentals/icons/arrow-down-icon.tsx | 2 +- .../icons/edit-icon/edit-icon.stories.tsx | 24 ++++++ .../fundamentals/icons/edit-icon/index.tsx | 36 +++++++++ .../icons/icons-overview.stories.tsx | 6 ++ .../fundamentals/icons/publish-icon/index.tsx | 57 +++++++++++++++ .../publish-icon/publish-icon.stories.tsx | 24 ++++++ .../icons/unpublish-icon/index.tsx | 57 +++++++++++++++ .../unpublish-icon/unpublish-icon.stories.tsx | 24 ++++++ src/components/molecules/actionables.tsx | 11 ++- .../banner-card/banner-card.stories.tsx | 60 +++++++++++++++ .../organisms/banner-card/index.tsx | 73 +++++++++++++++++++ 16 files changed, 475 insertions(+), 8 deletions(-) create mode 100644 src/components/atoms/denomination-badge/denomination-badege.stories.tsx create mode 100644 src/components/atoms/denomination-badge/index.tsx create mode 100644 src/components/atoms/status-indicator/index.tsx create mode 100644 src/components/atoms/status-indicator/status-indicator.stories.tsx create mode 100644 src/components/fundamentals/icons/edit-icon/edit-icon.stories.tsx create mode 100644 src/components/fundamentals/icons/edit-icon/index.tsx create mode 100644 src/components/fundamentals/icons/publish-icon/index.tsx create mode 100644 src/components/fundamentals/icons/publish-icon/publish-icon.stories.tsx create mode 100644 src/components/fundamentals/icons/unpublish-icon/index.tsx create mode 100644 src/components/fundamentals/icons/unpublish-icon/unpublish-icon.stories.tsx create mode 100644 src/components/organisms/banner-card/banner-card.stories.tsx create mode 100644 src/components/organisms/banner-card/index.tsx diff --git a/src/components/atoms/denomination-badge/denomination-badege.stories.tsx b/src/components/atoms/denomination-badge/denomination-badege.stories.tsx new file mode 100644 index 0000000000..8b55cf50ce --- /dev/null +++ b/src/components/atoms/denomination-badge/denomination-badege.stories.tsx @@ -0,0 +1,31 @@ +import { ComponentMeta } from "@storybook/react" +import React from "react" +import DenominationBadge from "." + +export default { + title: "Atoms/DenominationBadge", + component: DenominationBadge, +} as ComponentMeta + +const Template = args => + +export const DanishKroner = Template.bind({}) +DanishKroner.args = { + amount: 1000, + currencyCode: "DKK", + conversion: 100, +} + +export const JapaneseYen = Template.bind({}) +JapaneseYen.args = { + amount: 250000, + currencyCode: "JPY", + conversion: 100, +} + +export const KoreanWon = Template.bind({}) +KoreanWon.args = { + amount: 10000000, + currencyCode: "KRW", + conversion: 100, +} diff --git a/src/components/atoms/denomination-badge/index.tsx b/src/components/atoms/denomination-badge/index.tsx new file mode 100644 index 0000000000..2903a30733 --- /dev/null +++ b/src/components/atoms/denomination-badge/index.tsx @@ -0,0 +1,25 @@ +import React from "react" + +type DenominationBadgeProps = { + amount: number + currencyCode: string + conversion?: number +} + +const DenominationBadge: React.FC = ({ + amount, + currencyCode, + conversion = 100, +}) => { + return ( +
+
+ + {amount / conversion} {currencyCode.toUpperCase()} + +
+
+ ) +} + +export default DenominationBadge diff --git a/src/components/atoms/status-indicator/index.tsx b/src/components/atoms/status-indicator/index.tsx new file mode 100644 index 0000000000..521c84265e --- /dev/null +++ b/src/components/atoms/status-indicator/index.tsx @@ -0,0 +1,27 @@ +import clsx from "clsx" +import React from "react" + +type StatusIndicatorProps = { + ok: boolean + okText: string + notOkText: string +} + +const StatusIndicator: React.FC = ({ + ok, + okText, + notOkText, +}) => { + return ( +
+
+

{ok ? okText : notOkText}

+
+ ) +} + +export default StatusIndicator diff --git a/src/components/atoms/status-indicator/status-indicator.stories.tsx b/src/components/atoms/status-indicator/status-indicator.stories.tsx new file mode 100644 index 0000000000..1ec33e1eee --- /dev/null +++ b/src/components/atoms/status-indicator/status-indicator.stories.tsx @@ -0,0 +1,24 @@ +import { ComponentMeta } from "@storybook/react" +import React from "react" +import StatusIndicator from "." + +export default { + title: "Atoms/StatusIndicator", + component: StatusIndicator, +} as ComponentMeta + +const Template = args => + +export const Ok = Template.bind({}) +Ok.args = { + ok: true, + okText: "Published", + notOkText: "Not published", +} + +export const NotOk = Template.bind({}) +NotOk.args = { + ok: false, + okText: "Published", + notOkText: "Not published", +} diff --git a/src/components/fundamentals/button/index.tsx b/src/components/fundamentals/button/index.tsx index e2b44be99d..b65fc2e296 100644 --- a/src/components/fundamentals/button/index.tsx +++ b/src/components/fundamentals/button/index.tsx @@ -42,7 +42,7 @@ const Button = React.forwardRef( + = ({ actions }) => { + )} +
+ ) +} + +const Footer: React.FC = ({ children }) => { + return
{children}
+} + +BannerCard.Description = Description +BannerCard.Footer = Footer + +export default BannerCard From dbb4285b79b66a1d571b83877c1a670f2d27c0ff Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 17 Jan 2022 16:57:05 +0100 Subject: [PATCH 02/15] progress --- package.json | 1 + .../atoms/denomination-badge/index.tsx | 7 +- src/components/molecules/actionables.tsx | 4 +- .../banner-card/banner-card.stories.tsx | 29 + .../organisms/banner-card/index.tsx | 8 +- .../gift-card-banner.stories.tsx | 553 ++++++++++++++++++ .../organisms/gift-card-banner/index.tsx | 119 ++++ tailwind.config.js | 2 +- yarn.lock | 5 + 9 files changed, 719 insertions(+), 9 deletions(-) create mode 100644 src/components/organisms/gift-card-banner/gift-card-banner.stories.tsx create mode 100644 src/components/organisms/gift-card-banner/index.tsx diff --git a/package.json b/package.json index a26f014d86..8fedceeb5a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@storybook/addon-postcss": "^2.0.0", "@storybook/react": "^6.4.9", "@storybook/theming": "^6.4.9", + "@tailwindcss/line-clamp": "^0.3.1", "autoprefixer": "^10.4.1", "babel-loader": "^8.1.0", "gatsby-plugin-postcss": "^5.4.0", diff --git a/src/components/atoms/denomination-badge/index.tsx b/src/components/atoms/denomination-badge/index.tsx index 2903a30733..eebc304795 100644 --- a/src/components/atoms/denomination-badge/index.tsx +++ b/src/components/atoms/denomination-badge/index.tsx @@ -4,18 +4,19 @@ type DenominationBadgeProps = { amount: number currencyCode: string conversion?: number -} +} & React.HTMLAttributes const DenominationBadge: React.FC = ({ amount, currencyCode, conversion = 100, + ...attributes }) => { return ( -
+
- {amount / conversion} {currencyCode.toUpperCase()} + {amount / conversion} {currencyCode?.toUpperCase()}
diff --git a/src/components/molecules/actionables.tsx b/src/components/molecules/actionables.tsx index dbcbb56ab3..4a168b0fb2 100644 --- a/src/components/molecules/actionables.tsx +++ b/src/components/molecules/actionables.tsx @@ -28,9 +28,9 @@ const Actionables: React.FC = ({ actions }) => {
- + {}, + icon: , + }, + { + label: "Unpublish", + onClick: () => {}, + icon: , + }, + { + label: "Delete", + onClick: () => {}, + icon: , + variant: "danger", + }, + ], + }, + text: + "For the one partial to blank canvases, spontaneity, chance encounters and plot twists. The Tekla Gift Card is available in either digital or hard-copy format. For the one partial to blank canvases, spontaneity, chance encounters and plot twists. The Tekla Gift Card is available in either digital or hard-copy format. For the one partial to blank canvases, spontaneity, chance encounters and plot twists. The Tekla Gift Card is available in either digital or hard-copy format.", +} diff --git a/src/components/organisms/banner-card/index.tsx b/src/components/organisms/banner-card/index.tsx index 09eca1aaba..215bc6fa5d 100644 --- a/src/components/organisms/banner-card/index.tsx +++ b/src/components/organisms/banner-card/index.tsx @@ -22,7 +22,7 @@ const BannerCard: React.FC & {
{thumbnail && ( -
+
Thumbnail = ({ }) => { return (
-

{children}

+

+ {children} +

{cta && ( diff --git a/src/components/molecules/banner-card/index.tsx b/src/components/molecules/banner-card/index.tsx index 215bc6fa5d..7c55eaad8a 100644 --- a/src/components/molecules/banner-card/index.tsx +++ b/src/components/molecules/banner-card/index.tsx @@ -19,7 +19,7 @@ const BannerCard: React.FC & { Footer: React.FC } = ({ title, thumbnail, actions, children }) => { return ( -
+
{thumbnail && (
@@ -31,11 +31,9 @@ const BannerCard: React.FC & {
)}
-
+

{title}

-
- -
+
{children}
diff --git a/src/components/molecules/denomination-grid/index.tsx b/src/components/molecules/denomination-grid/index.tsx deleted file mode 100644 index 9b13bf16d1..0000000000 --- a/src/components/molecules/denomination-grid/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useEffect, useMemo, useRef, useState } from "react" -import { useObserveWidth } from "../../../hooks/use-observe-width" -import DenominationBadge from "../../atoms/denomination-badge" - -export type GiftCardVariant = { - prices: { - currency_code: string - amount: number - }[] -} - -type DenominationGridProps = { - variants: GiftCardVariant[] - defaultCurrency: string -} - -const DenominationGrid: React.FC = ({ - variants, - defaultCurrency, -}) => { - const containerRef = useRef(null) - - const width = useObserveWidth(containerRef) - - const [columns, setColumns] = useState(7) - - // We estimate that a Badge is rouglhy 70px and use this to calulate how many we can fit in a row - // It is a bit of a "magic" number, but it works for now. - useEffect(() => { - if (width) { - setColumns(Math.floor(width / 70) - 1) - } - }, [width]) - - const defaultDenominations = useMemo(() => { - return ( - variants.map(variant => { - const price = variant.prices.find( - price => price.currency_code === defaultCurrency - ) - return { amount: price?.amount, currencyCode: defaultCurrency } - }) ?? [] - ) - }, [variants, defaultCurrency]) - - const [denominations, setDenominations] = useState< - { amount: number; currencyCode: string }[] | null - >([]) - const [remainder, setRemainder] = useState(0) - - useEffect(() => { - if (defaultDenominations.length) { - setDenominations(defaultDenominations.slice(0, columns)) - setRemainder(defaultDenominations.length - columns) - } - - return - }, [defaultDenominations, columns]) - - return ( -
- {denominations.map((denomination, index) => { - return ( - - ) - })} - {remainder > 0 && ( -
-
- +{remainder} -
-
- )} -
- ) -} - -export default DenominationGrid diff --git a/src/components/molecules/tag-grid.tsx/index.tsx b/src/components/molecules/tag-grid.tsx/index.tsx new file mode 100644 index 0000000000..8eec18a973 --- /dev/null +++ b/src/components/molecules/tag-grid.tsx/index.tsx @@ -0,0 +1,38 @@ +import React, { useRef } from "react" +import { useObserveWidth } from "../../../hooks/use-observe-width" +import Badge from "../../fundamentals/badge" + +export type GiftCardVariant = { + prices: { + currency_code: string + amount: number + }[] +} + +type TagGridProps = { + tags: string[] + badgeVariant: "primary" | "danger" | "success" | "warning" | "denomination" +} + +const TagGrid: React.FC = ({ tags, badgeVariant }) => { + const containerRef = useRef(null) + const width = useObserveWidth(containerRef) + const columns = Math.max(Math.floor(width / 70) - 1, 1) + const visibleTags = tags.slice(0, columns) + const remainder = tags.length - columns + + return ( +
+ {visibleTags?.map((tag, index) => { + return ( + + {tag} + + ) + })} + {remainder > 0 && +{remainder}} +
+ ) +} + +export default TagGrid diff --git a/src/components/organisms/gift-card-banner/index.tsx b/src/components/organisms/gift-card-banner/index.tsx index b6e35d8727..523cfb781a 100644 --- a/src/components/organisms/gift-card-banner/index.tsx +++ b/src/components/organisms/gift-card-banner/index.tsx @@ -1,13 +1,18 @@ -import React from "react" +import React, { useMemo } from "react" import StatusIndicator from "../../atoms/status-indicator" import EditIcon from "../../fundamentals/icons/edit-icon" import TrashIcon from "../../fundamentals/icons/trash-icon" import UnpublishIcon from "../../fundamentals/icons/unpublish-icon" import { ActionType } from "../../molecules/actionables" import BannerCard from "../../molecules/banner-card" -import DenominationGrid, { - GiftCardVariant, -} from "../../molecules/denomination-grid" +import TagGrid from "../../molecules/tag-grid.tsx" + +type GiftCardVariant = { + prices: { + currency_code: string + amount: number + }[] +} type GiftCardBannerProps = { title: string @@ -51,15 +56,26 @@ const GiftCardBanner: React.FC = ({ }, ] + const denominations = useMemo(() => { + return variants + .map((variant) => { + const price = variant.prices.find( + (price) => price.currency_code === defaultCurrency + ) + + if (!price) return "" + + return `${price.amount / 100} ${defaultCurrency.toUpperCase()}` + }) + .filter(Boolean) + }, [variants, defaultCurrency]) + return ( {description}
- + { +export const useObserveWidth = (ref: MutableRefObject): number => { const [currentWidth, setCurrentWidth] = useState(0) const observer = useRef( - new ResizeObserver(entries => { + new ResizeObserver((entries) => { const { width } = entries[0].contentRect setCurrentWidth(width) From 8c5b4d0a734248ff6d9c8da5d8cfc96d3e7f26b2 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 21 Jan 2022 11:48:50 +0100 Subject: [PATCH 07/15] update --- src/components/molecules/currency-input/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/currency-input/index.tsx b/src/components/molecules/currency-input/index.tsx index 596490af97..51537dd379 100644 --- a/src/components/molecules/currency-input/index.tsx +++ b/src/components/molecules/currency-input/index.tsx @@ -37,7 +37,8 @@ const CurrencyInput: React.FC = ({ ? `${emoji} ${code.toUpperCase()}` : `${code.toUpperCase()}`, symbol: symbol_native, - })) ?? [] + })) + .filter(Boolean) || [] ) }, [options, currencies, currentCurrency]) @@ -56,7 +57,7 @@ const CurrencyInput: React.FC = ({ enableSearch label="Currency" options={opts} - value={{ ...selected }} + value={selected} onChange={setSelected} className={className} required={required} From a45b676d6d58cfb725a4d640ff8be6e6a07b69e2 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 21 Jan 2022 14:27:15 +0100 Subject: [PATCH 08/15] fixed currencies --- .../denomination-badege.stories.tsx | 31 -- .../atoms/denomination-badge/index.tsx | 26 - .../atoms/status-indicator/index.tsx | 27 -- .../status-indicator.stories.tsx | 24 - .../index.tsx | 15 +- .../status-indicator.stories.tsx | 34 ++ .../gift-card-banner.stories.tsx | 449 +++++------------- .../organisms/gift-card-banner/index.tsx | 16 +- src/components/templates/user-table.tsx | 38 +- src/utils/{currencies.js => currencies.ts} | 48 +- src/utils/{prices.js => prices.ts} | 40 +- 11 files changed, 231 insertions(+), 517 deletions(-) delete mode 100644 src/components/atoms/denomination-badge/denomination-badege.stories.tsx delete mode 100644 src/components/atoms/denomination-badge/index.tsx delete mode 100644 src/components/atoms/status-indicator/index.tsx delete mode 100644 src/components/atoms/status-indicator/status-indicator.stories.tsx rename src/components/fundamentals/{status-dot => status-indicator}/index.tsx (70%) create mode 100644 src/components/fundamentals/status-indicator/status-indicator.stories.tsx rename src/utils/{currencies.js => currencies.ts} (97%) rename src/utils/{prices.js => prices.ts} (53%) diff --git a/src/components/atoms/denomination-badge/denomination-badege.stories.tsx b/src/components/atoms/denomination-badge/denomination-badege.stories.tsx deleted file mode 100644 index 8b55cf50ce..0000000000 --- a/src/components/atoms/denomination-badge/denomination-badege.stories.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { ComponentMeta } from "@storybook/react" -import React from "react" -import DenominationBadge from "." - -export default { - title: "Atoms/DenominationBadge", - component: DenominationBadge, -} as ComponentMeta - -const Template = args => - -export const DanishKroner = Template.bind({}) -DanishKroner.args = { - amount: 1000, - currencyCode: "DKK", - conversion: 100, -} - -export const JapaneseYen = Template.bind({}) -JapaneseYen.args = { - amount: 250000, - currencyCode: "JPY", - conversion: 100, -} - -export const KoreanWon = Template.bind({}) -KoreanWon.args = { - amount: 10000000, - currencyCode: "KRW", - conversion: 100, -} diff --git a/src/components/atoms/denomination-badge/index.tsx b/src/components/atoms/denomination-badge/index.tsx deleted file mode 100644 index eebc304795..0000000000 --- a/src/components/atoms/denomination-badge/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react" - -type DenominationBadgeProps = { - amount: number - currencyCode: string - conversion?: number -} & React.HTMLAttributes - -const DenominationBadge: React.FC = ({ - amount, - currencyCode, - conversion = 100, - ...attributes -}) => { - return ( -
-
- - {amount / conversion} {currencyCode?.toUpperCase()} - -
-
- ) -} - -export default DenominationBadge diff --git a/src/components/atoms/status-indicator/index.tsx b/src/components/atoms/status-indicator/index.tsx deleted file mode 100644 index 521c84265e..0000000000 --- a/src/components/atoms/status-indicator/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import clsx from "clsx" -import React from "react" - -type StatusIndicatorProps = { - ok: boolean - okText: string - notOkText: string -} - -const StatusIndicator: React.FC = ({ - ok, - okText, - notOkText, -}) => { - return ( -
-
-

{ok ? okText : notOkText}

-
- ) -} - -export default StatusIndicator diff --git a/src/components/atoms/status-indicator/status-indicator.stories.tsx b/src/components/atoms/status-indicator/status-indicator.stories.tsx deleted file mode 100644 index 1ec33e1eee..0000000000 --- a/src/components/atoms/status-indicator/status-indicator.stories.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentMeta } from "@storybook/react" -import React from "react" -import StatusIndicator from "." - -export default { - title: "Atoms/StatusIndicator", - component: StatusIndicator, -} as ComponentMeta - -const Template = args => - -export const Ok = Template.bind({}) -Ok.args = { - ok: true, - okText: "Published", - notOkText: "Not published", -} - -export const NotOk = Template.bind({}) -NotOk.args = { - ok: false, - okText: "Published", - notOkText: "Not published", -} diff --git a/src/components/fundamentals/status-dot/index.tsx b/src/components/fundamentals/status-indicator/index.tsx similarity index 70% rename from src/components/fundamentals/status-dot/index.tsx rename to src/components/fundamentals/status-indicator/index.tsx index 3356d41612..bdb58cb132 100644 --- a/src/components/fundamentals/status-dot/index.tsx +++ b/src/components/fundamentals/status-indicator/index.tsx @@ -1,12 +1,12 @@ -import React from "react" import clsx from "clsx" +import React from "react" -type StatusDotProps = { +type StatusIndicatorProps = { title: string variant: "primary" | "danger" | "warning" | "success" } & React.HTMLAttributes -const StatusDot: React.FC = ({ +const StatusIndicator: React.FC = ({ title, variant = "success", className, @@ -19,13 +19,16 @@ const StatusDot: React.FC = ({ "bg-violet-60": variant === "primary", }) return ( -
+
+ /> {title}
) } -export default StatusDot +export default StatusIndicator diff --git a/src/components/fundamentals/status-indicator/status-indicator.stories.tsx b/src/components/fundamentals/status-indicator/status-indicator.stories.tsx new file mode 100644 index 0000000000..4ebf82baf0 --- /dev/null +++ b/src/components/fundamentals/status-indicator/status-indicator.stories.tsx @@ -0,0 +1,34 @@ +import { ComponentMeta } from "@storybook/react" +import React from "react" +import StatusIndicator from "." + +export default { + title: "Fundamentals/StatusIndicator", + component: StatusIndicator, +} as ComponentMeta + +const Template = (args) => + +export const Success = Template.bind({}) +Success.args = { + variant: "success", + title: "Active", +} + +export const Danger = Template.bind({}) +Danger.args = { + variant: "danger", + title: "Expired", +} + +export const Warning = Template.bind({}) +Warning.args = { + variant: "warning", + title: "Pending", +} + +export const Primary = Template.bind({}) +Primary.args = { + variant: "primary", + title: "Go", +} diff --git a/src/components/organisms/gift-card-banner/gift-card-banner.stories.tsx b/src/components/organisms/gift-card-banner/gift-card-banner.stories.tsx index 07769f564b..c0c1756f7a 100644 --- a/src/components/organisms/gift-card-banner/gift-card-banner.stories.tsx +++ b/src/components/organisms/gift-card-banner/gift-card-banner.stories.tsx @@ -18,58 +18,29 @@ const GiftCardRes = { status: "published", thumbnail: "https://images.ctfassets.net/4g6al16haqoj/kZT0jwrTOTGbDpK3XlRZQ/acb10c53c1acdd53cf1336b5f26fbb10/giftcard.jpg", - profile_id: "sp_01FS6WQNNH36C2B2Z55PMRP6FE", - weight: null, - length: null, - height: null, - width: null, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - collection_id: null, - type_id: null, - discountable: false, - external_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T14:08:52.462Z", - deleted_at: null, - metadata: null, variants: [ { id: "variant_01FSM2R7EVZR9MRNWBC5WB14RT", title: "1", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ + { + id: "ma_01FSM2R7GSAW9XRZDZ24YNYRAT", + currency_code: "bhd", + amount: 100000, + variant_id: "variant_01FSM2R7EVZR9MRNWBC5WB14RT", + }, { id: "ma_01FSM2R7GSAW9XRZDZ24YNYRAT", currency_code: "usd", amount: 10000, - sale_amount: null, variant_id: "variant_01FSM2R7EVZR9MRNWBC5WB14RT", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, + }, + { + id: "ma_01FSM2R7GSAW9XRZDZ24YNYRAT", + currency_code: "krw", + amount: 100, + variant_id: "variant_01FSM2R7EVZR9MRNWBC5WB14RT", }, ], options: [ @@ -78,10 +49,6 @@ const GiftCardRes = { value: "100", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7EVZR9MRNWBC5WB14RT", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -89,36 +56,24 @@ const GiftCardRes = { id: "variant_01FSM2R7F8H1SXCH4N8DAT1EM9", title: "2", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ + { + id: "ma_01FSM2R7GTKMSA6QTMC2P2P116", + currency_code: "bhd", + amount: 200000, + variant_id: "variant_01FSM2R7F8H1SXCH4N8DAT1EM9", + }, { id: "ma_01FSM2R7GTKMSA6QTMC2P2P116", currency_code: "usd", amount: 20000, - sale_amount: null, variant_id: "variant_01FSM2R7F8H1SXCH4N8DAT1EM9", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, + }, + { + id: "ma_01FSM2R7GTKMSA6QTMC2P2P116", + currency_code: "krw", + amount: 200, + variant_id: "variant_01FSM2R7F8H1SXCH4N8DAT1EM9", }, ], options: [ @@ -127,10 +82,6 @@ const GiftCardRes = { value: "200", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7F8H1SXCH4N8DAT1EM9", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -138,36 +89,24 @@ const GiftCardRes = { id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", title: "3", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "bhd", + amount: 500000, + variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", + }, { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", currency_code: "usd", amount: 50000, - sale_amount: null, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, + }, + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "krw", + amount: 500, + variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", }, ], options: [ @@ -176,10 +115,6 @@ const GiftCardRes = { value: "500", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -187,48 +122,32 @@ const GiftCardRes = { id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", title: "3", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "bhd", + amount: 600000, + variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", + }, { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", currency_code: "usd", amount: 60000, - sale_amount: null, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, + }, + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "krw", + amount: 600, + variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", }, ], options: [ { id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", + value: "600", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -236,48 +155,32 @@ const GiftCardRes = { id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", title: "3", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "bhd", + amount: 700000, + variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", + }, { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", currency_code: "usd", amount: 70000, - sale_amount: null, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, + }, + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "krw", + amount: 700, + variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", }, ], options: [ { id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", + value: "700", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -285,97 +188,32 @@ const GiftCardRes = { id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", title: "3", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", - currency_code: "usd", + currency_code: "bhd", amount: 80000, - sale_amount: null, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, }, - ], - options: [ { - id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", - option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "usd", + amount: 80000, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, - ], - }, - { - id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - title: "3", - product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, - prices: [ { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", - currency_code: "usd", - amount: 90000, - sale_amount: null, + currency_code: "krw", + amount: 800, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, }, ], options: [ { id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", + value: "800", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -383,97 +221,32 @@ const GiftCardRes = { id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", title: "3", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", - currency_code: "usd", - amount: 100000, - sale_amount: null, + currency_code: "bhd", + amount: 900000, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, }, - ], - options: [ { - id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", - option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "usd", + amount: 90000, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, - ], - }, - { - id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - title: "3", - product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, - prices: [ { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", - currency_code: "usd", - amount: 50000, - sale_amount: null, + currency_code: "krw", + amount: 900, variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, }, ], options: [ { id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", + value: "900", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, @@ -481,53 +254,33 @@ const GiftCardRes = { id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", title: "3", product_id: "prod_01FSM2R78R63VKXFAG3G7F52CD", - sku: null, - barcode: null, - ean: null, - upc: null, - inventory_quantity: 0, - allow_backorder: false, - manage_inventory: true, - hs_code: null, - origin_country: null, - mid_code: null, - material: null, - weight: null, - length: null, - height: null, - width: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, prices: [ + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "bhd", + amount: 1000000, + }, { id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", currency_code: "usd", - amount: 50000, - sale_amount: null, - variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - region_id: null, - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, + amount: 100000, + }, + { + id: "ma_01FSM2R7GTRDQVMGCX03H8KM6X", + currency_code: "krw", + amount: 1000, }, ], options: [ { id: "optval_01FSM2R7FFYCE65H3F87YT065Z", - value: "500", + value: "1000", option_id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", variant_id: "variant_01FSM2R7FFW6Z6QY79Q2DZ2M57", - created_at: "2022-01-17T13:13:48.466Z", - updated_at: "2022-01-17T13:13:48.466Z", - deleted_at: null, - metadata: null, }, ], }, ], - images: [], options: [ { id: "opt_01FSM2R7AFY919FXW1T5FD6MV8", @@ -539,15 +292,31 @@ const GiftCardRes = { metadata: null, }, ], - tags: [], - type: null, - collection: null, } -const Template = args => +const Template = (args) => export const GiftCardPublished = Template.bind({}) GiftCardPublished.args = { ...GiftCardRes, defaultCurrency: "usd", } + +export const GiftCardUnpublished = Template.bind({}) +GiftCardUnpublished.args = { + ...GiftCardRes, + status: "unpublished", + defaultCurrency: "usd", +} + +export const GiftCardNoSubunitCurrency = Template.bind({}) +GiftCardNoSubunitCurrency.args = { + ...GiftCardRes, + defaultCurrency: "krw", +} + +export const GiftCardThreeDigitSubunitCurrency = Template.bind({}) +GiftCardThreeDigitSubunitCurrency.args = { + ...GiftCardRes, + defaultCurrency: "bhd", +} diff --git a/src/components/organisms/gift-card-banner/index.tsx b/src/components/organisms/gift-card-banner/index.tsx index 523cfb781a..fc3be52d21 100644 --- a/src/components/organisms/gift-card-banner/index.tsx +++ b/src/components/organisms/gift-card-banner/index.tsx @@ -1,8 +1,10 @@ import React, { useMemo } from "react" -import StatusIndicator from "../../atoms/status-indicator" +import { currencies } from "../../../utils/currencies" +import { normalizeAmount } from "../../../utils/prices" import EditIcon from "../../fundamentals/icons/edit-icon" import TrashIcon from "../../fundamentals/icons/trash-icon" import UnpublishIcon from "../../fundamentals/icons/unpublish-icon" +import StatusIndicator from "../../fundamentals/status-indicator" import { ActionType } from "../../molecules/actionables" import BannerCard from "../../molecules/banner-card" import TagGrid from "../../molecules/tag-grid.tsx" @@ -65,10 +67,13 @@ const GiftCardBanner: React.FC = ({ if (!price) return "" - return `${price.amount / 100} ${defaultCurrency.toUpperCase()}` + return `${normalizeAmount( + defaultCurrency, + price.amount + )} ${defaultCurrency.toUpperCase()}` }) .filter(Boolean) - }, [variants, defaultCurrency]) + }, [variants, defaultCurrency, currencies]) return ( @@ -77,9 +82,8 @@ const GiftCardBanner: React.FC = ({
diff --git a/src/components/templates/user-table.tsx b/src/components/templates/user-table.tsx index 5984b363fe..897d2c9e8b 100644 --- a/src/components/templates/user-table.tsx +++ b/src/components/templates/user-table.tsx @@ -1,12 +1,12 @@ import React, { useEffect, useState } from "react" import useMedusa from "../../hooks/use-medusa" +import Medusa from "../../services/api" import EditIcon from "../fundamentals/icons/edit-icon" import RefreshIcon from "../fundamentals/icons/refresh-icon" import TrashIcon from "../fundamentals/icons/trash-icon" -import StatusDot from "../fundamentals/status-dot" +import StatusIndicator from "../fundamentals/status-indicator" import SidebarTeamMember from "../molecules/sidebar-team-member" import Table from "../molecules/table" -import Medusa from "../../services/api" import DeletePrompt from "../organisms/delete-prompt" import EditUser from "../organisms/edit-user-modal" @@ -22,7 +22,7 @@ type UserTableProps = { triggerRefetch: () => void } -const getInviteStatus = invite => { +const getInviteStatus = (invite) => { return new Date(invite.expires_at) < new Date() ? "expired" : "pending" } @@ -134,9 +134,9 @@ const UserTable: React.FC = ({ {new Date(invite?.expires_at) < new Date() ? ( - + ) : ( - + )} @@ -155,32 +155,32 @@ const UserTable: React.FC = ({ { title: "Member", count: elements.filter( - e => e.entityType === "user" && e.entity.role === "member" + (e) => e.entityType === "user" && e.entity.role === "member" ).length, onClick: () => setShownElements( elements.filter( - e => e.entityType === "user" && e.entity.role === "member" + (e) => e.entityType === "user" && e.entity.role === "member" ) ), }, { title: "Admin", count: elements.filter( - e => e.entityType === "user" && e.entity.role === "admin" + (e) => e.entityType === "user" && e.entity.role === "admin" ).length, onClick: () => setShownElements( elements.filter( - e => e.entityType === "user" && e.entity.role === "admin" + (e) => e.entityType === "user" && e.entity.role === "admin" ) ), }, { title: "No team permissions", - count: elements.filter(e => e.entityType === "invite").length, + count: elements.filter((e) => e.entityType === "invite").length, onClick: () => - setShownElements(elements.filter(e => e.entityType === "invite")), + setShownElements(elements.filter((e) => e.entityType === "invite")), }, ], }, @@ -194,21 +194,21 @@ const UserTable: React.FC = ({ }, { title: "Active", - count: elements.filter(e => e.entityType === "user").length, + count: elements.filter((e) => e.entityType === "user").length, onClick: () => - setShownElements(elements.filter(e => e.entityType === "user")), + setShownElements(elements.filter((e) => e.entityType === "user")), }, { title: "Pending", count: elements.filter( - e => + (e) => e.entityType === "invite" && getInviteStatus(e.entity) === "pending" ).length, onClick: () => setShownElements( elements.filter( - e => + (e) => e.entityType === "invite" && getInviteStatus(e.entity) === "pending" ) @@ -217,14 +217,14 @@ const UserTable: React.FC = ({ { title: "Expired", count: elements.filter( - e => + (e) => e.entityType === "invite" && getInviteStatus(e.entity) === "expired" ).length, onClick: () => setShownElements( elements.filter( - e => + (e) => e.entityType === "invite" && getInviteStatus(e.entity) === "expired" ) @@ -237,7 +237,7 @@ const UserTable: React.FC = ({ const handleUserSearch = (term: string) => { setShownElements( elements.filter( - e => + (e) => e.entity?.first_name?.includes(term) || e.entity?.last_name?.includes(term) || e.entity?.email?.includes(term) || @@ -261,7 +261,7 @@ const UserTable: React.FC = ({ Status - {shownElements.map(e => e.tableElement)} + {shownElements.map((e) => e.tableElement)} {selectedUser && (deleteUser ? ( diff --git a/src/utils/currencies.js b/src/utils/currencies.ts similarity index 97% rename from src/utils/currencies.js rename to src/utils/currencies.ts index bcaa487593..229905abc0 100644 --- a/src/utils/currencies.js +++ b/src/utils/currencies.ts @@ -1,4 +1,18 @@ -export const currencies = { +type CurrencyType = { + symbol: string + name: string + symbol_native: string + decimal_digits: number + rounding: number + code: string + name_plural: string +} + +type CurrenciesType = { + [key: string]: CurrencyType +} + +export const currencies: CurrenciesType = { USD: { symbol: "$", name: "US Dollar", @@ -48,7 +62,7 @@ export const currencies = { symbol: "ALL", name: "Albanian Lek", symbol_native: "Lek", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "ALL", name_plural: "Albanian lekë", @@ -57,7 +71,7 @@ export const currencies = { symbol: "AMD", name: "Armenian Dram", symbol_native: "դր.", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "AMD", name_plural: "Armenian drams", @@ -228,7 +242,7 @@ export const currencies = { symbol: "CO$", name: "Colombian Peso", symbol_native: "$", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "COP", name_plural: "Colombian pesos", @@ -237,7 +251,7 @@ export const currencies = { symbol: "₡", name: "Costa Rican Colón", symbol_native: "₡", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "CRC", name_plural: "Costa Rican colóns", @@ -408,7 +422,7 @@ export const currencies = { symbol: "Ft", name: "Hungarian Forint", symbol_native: "Ft", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "HUF", name_plural: "Hungarian forints", @@ -417,7 +431,7 @@ export const currencies = { symbol: "Rp", name: "Indonesian Rupiah", symbol_native: "Rp", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "IDR", name_plural: "Indonesian rupiahs", @@ -462,7 +476,7 @@ export const currencies = { symbol: "Ikr", name: "Icelandic Króna", symbol_native: "kr", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "ISK", name_plural: "Icelandic krónur", @@ -552,7 +566,7 @@ export const currencies = { symbol: "LB£", name: "Lebanese Pound", symbol_native: "ل.ل.‏", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "LBP", name_plural: "Lebanese pounds", @@ -633,7 +647,7 @@ export const currencies = { symbol: "MMK", name: "Myanma Kyat", symbol_native: "K", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "MMK", name_plural: "Myanma kyats", @@ -651,7 +665,7 @@ export const currencies = { symbol: "MURs", name: "Mauritian Rupee", symbol_native: "MURs", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "MUR", name_plural: "Mauritian rupees", @@ -957,7 +971,7 @@ export const currencies = { symbol: "TSh", name: "Tanzanian Shilling", symbol_native: "TSh", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "TZS", name_plural: "Tanzanian shillings", @@ -975,7 +989,7 @@ export const currencies = { symbol: "USh", name: "Ugandan Shilling", symbol_native: "USh", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "UGX", name_plural: "Ugandan shillings", @@ -993,7 +1007,7 @@ export const currencies = { symbol: "UZS", name: "Uzbekistan Som", symbol_native: "UZS", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "UZS", name_plural: "Uzbekistan som", @@ -1038,7 +1052,7 @@ export const currencies = { symbol: "YR", name: "Yemeni Rial", symbol_native: "ر.ي.‏", - decimal_digits: 0, + decimal_digits: 2, rounding: 0, code: "YER", name_plural: "Yemeni rials", @@ -1052,13 +1066,13 @@ export const currencies = { code: "ZAR", name_plural: "South African rand", }, - ZMK: { + ZMW: { symbol: "ZK", name: "Zambian Kwacha", symbol_native: "ZK", decimal_digits: 0, rounding: 0, - code: "ZMK", + code: "ZMW", name_plural: "Zambian kwachas", }, ZWL: { diff --git a/src/utils/prices.js b/src/utils/prices.ts similarity index 53% rename from src/utils/prices.js rename to src/utils/prices.ts index 3938a3a399..b739c5c66d 100644 --- a/src/utils/prices.js +++ b/src/utils/prices.ts @@ -1,22 +1,15 @@ -const noDivisionCurrencies = ["krw"] - -export function normalizeAmount(currency, amount) { - let divisor = 100 - if (noDivisionCurrencies.includes(currency.toLowerCase())) { - divisor = 1 - } +import { currencies } from "./currencies" +export function normalizeAmount(currency: string, amount: number): number { + let divisor = getDecimalDigits(currency) return Math.floor(amount) / divisor } -export function displayAmount(currency, amount, decimals = 2) { - if (noDivisionCurrencies.includes(currency.toLowerCase())) { - return normalizeAmount(currency, amount) - } - +export function displayAmount(currency: string, amount: number) { const normalizedAmount = normalizeAmount(currency, amount) - - return normalizedAmount.toFixed(decimals) + return normalizedAmount.toFixed( + currencies[currency.toUpperCase()].decimal_digits + ) } export const extractUnitPrice = (item, region, withTax = true) => { @@ -24,7 +17,7 @@ export const extractUnitPrice = (item, region, withTax = true) => { if (itemPrice === undefined) { const regionPrice = item.prices.find( - p => p.currency_code === region.currency_code + (p) => p.currency_code === region.currency_code ) itemPrice = regionPrice.amount @@ -54,11 +47,16 @@ export const extractOptionPrice = (price, region) => { return `${amount} ${region.currency_code.toUpperCase()}` } -export function persistedPrice(currency, amount) { - let multiplier = 100 - if (noDivisionCurrencies.includes(currency.toLowerCase())) { - multiplier = 1 - } - +export function persistedPrice(currency: string, amount: number): number { + const multiplier = getDecimalDigits(currency) return Math.floor(amount) * multiplier } + +/** + * Checks the list of currencies and returns the divider/multiplier + * that should be used to calculate the persited and display amount. + */ +function getDecimalDigits(currency: string) { + const divisionDigits = currencies[currency.toUpperCase()].decimal_digits + return Math.pow(10, divisionDigits) +} From 4a9de05e35053ad232cc472757ef5bdbbc2020eb Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 24 Jan 2022 15:24:04 +0100 Subject: [PATCH 09/15] added new component to region settings + minor fixes to settings and story --- .../currency-input/currency-input.stories.tsx | 23 -- .../molecules/currency-input/index.tsx | 76 ------ .../currency-input/currency-input.stories.tsx | 41 ++++ .../organisms/currency-input/index.tsx | 224 ++++++++++++++++++ .../organisms/price-input/index.tsx | 129 ---------- .../price-input/price-input.stories.tsx | 34 --- src/domain/settings/regions/details.tsx | 61 +++-- src/domain/settings/regions/edit-shipping.tsx | 159 +++++++------ src/domain/settings/regions/new-shipping.tsx | 100 ++++---- src/domain/settings/regions/new.tsx | 57 ++--- src/domain/settings/regions/shipping.tsx | 12 +- src/types/shared.ts | 4 + src/utils/currencies.ts | 2 +- src/utils/prices.ts | 2 +- 14 files changed, 470 insertions(+), 454 deletions(-) delete mode 100644 src/components/molecules/currency-input/currency-input.stories.tsx delete mode 100644 src/components/molecules/currency-input/index.tsx create mode 100644 src/components/organisms/currency-input/currency-input.stories.tsx create mode 100644 src/components/organisms/currency-input/index.tsx delete mode 100644 src/components/organisms/price-input/index.tsx delete mode 100644 src/components/organisms/price-input/price-input.stories.tsx create mode 100644 src/types/shared.ts diff --git a/src/components/molecules/currency-input/currency-input.stories.tsx b/src/components/molecules/currency-input/currency-input.stories.tsx deleted file mode 100644 index 0519ae26f2..0000000000 --- a/src/components/molecules/currency-input/currency-input.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentMeta } from "@storybook/react" -import React from "react" -import CurrencyInput from "." - -export default { - title: "Molecules/CurrencyInput", - component: CurrencyInput, -} as ComponentMeta - -const Template = (args) => - -export const Select = Template.bind({}) -Select.args = { - options: ["USD", "EUR", "GBP", "DKK", "NOK", "SEK"], - onChange: (currency) => { - console.log(currency) - }, -} - -export const ReadOnly = Template.bind({}) -ReadOnly.args = { - currentCurrency: "USD", -} diff --git a/src/components/molecules/currency-input/index.tsx b/src/components/molecules/currency-input/index.tsx deleted file mode 100644 index 94330d9a55..0000000000 --- a/src/components/molecules/currency-input/index.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import clsx from "clsx" -import React, { useEffect, useMemo, useState } from "react" -import { currencies } from "../../../utils/currencies" -import Input from "../input" -import Select from "../select" - -type CurrencyInputProps = { - options?: string[] - currentCurrency?: string - required?: boolean - onCurrencyChange?: (currency) => void -} & React.InputHTMLAttributes - -const CurrencyInput: React.FC = ({ - options, - currentCurrency, - required = false, - onCurrencyChange, - className, - name, - ...props -}) => { - const isSelectable = options ? true : false - - const opts = useMemo(() => { - const codes = options - ? options.map((o) => o.toLowerCase()) - : [currentCurrency?.toLowerCase()] - return ( - Object.entries(currencies) - .filter(([_key, obj]) => codes.includes(obj.code.toLowerCase())) - .map(({ "1": { code, symbol_native, decimal_digits } }) => ({ - value: code.toLowerCase(), - label: `${code.toUpperCase()}`, - symbol: symbol_native, - digits: decimal_digits, - })) - .filter(Boolean) || [] - ) - }, [options, currencies, currentCurrency]) - - const [selected, setSelected] = useState( - opts.find(({ value }) => value === currentCurrency?.toLowerCase()) - ) - - useEffect(() => { - if (onCurrencyChange) { - onCurrencyChange(selected) - } - }, [selected]) - - return isSelectable ? ( - - ) -} - -export default CurrencyInput diff --git a/src/components/organisms/currency-input/currency-input.stories.tsx b/src/components/organisms/currency-input/currency-input.stories.tsx new file mode 100644 index 0000000000..36412f2566 --- /dev/null +++ b/src/components/organisms/currency-input/currency-input.stories.tsx @@ -0,0 +1,41 @@ +import { ComponentMeta } from "@storybook/react" +import React from "react" +import CurrencyInput from "." + +export default { + title: "Organisms/CurrencyInput", + component: CurrencyInput, +} as ComponentMeta + +const Template = (args) => + +export const Default = Template.bind({}) +Default.args = { + currentCurrency: "usd", + currencyCodes: ["usd", "eur", "gbp"], +} + +export const ReadOnly = Template.bind({}) +ReadOnly.args = { + currentCurrency: "usd", + readonly: true, +} + +const TemplateWithAmount = (args) => ( + + + +) + +export const WithAmount = TemplateWithAmount.bind({}) +WithAmount.args = { + currencyArgs: { + currentCurrency: "usd", + currencyCodes: ["usd", "eur", "krw"], + size: "small", + }, + amountArgs: { + label: "Price", + amount: 10000, + }, +} diff --git a/src/components/organisms/currency-input/index.tsx b/src/components/organisms/currency-input/index.tsx new file mode 100644 index 0000000000..3aa5ca43e7 --- /dev/null +++ b/src/components/organisms/currency-input/index.tsx @@ -0,0 +1,224 @@ +import clsx from "clsx" +import React, { useContext, useEffect, useRef, useState } from "react" +import AmountField from "react-currency-input-field" +import { Option } from "../../../types/shared" +import { currencies, CurrencyType } from "../../../utils/currencies" +import { displayAmount, persistedPrice } from "../../../utils/prices" +import MinusIcon from "../../fundamentals/icons/minus-icon" +import PlusIcon from "../../fundamentals/icons/plus-icon" +import InputContainer from "../../fundamentals/input-container" +import InputHeader from "../../fundamentals/input-header" +import Input from "../../molecules/input" +import Select from "../../molecules/select" + +type CurrencyInputProps = { + currencyCodes?: string[] + currentCurrency?: string + size?: "small" | "medium" | "full" + readOnly?: boolean + onChange?: (currencyCode: string) => void + className?: React.HTMLAttributes["className"] +} + +type CurrencyInputState = { + currencyInfo: CurrencyType | undefined +} + +type AmountInputProps = { + label: string + amount: number | undefined + required?: boolean + step?: number + allowNegative?: boolean + onChange?: (amount: number | undefined) => void +} + +const CurrencyContext = React.createContext({ + currencyInfo: undefined, +}) + +const getCurrencyInfo = (currencyCode?: string) => { + if (!currencyCode) return undefined + const currencyInfo = currencies[currencyCode.toUpperCase()] + return currencyInfo +} + +const CurrencyInput: React.FC & { + AmountInput: React.FC +} = ({ + currentCurrency, + currencyCodes, + size = "full", + readOnly = false, + onChange, + children, + className, +}) => { + const options: Option[] = + currencyCodes?.map((code) => ({ + label: code.toUpperCase(), + value: code, + })) ?? [] + + const [selectedCurrency, setSelectedCurrency] = useState< + CurrencyType | undefined + >(getCurrencyInfo(currentCurrency)) + + const [value, setValue] = useState
-

Requirements

-
-
- - -
-
- - -
-
+ {!shippingOption.is_return && ( + <> +

Requirements

+
+ + + + + + +
+ + )}

Danger Zone

diff --git a/src/domain/settings/regions/new-shipping.tsx b/src/domain/settings/regions/new-shipping.tsx index cb73c1e107..bc1ec3e7c0 100644 --- a/src/domain/settings/regions/new-shipping.tsx +++ b/src/domain/settings/regions/new-shipping.tsx @@ -9,7 +9,9 @@ import Button from "../../../components/fundamentals/button" import Input from "../../../components/molecules/input" import Modal from "../../../components/molecules/modal" import Select from "../../../components/molecules/select" +import CurrencyInput from "../../../components/organisms/currency-input" import useToaster from "../../../hooks/use-toaster" +import { Option } from "../../../types/shared" import { getErrorMessage } from "../../../utils/error-messages" import fulfillmentProvidersMapper from "../../../utils/fulfillment-providers.mapper" @@ -28,11 +30,21 @@ const NewShipping = ({ const [adminOnly, setAdminOnly] = useState(false) const [options, setOptions] = useState([]) const [selectedOption, setSelectedOption] = useState(null) - const [profileOptions, setProfileOptions] = useState([]) + const [profileOptions, setProfileOptions] = useState([]) const [selectedProfile, setSelectedProfile] = useState(null) const createShippingOption = useAdminCreateShippingOption() const toaster = useToaster() + useEffect(() => { + register("amount", { required: true }) + register("requirements.max_subtotal.amount") + register("requirements.min_subtotal.amount") + }, []) + + const handleAmountChange = (fieldName: string, amount?: number) => { + setValue(fieldName, amount) + } + const handleSave = (data: { name: string requirements: { amount: number; type: string }[] @@ -60,7 +72,7 @@ const NewShipping = ({ if (data.requirements) { reqs = Object.entries(data.requirements).reduce((acc, [key, value]) => { if (value.amount && value.amount > 0) { - acc.push({ type: key, amount: Math.round(value.amount * 100) }) + acc.push({ type: key, amount: value.amount }) return acc } else { return acc @@ -75,7 +87,7 @@ const NewShipping = ({ profile_id: data.profile_id?.value, requirements: reqs, price_type: "flat_rate", - amount: Math.round(data.amount * 100), + amount: data.amount, is_return: isReturn, provider_id, admin_only: adminOnly, @@ -160,25 +172,13 @@ const NewShipping = ({ placeholder="New Shipping Option" className="flex-grow" /> -

- - + handleAmountChange("amount", v)} + amount={undefined} /> -
+
- {loadingOptions ? ( + {!shipping_options ? ( { ) : ( shipping_options - .filter(o => o.is_return === false && o.region_id === region.id) - .map(option => { + .filter((o) => o.is_return === false && o.region_id === region.id) + .map((option) => { return (
{ ) : shipping_options ? ( shipping_options - .filter(o => o.is_return && o.region_id === region.id) - .map(option => { + .filter((o) => o.is_return && o.region_id === region.id) + .map((option) => { return (
Date: Mon, 24 Jan 2022 15:43:56 +0100 Subject: [PATCH 10/15] requested changes --- src/components/molecules/actionables.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/actionables.tsx b/src/components/molecules/actionables.tsx index 858e85beff..f184673c48 100644 --- a/src/components/molecules/actionables.tsx +++ b/src/components/molecules/actionables.tsx @@ -48,7 +48,7 @@ const Actionables: React.FC = ({ actions }) => {