From 6721b3a6ef55c75416f31a054a6ff0ad33b4f1f6 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 14:07:05 +0100 Subject: [PATCH 01/19] init --- src/domain/collections/details/index.tsx | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/domain/collections/details/index.tsx diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx new file mode 100644 index 0000000000..187237d8f2 --- /dev/null +++ b/src/domain/collections/details/index.tsx @@ -0,0 +1,55 @@ +import { useAdminCollection } from "medusa-react" +import React from "react" +import Spinner from "../../../components/atoms/spinner" +import Breadcrumb from "../../../components/molecules/breadcrumb" +import InfoTooltip from "../../../components/molecules/info-tooltip" +import InputField from "../../../components/molecules/input" +import BodyCard from "../../../components/organisms/body-card" + +type CollectionDetailsProps = { + id: string +} + +const CollectionDetails: React.FC = ({ id }) => { + const { collection, isLoading } = useAdminCollection(id) + + return ( +
+ + + {isLoading || !collection ? ( +
+ +
+ ) : ( +
+ + } + /> +
+ )} +
+ +
+ ) +} + +export default CollectionDetails From 02ac03a40e2f1dcfea7f3f4146cf865233355e8f Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 14:12:20 +0100 Subject: [PATCH 02/19] merged revamp --- src/domain/collections/details/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index 187237d8f2..cfd9251d76 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -1,5 +1,6 @@ import { useAdminCollection } from "medusa-react" import React from "react" +import { useForm } from "react-hook-form" import Spinner from "../../../components/atoms/spinner" import Breadcrumb from "../../../components/molecules/breadcrumb" import InfoTooltip from "../../../components/molecules/info-tooltip" @@ -13,6 +14,8 @@ type CollectionDetailsProps = { const CollectionDetails: React.FC = ({ id }) => { const { collection, isLoading } = useAdminCollection(id) + const { register, unregister, setValue, handleSubmit } = useForm() + return (
= ({ id }) => { } + prefix="/" + tooltip={ + + } />
)} From bd132966578565b0913455774dedda2a8745bfcc Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 17:05:29 +0100 Subject: [PATCH 03/19] progress --- .../fundamentals/icons/info-icon/index.tsx | 18 ++--- src/domain/collections/details/index.tsx | 80 ++++++++++++------- src/domain/collections/index.js | 4 +- 3 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/components/fundamentals/icons/info-icon/index.tsx b/src/components/fundamentals/icons/info-icon/index.tsx index 9b717fa360..723aca577e 100644 --- a/src/components/fundamentals/icons/info-icon/index.tsx +++ b/src/components/fundamentals/icons/info-icon/index.tsx @@ -18,23 +18,23 @@ const InfoIcon: React.FC = ({ ) diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index cfd9251d76..e11fe800c8 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -6,15 +6,17 @@ import Breadcrumb from "../../../components/molecules/breadcrumb" import InfoTooltip from "../../../components/molecules/info-tooltip" import InputField from "../../../components/molecules/input" import BodyCard from "../../../components/organisms/body-card" +import { RouteComponentProps } from "@reach/router" +import Actionables from "../../../components/molecules/actionables" +import TrashIcon from "../../../components/fundamentals/icons/trash-icon" +import Button from "../../../components/fundamentals/button" +import ReactJson from "react-json-view" -type CollectionDetailsProps = { - id: string -} - -const CollectionDetails: React.FC = ({ id }) => { - const { collection, isLoading } = useAdminCollection(id) +const CollectionDetails: React.FC = ({ location }) => { + const ensuredPath = location!.pathname.replace("/a/collections/", ``) + const { collection, isLoading, isError } = useAdminCollection(ensuredPath) - const { register, unregister, setValue, handleSubmit } = useForm() + const { register, unregister, setValue, handleSubmit, formState } = useForm() return (
@@ -23,37 +25,61 @@ const CollectionDetails: React.FC = ({ id }) => { previousBreadcrumb="Collections" previousRoute="/a/collections" /> - +
{isLoading || !collection ? (
- +
) : (
- - - } - /> +
+
+

+ {collection.title} +

+ {}, + variant: "danger", + icon: , + }, + ]} + /> +
+

+ /{collection.handle} +

+
+ {collection.metadata && ( +
+

Metadata

+
+ +
+
+ )}
)} - +
+
+ + +
) } diff --git a/src/domain/collections/index.js b/src/domain/collections/index.js index 7c729956af..21b92cff27 100644 --- a/src/domain/collections/index.js +++ b/src/domain/collections/index.js @@ -3,7 +3,7 @@ import { navigate } from "gatsby" import { Router } from "@reach/router" import { Text, Flex, Box } from "rebass" import qs from "query-string" -// import Details from "./details" +import CollectionDetails from "./details" import useMedusa from "../../hooks/use-medusa" @@ -183,7 +183,7 @@ const Collections = () => { return ( - {/*
*/} + ) } From 9b1a4572f8602e5d6213e5432f1647a2bf8b9a1e Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 18:08:08 +0100 Subject: [PATCH 04/19] created raw data component --- .../icons/clipboard-copy-icon/index.tsx | 50 +++++++++++++ src/components/molecules/view-raw/index.tsx | 75 +++++++++++++++++++ .../molecules/view-raw/view-raw.stories.tsx | 18 +++++ tailwind.config.js | 12 +++ 4 files changed, 155 insertions(+) create mode 100644 src/components/fundamentals/icons/clipboard-copy-icon/index.tsx create mode 100644 src/components/molecules/view-raw/index.tsx create mode 100644 src/components/molecules/view-raw/view-raw.stories.tsx diff --git a/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx b/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx new file mode 100644 index 0000000000..c2ad4311f7 --- /dev/null +++ b/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ClipboardCopyIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default ClipboardCopyIcon diff --git a/src/components/molecules/view-raw/index.tsx b/src/components/molecules/view-raw/index.tsx new file mode 100644 index 0000000000..aa21aaf15d --- /dev/null +++ b/src/components/molecules/view-raw/index.tsx @@ -0,0 +1,75 @@ +import React, { useState } from "react" +import ReactJson from "react-json-view" +import * as Collapsible from "@radix-ui/react-collapsible" +import ChevronDownIcon from "../../fundamentals/icons/chevron-down" +import Button from "../../fundamentals/button" +import ClipboardCopyIcon from "../../fundamentals/icons/clipboard-copy-icon" +import clsx from "clsx" +import useClipboard from "../../../hooks/use-clipboard" + +type ViewRawProps = { + raw: object + title?: string +} + +const ViewRaw: React.FC = ({ raw, title = "Data" }) => { + const [expanded, setExpanded] = useState(false) + const [isCopied, handleCopy] = useClipboard( + JSON.stringify(raw, undefined, 2), + { + successDuration: 5500, + onCopied: () => {}, + } + ) + + return ( +
+ + +
+

+ {title}{" "} + + ({Object.keys(raw).length}) + +

+ +
+
+ +
+ +
+
+ {isCopied && Copied!} + +
+
+
+
+ ) +} + +export default ViewRaw diff --git a/src/components/molecules/view-raw/view-raw.stories.tsx b/src/components/molecules/view-raw/view-raw.stories.tsx new file mode 100644 index 0000000000..42b82cedd3 --- /dev/null +++ b/src/components/molecules/view-raw/view-raw.stories.tsx @@ -0,0 +1,18 @@ +import { ComponentMeta, ComponentStory } from "@storybook/react" +import React from "react" +import ViewRaw from "." + +export default { + title: "Molecules/ViewRaw", + component: ViewRaw, +} as ComponentMeta + +const Template: ComponentStory = (args) => + +export const Default = Template.bind({}) +Default.args = { + raw: { + test: true, + valid_days: ["monday", "wednesday", "friday"], + }, +} diff --git a/tailwind.config.js b/tailwind.config.js index dfdf2ea768..528df89931 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -248,12 +248,24 @@ module.exports = { "0%": { transform: "rotate(0deg)" }, "100%": { transform: "rotate(360deg)" }, }, + "fade-in-right": { + "0%": { + opacity: "0", + transform: "translateX(10px)", + }, + "100%": { + opacity: "1", + transform: "translateX(0)", + }, + }, }, transitionProperty: { width: "width margin", }, animation: { ring: "ring 2.2s cubic-bezier(0.5, 0, 0.5, 1) infinite", + "fade-in-right": + "fade-in-right 0.5s cubic-bezier(0.5, 0, 0.5, 1) forwards", }, lineClamp: { "[var(--lines)]": "var(--lines)", From 932f270e4d9b6f2d7e1ad66808ae42953f7269a6 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 18:13:46 +0100 Subject: [PATCH 05/19] finalize component --- .../icons/clipboard-copy-icon/index.tsx | 50 +++++++++++++ src/components/molecules/view-raw/index.tsx | 75 +++++++++++++++++++ .../molecules/view-raw/view-raw.stories.tsx | 18 +++++ tailwind.config.js | 12 +++ 4 files changed, 155 insertions(+) create mode 100644 src/components/fundamentals/icons/clipboard-copy-icon/index.tsx create mode 100644 src/components/molecules/view-raw/index.tsx create mode 100644 src/components/molecules/view-raw/view-raw.stories.tsx diff --git a/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx b/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx new file mode 100644 index 0000000000..c2ad4311f7 --- /dev/null +++ b/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ClipboardCopyIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default ClipboardCopyIcon diff --git a/src/components/molecules/view-raw/index.tsx b/src/components/molecules/view-raw/index.tsx new file mode 100644 index 0000000000..893ce003c0 --- /dev/null +++ b/src/components/molecules/view-raw/index.tsx @@ -0,0 +1,75 @@ +import React, { useState } from "react" +import ReactJson from "react-json-view" +import * as Collapsible from "@radix-ui/react-collapsible" +import ChevronDownIcon from "../../fundamentals/icons/chevron-down" +import Button from "../../fundamentals/button" +import clsx from "clsx" +import useClipboard from "../../../hooks/use-clipboard" +import ClipboardCopyIcon from "../../fundamentals/icons/clipboard-copy-icon" + +type ViewRawProps = { + raw: object + title?: string +} + +const ViewRaw: React.FC = ({ raw, title = "Data" }) => { + const [expanded, setExpanded] = useState(false) + const [isCopied, handleCopy] = useClipboard( + JSON.stringify(raw, undefined, 2), + { + successDuration: 5500, + onCopied: () => {}, + } + ) + + return ( +
+ + +
+

+ {title}{" "} + + ({Object.keys(raw).length}) + +

+ +
+
+ +
+ +
+
+ {isCopied && Copied!} + +
+
+
+
+ ) +} + +export default ViewRaw diff --git a/src/components/molecules/view-raw/view-raw.stories.tsx b/src/components/molecules/view-raw/view-raw.stories.tsx new file mode 100644 index 0000000000..42b82cedd3 --- /dev/null +++ b/src/components/molecules/view-raw/view-raw.stories.tsx @@ -0,0 +1,18 @@ +import { ComponentMeta, ComponentStory } from "@storybook/react" +import React from "react" +import ViewRaw from "." + +export default { + title: "Molecules/ViewRaw", + component: ViewRaw, +} as ComponentMeta + +const Template: ComponentStory = (args) => + +export const Default = Template.bind({}) +Default.args = { + raw: { + test: true, + valid_days: ["monday", "wednesday", "friday"], + }, +} diff --git a/tailwind.config.js b/tailwind.config.js index dfdf2ea768..528df89931 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -248,12 +248,24 @@ module.exports = { "0%": { transform: "rotate(0deg)" }, "100%": { transform: "rotate(360deg)" }, }, + "fade-in-right": { + "0%": { + opacity: "0", + transform: "translateX(10px)", + }, + "100%": { + opacity: "1", + transform: "translateX(0)", + }, + }, }, transitionProperty: { width: "width margin", }, animation: { ring: "ring 2.2s cubic-bezier(0.5, 0, 0.5, 1) infinite", + "fade-in-right": + "fade-in-right 0.5s cubic-bezier(0.5, 0, 0.5, 1) forwards", }, lineClamp: { "[var(--lines)]": "var(--lines)", From df756efac42857e1daf196f3637d4a9ed66d97d7 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 18:19:05 +0100 Subject: [PATCH 06/19] fixed chevron icon --- src/components/fundamentals/icons/chevron-up.tsx | 6 +++--- src/components/molecules/view-raw/view-raw.stories.tsx | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/fundamentals/icons/chevron-up.tsx b/src/components/fundamentals/icons/chevron-up.tsx index 153a2e251f..750baa77a1 100644 --- a/src/components/fundamentals/icons/chevron-up.tsx +++ b/src/components/fundamentals/icons/chevron-up.tsx @@ -18,9 +18,9 @@ const ChevronUpIcon: React.FC = ({ ) diff --git a/src/components/molecules/view-raw/view-raw.stories.tsx b/src/components/molecules/view-raw/view-raw.stories.tsx index 42b82cedd3..06e8eda589 100644 --- a/src/components/molecules/view-raw/view-raw.stories.tsx +++ b/src/components/molecules/view-raw/view-raw.stories.tsx @@ -10,9 +10,11 @@ export default { const Template: ComponentStory = (args) => export const Default = Template.bind({}) +const product = { + test: true, + valid_days: ["monday", "wednesday", "friday"], +} + Default.args = { - raw: { - test: true, - valid_days: ["monday", "wednesday", "friday"], - }, + raw: product, } From afacd71f08edc922f89c3d0b994017bd76996acd Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 1 Feb 2022 18:22:28 +0100 Subject: [PATCH 07/19] added optional name --- src/components/molecules/view-raw/index.tsx | 4 +++- src/components/molecules/view-raw/view-raw.stories.tsx | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/molecules/view-raw/index.tsx b/src/components/molecules/view-raw/index.tsx index 893ce003c0..eb1ea97bcb 100644 --- a/src/components/molecules/view-raw/index.tsx +++ b/src/components/molecules/view-raw/index.tsx @@ -10,9 +10,10 @@ import ClipboardCopyIcon from "../../fundamentals/icons/clipboard-copy-icon" type ViewRawProps = { raw: object title?: string + name?: string } -const ViewRaw: React.FC = ({ raw, title = "Data" }) => { +const ViewRaw: React.FC = ({ raw, title = "Data", name }) => { const [expanded, setExpanded] = useState(false) const [isCopied, handleCopy] = useClipboard( JSON.stringify(raw, undefined, 2), @@ -53,6 +54,7 @@ const ViewRaw: React.FC = ({ raw, title = "Data" }) => { fontFamily: "Roboto Mono", fontSize: "12px", }} + name={name} />
diff --git a/src/components/molecules/view-raw/view-raw.stories.tsx b/src/components/molecules/view-raw/view-raw.stories.tsx index 06e8eda589..73b4876cdb 100644 --- a/src/components/molecules/view-raw/view-raw.stories.tsx +++ b/src/components/molecules/view-raw/view-raw.stories.tsx @@ -10,11 +10,17 @@ export default { const Template: ComponentStory = (args) => export const Default = Template.bind({}) -const product = { +const metadata = { test: true, valid_days: ["monday", "wednesday", "friday"], } Default.args = { - raw: product, + raw: metadata, +} + +export const WithName = Template.bind({}) +WithName.args = { + raw: metadata, + name: "metadata", } From edca54705c8a620ee7e7ae03ca5b72c27a4467b1 Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 11:34:58 +0100 Subject: [PATCH 08/19] progress on table --- src/components/molecules/view-raw/index.tsx | 5 +- .../add-metadata/add-metadata.stories.tsx | 20 -- .../{add-metadata => metadata}/index.tsx | 49 +++-- .../organisms/metadata/metadata.stories.tsx | 19 ++ .../add-collection-modal.stories.tsx | 10 +- .../index.tsx | 63 ++++-- .../collection-product-table/index.tsx | 67 +++++++ src/domain/collections/details/index.tsx | 185 ++++++++++++------ src/domain/collections/index.js | 4 +- src/domain/gift-cards/overview.tsx | 4 +- tailwind.config.js | 2 +- 11 files changed, 310 insertions(+), 118 deletions(-) delete mode 100644 src/components/organisms/add-metadata/add-metadata.stories.tsx rename src/components/organisms/{add-metadata => metadata}/index.tsx (66%) create mode 100644 src/components/organisms/metadata/metadata.stories.tsx rename src/components/templates/{add-collection-modal => collection-modal}/add-collection-modal.stories.tsx (57%) rename src/components/templates/{add-collection-modal => collection-modal}/index.tsx (55%) create mode 100644 src/components/templates/collection-product-table/index.tsx diff --git a/src/components/molecules/view-raw/index.tsx b/src/components/molecules/view-raw/index.tsx index eb1ea97bcb..8f9399bf63 100644 --- a/src/components/molecules/view-raw/index.tsx +++ b/src/components/molecules/view-raw/index.tsx @@ -63,7 +63,10 @@ const ViewRaw: React.FC = ({ raw, title = "Data", name }) => { variant="ghost" size="small" type="button" - onClick={handleCopy} + onClick={(e) => { + e.currentTarget.blur() + handleCopy() + }} > diff --git a/src/components/organisms/add-metadata/add-metadata.stories.tsx b/src/components/organisms/add-metadata/add-metadata.stories.tsx deleted file mode 100644 index 1aa2e722e7..0000000000 --- a/src/components/organisms/add-metadata/add-metadata.stories.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { ComponentMeta, ComponentStory } from "@storybook/react" -import React from "react" -import AddMetadata from "." - -export default { - title: "Organisms/AddMetadata", - component: AddMetadata, -} as ComponentMeta - -const Template: ComponentStory = (args) => ( -
- -
-) - -export const Default = Template.bind({}) -Default.args = { - register: () => {}, - unregister: () => {}, -} diff --git a/src/components/organisms/add-metadata/index.tsx b/src/components/organisms/metadata/index.tsx similarity index 66% rename from src/components/organisms/add-metadata/index.tsx rename to src/components/organisms/metadata/index.tsx index 52a8b3c676..13b4fc9cab 100644 --- a/src/components/organisms/add-metadata/index.tsx +++ b/src/components/organisms/metadata/index.tsx @@ -1,26 +1,26 @@ -import React from "react" +import React, { useEffect } from "react" import Button from "../../fundamentals/button" import PlusIcon from "../../fundamentals/icons/plus-icon" import TrashIcon from "../../fundamentals/icons/trash-icon" import InputField from "../../molecules/input" type AddMetadataProps = { - register: Function - unregister: Function + control: any heading?: string + existingMetadata?: object } -type KeyPair = { +type KP = { key: string value: any } -const AddMetadata: React.FC = ({ - register, - unregister, +const Metadata: React.FC = ({ + control, heading = "Metadata", + existingMetadata, }) => { - const [keyPairs, setKeyPairs] = React.useState([]) + const [keyPairs, setKeyPairs] = React.useState([]) const addKeyPair = () => { const baseName = `metadata.${keyPairs.length}` @@ -32,12 +32,29 @@ const AddMetadata: React.FC = ({ } const deleteKeyPair = (index: number) => { - unregister(keyPairs[index].key) - unregister(keyPairs[index].value) + control.unregister(keyPairs[index].key) + control.unregister(keyPairs[index].value) setKeyPairs(keyPairs.filter((_, i) => i !== index)) } + useEffect(() => { + if (existingMetadata) { + for (const key of Object.keys(existingMetadata)) { + control.register(`metadata.${keyPairs.length}.key`) + control.register(`metadata.${keyPairs.length}.value`) + + addKeyPair() + + control.setValue(`metadata.${keyPairs.length}.key`, key) + control.setValue( + `metadata.${keyPairs.length}.value`, + existingMetadata[key] + ) + } + } + }, [existingMetadata]) + return (
{heading} @@ -49,7 +66,11 @@ const AddMetadata: React.FC = ({ index={index} onDelete={deleteKeyPair} > - + ) })} @@ -79,10 +100,10 @@ const KeyPair: React.FC = ({ k, v, register }) => { return (
- +
- +
) @@ -114,4 +135,4 @@ const DeletableElement: React.FC = ({ ) } -export default AddMetadata +export default Metadata diff --git a/src/components/organisms/metadata/metadata.stories.tsx b/src/components/organisms/metadata/metadata.stories.tsx new file mode 100644 index 0000000000..a740dab6da --- /dev/null +++ b/src/components/organisms/metadata/metadata.stories.tsx @@ -0,0 +1,19 @@ +import { ComponentMeta, ComponentStory } from "@storybook/react" +import React from "react" +import Metadata from "." + +export default { + title: "Organisms/Metadata", + component: Metadata, +} as ComponentMeta + +const Template: ComponentStory = (args) => ( +
+ +
+) + +export const Default = Template.bind({}) +Default.args = { + control: {}, +} diff --git a/src/components/templates/add-collection-modal/add-collection-modal.stories.tsx b/src/components/templates/collection-modal/add-collection-modal.stories.tsx similarity index 57% rename from src/components/templates/add-collection-modal/add-collection-modal.stories.tsx rename to src/components/templates/collection-modal/add-collection-modal.stories.tsx index 2117ec11eb..0e52e928b2 100644 --- a/src/components/templates/add-collection-modal/add-collection-modal.stories.tsx +++ b/src/components/templates/collection-modal/add-collection-modal.stories.tsx @@ -1,14 +1,14 @@ import { ComponentMeta, ComponentStory } from "@storybook/react" import React from "react" -import AddCollectionModal from "." +import CollectionModal from "." export default { title: "Template/AddCollectionModal", - component: AddCollectionModal, -} as ComponentMeta + component: CollectionModal, +} as ComponentMeta -const Template: ComponentStory = (args) => ( - +const Template: ComponentStory = (args) => ( + ) export const Default = Template.bind({}) diff --git a/src/components/templates/add-collection-modal/index.tsx b/src/components/templates/collection-modal/index.tsx similarity index 55% rename from src/components/templates/add-collection-modal/index.tsx rename to src/components/templates/collection-modal/index.tsx index 3ec52b4245..751ac7ed7f 100644 --- a/src/components/templates/add-collection-modal/index.tsx +++ b/src/components/templates/collection-modal/index.tsx @@ -1,28 +1,59 @@ -import React from "react" +import React, { useEffect } from "react" import { useForm } from "react-hook-form" import Button from "../../fundamentals/button" import InfoTooltip from "../../molecules/info-tooltip" import InputField from "../../molecules/input" import Modal from "../../molecules/modal" -import AddMetadata from "../../organisms/add-metadata" +import Metadata from "../../organisms/metadata" -type AddCollectionModalProps = { +type CollectionModalProps = { onClose: () => void onSubmit: (values: any) => void + isEdit?: boolean + collection?: any } -const AddCollectionModal: React.FC = ({ +const CollectionModal: React.FC = ({ onClose, onSubmit, + isEdit = false, + collection, }) => { - const { register, unregister, handleSubmit } = useForm() + const { register, setValue, handleSubmit, control } = useForm() + + if (isEdit && !collection) { + throw new Error("Collection is required for edit") + } + + useEffect(() => { + register("title", { required: true }) + register("handle") + }, []) + + useEffect(() => { + if (isEdit && collection) { + setValue("title", collection.title) + setValue("handle", collection.handle) + + if (collection.metadata) { + Object.entries(collection.metadata).map(([key, value], i) => { + register(`metadata.${i}.key`, { required: true }) + register(`metadata.${i}.value`, { required: true }) + setValue(`metadata.${i}.key`, key) + setValue(`metadata.${i}.value`, value) + }) + } + } + }, [collection, isEdit]) return (
-

Add Collection

+

+ {isEdit ? "Edit Collection" : "Add Collection"} +

To create a collection, all you need is a title and a handle.

@@ -37,32 +68,40 @@ const AddCollectionModal: React.FC = ({ label="Title" required placeholder="Sunglasses" - ref={register({ required: true })} name="title" + ref={register({ required: true })} /> } + ref={register} />
- +
-
@@ -72,4 +111,4 @@ const AddCollectionModal: React.FC = ({ ) } -export default AddCollectionModal +export default CollectionModal diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx new file mode 100644 index 0000000000..1c570a1207 --- /dev/null +++ b/src/components/templates/collection-product-table/index.tsx @@ -0,0 +1,67 @@ +import React, { useEffect, useState } from "react" +import Spinner from "../../atoms/spinner" +import Table from "../../molecules/table" +import { FilteringOptionProps } from "../../molecules/table/filtering-option" + +type CollectionProductTableProps = { + loadingProducts: boolean + products?: any[] + handleSearch: (value: string) => void +} + +const CollectionProductTable: React.FC = ({ + loadingProducts, + products, + handleSearch, +}) => { + const [filteringOptions, setFilteringOptions] = useState< + FilteringOptionProps[] + >([]) + + useEffect(() => { + setFilteringOptions([ + { + title: "Sort by", + options: [ + { + title: "All", + onClick: () => {}, + }, + { + title: "Newest", + onClick: () => {}, + }, + { + title: "Oldest", + onClick: () => {}, + }, + ], + }, + ]) + }, [products]) + + return ( +
+ + {loadingProducts && !products ? ( +
+ +
+ ) : ( +
+ {products?.map((product, index) => ( +

{JSON.stringify(product, undefined, 2)}

+ ))} +
+ )} +
+
+ ) +} + +export default CollectionProductTable diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index e11fe800c8..35df35f0cc 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -1,86 +1,147 @@ -import { useAdminCollection } from "medusa-react" -import React from "react" +import { + useAdminCollection, + useAdminDeleteCollection, + useAdminUpdateCollection, +} from "medusa-react" +import React, { useState } from "react" import { useForm } from "react-hook-form" import Spinner from "../../../components/atoms/spinner" import Breadcrumb from "../../../components/molecules/breadcrumb" -import InfoTooltip from "../../../components/molecules/info-tooltip" -import InputField from "../../../components/molecules/input" import BodyCard from "../../../components/organisms/body-card" import { RouteComponentProps } from "@reach/router" import Actionables from "../../../components/molecules/actionables" import TrashIcon from "../../../components/fundamentals/icons/trash-icon" import Button from "../../../components/fundamentals/button" -import ReactJson from "react-json-view" +import ViewRaw from "../../../components/molecules/view-raw" +import EditIcon from "../../../components/fundamentals/icons/edit-icon" +import CollectionModal from "../../../components/templates/collection-modal" +import CollectionProductTable from "../../../components/templates/collection-product-table" +import DeletePrompt from "../../../components/organisms/delete-prompt" +import { navigate } from "gatsby" const CollectionDetails: React.FC = ({ location }) => { const ensuredPath = location!.pathname.replace("/a/collections/", ``) const { collection, isLoading, isError } = useAdminCollection(ensuredPath) + const deleteCollection = useAdminDeleteCollection(ensuredPath) + const updateCollection = useAdminUpdateCollection(ensuredPath) + const [showEdit, setShowEdit] = useState(false) + const [showDelete, setShowDelete] = useState(false) + + const handleDelete = () => { + deleteCollection.mutate(undefined, { + onSuccess: () => navigate(`/a/collections`), + }) + } + + const handleUpdateDetails = (data: any) => { + const payload: { + title: string + handle?: string + metadata?: object + } = { + title: data.title, + handle: data.handle, + } + + if (data.metadata) { + const metadata = data.metadata.reduce((acc, next) => { + return { + ...acc, + [next.key]: next.value, + } + }, {}) + + payload.metadata = metadata + } + + updateCollection.mutate(payload, { + onSuccess: () => setShowEdit(false), + }) + } const { register, unregister, setValue, handleSubmit, formState } = useForm() return ( -
- -
- {isLoading || !collection ? ( -
- -
- ) : ( -
-
-
-

- {collection.title} -

- {}, - variant: "danger", - icon: , - }, - ]} - /> -
-

- /{collection.handle} -

+ <> +
+ +
+ {isLoading || !collection ? ( +
+
- {collection.metadata && ( -
-

Metadata

-
- +
+
+

+ {collection.title} +

+ setShowEdit(true), + icon: , + }, + { + label: "Delete", + onClick: () => setShowDelete(!showDelete), + variant: "danger", + icon: , + }, + ]} />
+

+ /{collection.handle} +

- )} -
- )} -
- -
- - + {collection.metadata && ( +
+

Metadata

+
+ +
+
+ )} +
+ )} +
+ + +
-
+ {showEdit && ( + setShowEdit(!showEdit)} + onSubmit={handleUpdateDetails} + isEdit + collection={collection} + /> + )} + {showDelete && ( + setShowDelete(!showDelete)} + heading="Delete collection" + successText="Successfully deleted collection" + onDelete={async () => handleDelete()} + confirmText="Yes, delete" + /> + )} + ) } diff --git a/src/domain/collections/index.js b/src/domain/collections/index.js index 21b92cff27..3c1c8f8d6d 100644 --- a/src/domain/collections/index.js +++ b/src/domain/collections/index.js @@ -18,7 +18,7 @@ import { } from "../../components/table" import Button from "../../components/button" import Spinner from "../../components/spinner" -import AddCollectionModal from "../../components/templates/add-collection-modal" +import CollectionModal from "../../components/templates/collection-modal" import { useAdminCreateCollection } from "medusa-react" import useToaster from "../../hooks/use-toaster" import { getErrorMessage } from "../../utils/error-messages" @@ -170,7 +170,7 @@ const CollectionsIndex = () => { {showNew && ( - setShowNew(!showNew)} onSubmit={handleAddNew} /> diff --git a/src/domain/gift-cards/overview.tsx b/src/domain/gift-cards/overview.tsx index 7b7dff3d1d..81323f6984 100644 --- a/src/domain/gift-cards/overview.tsx +++ b/src/domain/gift-cards/overview.tsx @@ -39,7 +39,9 @@ const Overview: React.FC = ({ ] const giftCardWithCurrency = useMemo(() => { - if (!giftCard || !store) return null + if (!giftCard || !store) { + return null + } return { ...giftCard, defaultCurrency: store.default_currency_code } }, [giftCard, store]) diff --git a/tailwind.config.js b/tailwind.config.js index 528df89931..556d1a1189 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -265,7 +265,7 @@ module.exports = { animation: { ring: "ring 2.2s cubic-bezier(0.5, 0, 0.5, 1) infinite", "fade-in-right": - "fade-in-right 0.5s cubic-bezier(0.5, 0, 0.5, 1) forwards", + "fade-in-right 0.3s cubic-bezier(0.5, 0, 0.5, 1) forwards", }, lineClamp: { "[var(--lines)]": "var(--lines)", From a92ad34f16ac8651ca4657a2d1aca162d57328e2 Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 13:34:53 +0100 Subject: [PATCH 09/19] progress on add product --- .../organisms/add-product-modal/index.tsx | 49 +++++++++++++++++++ .../collection-product-table/index.tsx | 18 +++++-- src/domain/collections/details/index.tsx | 28 +++++++++-- 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/components/organisms/add-product-modal/index.tsx diff --git a/src/components/organisms/add-product-modal/index.tsx b/src/components/organisms/add-product-modal/index.tsx new file mode 100644 index 0000000000..de15092495 --- /dev/null +++ b/src/components/organisms/add-product-modal/index.tsx @@ -0,0 +1,49 @@ +import { useAdminProducts } from "medusa-react" +import React from "react" +import Button from "../../fundamentals/button" +import Modal from "../../molecules/modal" +import CollectionProductTable from "../../templates/collection-product-table" + +type AddProductModalProps = { + handleClose: () => void + onSubmit: () => void +} + +const AddProductModal: React.FC = ({ + handleClose, + onSubmit, +}) => { + const { products, isLoading } = useAdminProducts({ + offset: 0, + limit: 10, + }) + + return ( + + + +

Add Products

+
+ + + + +
+ + +
+
+
+
+ ) +} + +export default AddProductModal diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index 1c570a1207..f813bcada6 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -3,9 +3,16 @@ import Spinner from "../../atoms/spinner" import Table from "../../molecules/table" import { FilteringOptionProps } from "../../molecules/table/filtering-option" +type CollectionProductTableItem = { + id: string + thumbnail?: string + title: string + status: string +} + type CollectionProductTableProps = { loadingProducts: boolean - products?: any[] + products?: CollectionProductTableItem[] handleSearch: (value: string) => void } @@ -17,6 +24,9 @@ const CollectionProductTable: React.FC = ({ const [filteringOptions, setFilteringOptions] = useState< FilteringOptionProps[] >([]) + const [shownProducts, setShownProducts] = useState< + CollectionProductTableItem[] + >([]) useEffect(() => { setFilteringOptions([ @@ -38,6 +48,8 @@ const CollectionProductTable: React.FC = ({ ], }, ]) + + setShownProducts(products ?? []) }, [products]) return ( @@ -54,8 +66,8 @@ const CollectionProductTable: React.FC = ({
) : (
- {products?.map((product, index) => ( -

{JSON.stringify(product, undefined, 2)}

+ {shownProducts?.map((product, index) => ( +

{product.id}

))}
)} diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index 35df35f0cc..4b1f4a0b0b 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -18,6 +18,8 @@ import CollectionModal from "../../../components/templates/collection-modal" import CollectionProductTable from "../../../components/templates/collection-product-table" import DeletePrompt from "../../../components/organisms/delete-prompt" import { navigate } from "gatsby" +import PlusIcon from "../../../components/fundamentals/icons/plus-icon" +import AddProductModal from "../../../components/organisms/add-product-modal" const CollectionDetails: React.FC = ({ location }) => { const ensuredPath = location!.pathname.replace("/a/collections/", ``) @@ -26,6 +28,7 @@ const CollectionDetails: React.FC = ({ location }) => { const updateCollection = useAdminUpdateCollection(ensuredPath) const [showEdit, setShowEdit] = useState(false) const [showDelete, setShowDelete] = useState(false) + const [showAddProducts, setShowAddProducts] = useState(false) const handleDelete = () => { deleteCollection.mutate(undefined, { @@ -116,12 +119,21 @@ const CollectionDetails: React.FC = ({ location }) => { , + onClick: () => setShowAddProducts(!showAddProducts), + }, + ]} > - +
+ +
{showEdit && ( @@ -141,6 +153,12 @@ const CollectionDetails: React.FC = ({ location }) => { confirmText="Yes, delete" /> )} + {showAddProducts && ( + setShowAddProducts(!showAddProducts)} + onSubmit={() => {}} + /> + )} ) } From 480ac319eb187b1472ba7d50c146985841838ed1 Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 13:35:43 +0100 Subject: [PATCH 10/19] commit changes --- src/components/templates/collection-product-table/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index f813bcada6..381faee53d 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -14,6 +14,7 @@ type CollectionProductTableProps = { loadingProducts: boolean products?: CollectionProductTableItem[] handleSearch: (value: string) => void + rowElement: (item: CollectionProductTableItem) => React.ReactNode } const CollectionProductTable: React.FC = ({ From bbdefc893cdfd88d50ccd836b8b4c6bf66455cfb Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 14:38:40 +0100 Subject: [PATCH 11/19] progress --- src/components/organisms/metadata/index.tsx | 4 ++-- .../templates/collection-modal/index.tsx | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/organisms/metadata/index.tsx b/src/components/organisms/metadata/index.tsx index 13b4fc9cab..641b643f9b 100644 --- a/src/components/organisms/metadata/index.tsx +++ b/src/components/organisms/metadata/index.tsx @@ -10,7 +10,7 @@ type AddMetadataProps = { existingMetadata?: object } -type KP = { +export type MetadataField = { key: string value: any } @@ -20,7 +20,7 @@ const Metadata: React.FC = ({ heading = "Metadata", existingMetadata, }) => { - const [keyPairs, setKeyPairs] = React.useState([]) + const [keyPairs, setKeyPairs] = React.useState([]) const addKeyPair = () => { const baseName = `metadata.${keyPairs.length}` diff --git a/src/components/templates/collection-modal/index.tsx b/src/components/templates/collection-modal/index.tsx index 751ac7ed7f..922ddf1031 100644 --- a/src/components/templates/collection-modal/index.tsx +++ b/src/components/templates/collection-modal/index.tsx @@ -1,14 +1,14 @@ -import React, { useEffect } from "react" +import React, { useEffect, useState } from "react" import { useForm } from "react-hook-form" import Button from "../../fundamentals/button" import InfoTooltip from "../../molecules/info-tooltip" import InputField from "../../molecules/input" import Modal from "../../molecules/modal" -import Metadata from "../../organisms/metadata" +import Metadata, { MetadataField } from "../../organisms/metadata" type CollectionModalProps = { onClose: () => void - onSubmit: (values: any) => void + onSubmit: (values: any, metadata?: MetadataField[]) => void isEdit?: boolean collection?: any } @@ -20,6 +20,7 @@ const CollectionModal: React.FC = ({ collection, }) => { const { register, setValue, handleSubmit, control } = useForm() + const [metadata, setMetadata] = useState([]) if (isEdit && !collection) { throw new Error("Collection is required for edit") @@ -37,15 +38,16 @@ const CollectionModal: React.FC = ({ if (collection.metadata) { Object.entries(collection.metadata).map(([key, value], i) => { - register(`metadata.${i}.key`, { required: true }) - register(`metadata.${i}.value`, { required: true }) - setValue(`metadata.${i}.key`, key) - setValue(`metadata.${i}.value`, value) + setMetadata([...metadata, { key, value }]) }) } } }, [collection, isEdit]) + const submit = (data: any) => { + onSubmit(data, metadata) + } + return ( @@ -59,7 +61,7 @@ const CollectionModal: React.FC = ({

-
+

Details

From d77c1b61810934b2ba216f14783970cab4cb133a Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 16:28:14 +0100 Subject: [PATCH 12/19] progress on modal --- package.json | 1 + .../fundamentals/icons/chevron-down.tsx | 6 +- .../organisms/add-product-modal/index.tsx | 31 +++-- src/components/organisms/metadata/index.tsx | 112 +++++++++--------- .../templates/collection-modal/index.tsx | 13 +- .../collection-product-table/index.tsx | 57 +++++++-- src/domain/collections/details/index.tsx | 32 ++--- src/domain/collections/index.js | 31 +++-- yarn.lock | 7 ++ 9 files changed, 180 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index 551b971426..070d7ff538 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@storybook/react": "^6.4.9", "@storybook/theming": "^6.4.9", "@tailwindcss/line-clamp": "^0.3.1", + "@types/react-table": "^7.7.9", "@typescript-eslint/parser": "^5.9.1", "autoprefixer": "^10.4.1", "babel-loader": "^8.1.0", diff --git a/src/components/fundamentals/icons/chevron-down.tsx b/src/components/fundamentals/icons/chevron-down.tsx index 980194c095..5af31d7dde 100644 --- a/src/components/fundamentals/icons/chevron-down.tsx +++ b/src/components/fundamentals/icons/chevron-down.tsx @@ -18,9 +18,9 @@ const ChevronDownIcon: React.FC = ({ ) diff --git a/src/components/organisms/add-product-modal/index.tsx b/src/components/organisms/add-product-modal/index.tsx index de15092495..da010d3790 100644 --- a/src/components/organisms/add-product-modal/index.tsx +++ b/src/components/organisms/add-product-modal/index.tsx @@ -1,5 +1,6 @@ import { useAdminProducts } from "medusa-react" -import React from "react" +import React, { useState } from "react" +import { useDebounce } from "../../../hooks/use-debounce" import Button from "../../fundamentals/button" import Modal from "../../molecules/modal" import CollectionProductTable from "../../templates/collection-product-table" @@ -13,11 +14,23 @@ const AddProductModal: React.FC = ({ handleClose, onSubmit, }) => { + const [query, setQuery] = useState("") + const limit = 10 + const [offset, setOffset] = useState(0) + + const debouncedSearchTerm = useDebounce(query, 500) + const { products, isLoading } = useAdminProducts({ - offset: 0, - limit: 10, + q: debouncedSearchTerm, + offset: offset, + limit: limit, }) + const handleSearch = (q) => { + setOffset(0) + setQuery(q) + } + return ( @@ -25,11 +38,13 @@ const AddProductModal: React.FC = ({

Add Products

- +
+ +
diff --git a/src/components/organisms/metadata/index.tsx b/src/components/organisms/metadata/index.tsx index 641b643f9b..6ddf6f60ea 100644 --- a/src/components/organisms/metadata/index.tsx +++ b/src/components/organisms/metadata/index.tsx @@ -1,13 +1,13 @@ -import React, { useEffect } from "react" +import React, { useEffect, useState } from "react" import Button from "../../fundamentals/button" import PlusIcon from "../../fundamentals/icons/plus-icon" import TrashIcon from "../../fundamentals/icons/trash-icon" import InputField from "../../molecules/input" type AddMetadataProps = { - control: any + metadata: MetadataField[] + setMetadata: (metadata: MetadataField[]) => void heading?: string - existingMetadata?: object } export type MetadataField = { @@ -16,60 +16,56 @@ export type MetadataField = { } const Metadata: React.FC = ({ - control, + metadata, + setMetadata, heading = "Metadata", - existingMetadata, }) => { - const [keyPairs, setKeyPairs] = React.useState([]) + const [localData, setLocalData] = useState([]) - const addKeyPair = () => { - const baseName = `metadata.${keyPairs.length}` + useEffect(() => { + setLocalData(metadata) + }, [metadata]) - setKeyPairs([ - ...keyPairs, - { key: `${baseName}.key`, value: `${baseName}.value` }, - ]) + const addKeyPair = () => { + setMetadata([...metadata, { key: ``, value: `` }]) } - const deleteKeyPair = (index: number) => { - control.unregister(keyPairs[index].key) - control.unregister(keyPairs[index].value) - - setKeyPairs(keyPairs.filter((_, i) => i !== index)) + const onKeyChange = (index: number) => { + return (key: string) => { + const newFields = metadata + newFields[index] = { key: key, value: newFields[index].value } + setMetadata(newFields) + } } - useEffect(() => { - if (existingMetadata) { - for (const key of Object.keys(existingMetadata)) { - control.register(`metadata.${keyPairs.length}.key`) - control.register(`metadata.${keyPairs.length}.value`) - - addKeyPair() - - control.setValue(`metadata.${keyPairs.length}.key`, key) - control.setValue( - `metadata.${keyPairs.length}.value`, - existingMetadata[key] - ) + const onValueChange = (index: number) => { + return (value: any) => { + const newFields = metadata + newFields[index] = { + key: newFields[index].key, + value: value, } + setMetadata(newFields) } - }, [existingMetadata]) + } + + const deleteKeyPair = (index: number) => { + return () => { + setMetadata(metadata.filter((_, i) => i !== index)) + } + } return (
{heading}
- {keyPairs.map((keyPair, index) => { + {localData.map((field, index) => { return ( - - + ) @@ -90,32 +86,42 @@ const Metadata: React.FC = ({ ) } -type KeyPairProps = { - k: string - v: string - register: any -} & React.HTMLAttributes +type FieldProps = { + field: MetadataField + updateKey: (key: string) => void + updateValue: (value: string) => void +} -const KeyPair: React.FC = ({ k, v, register }) => { +const Field: React.FC = ({ field, updateKey, updateValue }) => { return (
- + { + updateKey(e.currentTarget.value) + }} + />
- + { + updateValue(e.currentTarget.value) + }} + />
) } type DeletableElementProps = { - onDelete: (index: number) => void - index: number + onDelete: () => void } const DeletableElement: React.FC = ({ - index, onDelete, children, }) => { @@ -127,7 +133,7 @@ const DeletableElement: React.FC = ({ size="small" className="text-grey-40 justify-end" type="button" - onClick={() => onDelete(index)} + onClick={onDelete} > diff --git a/src/components/templates/collection-modal/index.tsx b/src/components/templates/collection-modal/index.tsx index 922ddf1031..521bc1a22f 100644 --- a/src/components/templates/collection-modal/index.tsx +++ b/src/components/templates/collection-modal/index.tsx @@ -19,7 +19,7 @@ const CollectionModal: React.FC = ({ isEdit = false, collection, }) => { - const { register, setValue, handleSubmit, control } = useForm() + const { register, setValue, handleSubmit } = useForm() const [metadata, setMetadata] = useState([]) if (isEdit && !collection) { @@ -37,8 +37,10 @@ const CollectionModal: React.FC = ({ setValue("handle", collection.handle) if (collection.metadata) { - Object.entries(collection.metadata).map(([key, value], i) => { - setMetadata([...metadata, { key, value }]) + Object.entries(collection.metadata).map(([key, value]) => { + const newMeta = metadata + newMeta.push({ key, value }) + setMetadata(newMeta) }) } } @@ -86,10 +88,7 @@ const CollectionModal: React.FC = ({
- +
diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index 381faee53d..e9cab0e7d8 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from "react" +import React, { useEffect, useMemo, useState } from "react" +import { Column, TableOptions, usePagination, useTable } from "react-table" import Spinner from "../../atoms/spinner" import Table from "../../molecules/table" import { FilteringOptionProps } from "../../molecules/table/filtering-option" @@ -53,6 +54,30 @@ const CollectionProductTable: React.FC = ({ setShownProducts(products ?? []) }, [products]) + const columns: Column[] = useMemo( + () => [ + { + accessor: "thumbnail", + Cell: ({ cell: { value } }) => ( +
{value ? : null}
+ ), + }, + { + accessor: "title", + Cell: ({ cell: { value } }) =>
{value}
, + }, + ], + [] + ) + + const { rows, prepareRow, getTableBodyProps, getTableProps } = useTable( + { + data: shownProducts, + columns: columns, + }, + usePagination + ) + return (
= ({ handleSearch={handleSearch} searchPlaceholder="Search Products" filteringOptions={filteringOptions} + {...getTableProps()} > - {loadingProducts && !products ? ( -
+ {loadingProducts || !products ? ( +
) : ( -
- {shownProducts?.map((product, index) => ( -

{product.id}

- ))} -
+ + {rows.map((row) => { + prepareRow(row) + return ( + + {row.cells.map((cell, index) => { + return ( + + {cell.render("Cell", { index })} + + ) + })} + + ) + })} + )}
diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index 4b1f4a0b0b..d809ea1d8f 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -4,14 +4,12 @@ import { useAdminUpdateCollection, } from "medusa-react" import React, { useState } from "react" -import { useForm } from "react-hook-form" import Spinner from "../../../components/atoms/spinner" import Breadcrumb from "../../../components/molecules/breadcrumb" import BodyCard from "../../../components/organisms/body-card" import { RouteComponentProps } from "@reach/router" import Actionables from "../../../components/molecules/actionables" import TrashIcon from "../../../components/fundamentals/icons/trash-icon" -import Button from "../../../components/fundamentals/button" import ViewRaw from "../../../components/molecules/view-raw" import EditIcon from "../../../components/fundamentals/icons/edit-icon" import CollectionModal from "../../../components/templates/collection-modal" @@ -20,10 +18,11 @@ import DeletePrompt from "../../../components/organisms/delete-prompt" import { navigate } from "gatsby" import PlusIcon from "../../../components/fundamentals/icons/plus-icon" import AddProductModal from "../../../components/organisms/add-product-modal" +import { MetadataField } from "../../../components/organisms/metadata" const CollectionDetails: React.FC = ({ location }) => { const ensuredPath = location!.pathname.replace("/a/collections/", ``) - const { collection, isLoading, isError } = useAdminCollection(ensuredPath) + const { collection, isLoading, refetch } = useAdminCollection(ensuredPath) const deleteCollection = useAdminDeleteCollection(ensuredPath) const updateCollection = useAdminUpdateCollection(ensuredPath) const [showEdit, setShowEdit] = useState(false) @@ -36,7 +35,7 @@ const CollectionDetails: React.FC = ({ location }) => { }) } - const handleUpdateDetails = (data: any) => { + const handleUpdateDetails = (data: any, metadata: MetadataField[]) => { const payload: { title: string handle?: string @@ -46,24 +45,27 @@ const CollectionDetails: React.FC = ({ location }) => { handle: data.handle, } - if (data.metadata) { - const metadata = data.metadata.reduce((acc, next) => { - return { - ...acc, - [next.key]: next.value, - } - }, {}) + if (metadata.length > 0) { + const payloadMetadata = metadata + .filter((m) => m.key && m.value) // remove empty metadata + .reduce((acc, next) => { + return { + ...acc, + [next.key]: next.value, + } + }, {}) - payload.metadata = metadata + payload.metadata = payloadMetadata // deleting metadata will not work as it's not supported by the core } updateCollection.mutate(payload, { - onSuccess: () => setShowEdit(false), + onSuccess: () => { + setShowEdit(false) + refetch() + }, }) } - const { register, unregister, setValue, handleSubmit, formState } = useForm() - return ( <>
diff --git a/src/domain/collections/index.js b/src/domain/collections/index.js index 3c1c8f8d6d..4b0d46effa 100644 --- a/src/domain/collections/index.js +++ b/src/domain/collections/index.js @@ -27,28 +27,27 @@ const CollectionsIndex = () => { const [showNew, setShowNew] = useState(false) const createCollection = useAdminCreateCollection() const toaster = useToaster() - const { collections, isLoading, refresh, total_count } = useMedusa( - "collections", - { - search: `?${qs.stringify(filtersOnLoad)}`, - } - ) + const { collections, isLoading, refresh } = useMedusa("collections", { + search: `?${qs.stringify(filtersOnLoad)}`, + }) - const handleAddNew = (data) => { + const handleAddNew = (data, metadata) => { const payload = { title: data.title, handle: data.handle, } - if (data.metadata) { - const metadata = data.metadata.reduce((acc, next) => { - return { - ...acc, - [next.key]: next.value, - } - }, {}) - - payload.metadata = metadata + if (metadata) { + const payloadMetadata = metadata + .filter((m) => m.key && m.value) + .reduce((acc, next) => { + return { + ...acc, + [next.key]: next.value, + } + }, {}) + + payload.metadata = payloadMetadata } createCollection.mutate(payload, { diff --git a/yarn.lock b/yarn.lock index ab1b324f23..9c06af2a11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4200,6 +4200,13 @@ dependencies: "@types/react" "*" +"@types/react-table@^7.7.9": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.9.tgz#ea82875775fc6ee71a28408dcc039396ae067c92" + integrity sha512-ejP/J20Zlj9VmuLh73YgYkW2xOSFTW39G43rPH93M4mYWdMmqv66lCCr+axZpkdtlNLGjvMG2CwzT4S6abaeGQ== + dependencies: + "@types/react" "*" + "@types/react@*": version "17.0.38" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" From c99692931b6b1258ce2befca94ffff4bba8b22f2 Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 20:16:29 +0100 Subject: [PATCH 13/19] push progress --- package.json | 1 + .../atoms/checkbox/checkbox.stories.tsx | 17 ++ src/components/atoms/checkbox/index.tsx | 35 ++++ .../indeterminate-checkbox/index.tsx | 58 ++++++ .../organisms/add-product-modal/index.tsx | 29 +-- ...a.stories.tsx => add-metadata.stories.tsx} | 0 .../templates/collection-modal/index.tsx | 2 +- .../collection-product-table/index.tsx | 180 ++++++++++++------ .../collection-product-table/types.ts | 11 ++ .../use-collection-product-columns.tsx | 66 +++++++ src/domain/collections/details/index.tsx | 35 ++-- tailwind.config.js | 7 +- yarn.lock | 11 +- 13 files changed, 356 insertions(+), 96 deletions(-) create mode 100644 src/components/atoms/checkbox/checkbox.stories.tsx create mode 100644 src/components/atoms/checkbox/index.tsx create mode 100644 src/components/molecules/indeterminate-checkbox/index.tsx rename src/components/organisms/metadata/{metadata.stories.tsx => add-metadata.stories.tsx} (100%) create mode 100644 src/components/templates/collection-product-table/types.ts create mode 100644 src/components/templates/collection-product-table/use-collection-product-columns.tsx diff --git a/package.json b/package.json index c7d7260202..7ef693f35f 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@storybook/addon-postcss": "^2.0.0", "@storybook/react": "^6.4.9", "@storybook/theming": "^6.4.9", + "@tailwindcss/forms": "^0.4.0", "@tailwindcss/line-clamp": "^0.3.1", "@types/react-table": "^7.7.9", "@typescript-eslint/parser": "^5.9.1", diff --git a/src/components/atoms/checkbox/checkbox.stories.tsx b/src/components/atoms/checkbox/checkbox.stories.tsx new file mode 100644 index 0000000000..5edf00d18b --- /dev/null +++ b/src/components/atoms/checkbox/checkbox.stories.tsx @@ -0,0 +1,17 @@ +import { ComponentMeta, ComponentStory } from "@storybook/react" +import React from "react" +import Checkbox from "." + +export default { + title: "Atoms/Checkbox", + component: Checkbox, +} as ComponentMeta + +const Template: ComponentStory = (args) => ( + +) + +export const Default = Template.bind({}) +Default.args = { + label: "Checkbox", +} diff --git a/src/components/atoms/checkbox/index.tsx b/src/components/atoms/checkbox/index.tsx new file mode 100644 index 0000000000..fb0c9bf497 --- /dev/null +++ b/src/components/atoms/checkbox/index.tsx @@ -0,0 +1,35 @@ +import clsx from "clsx" +import React, { useImperativeHandle } from "react" + +type CheckboxProps = React.InputHTMLAttributes & { + label: string +} + +const Checkbox = React.forwardRef( + ({ label, value, className, id, ...rest }: CheckboxProps, ref) => { + const checkboxRef = React.useRef(null) + + useImperativeHandle(ref, () => checkboxRef.current) + return ( + + ) + } +) + +export default Checkbox diff --git a/src/components/molecules/indeterminate-checkbox/index.tsx b/src/components/molecules/indeterminate-checkbox/index.tsx new file mode 100644 index 0000000000..51e2d62d9d --- /dev/null +++ b/src/components/molecules/indeterminate-checkbox/index.tsx @@ -0,0 +1,58 @@ +import React, { forwardRef, useEffect, useRef } from "react" +import CheckIcon from "../../fundamentals/icons/check-icon" + +interface Props { + indeterminate?: boolean + name: string +} + +const useCombinedRefs = (...refs): React.MutableRefObject => { + const targetRef = React.useRef() + + React.useEffect(() => { + refs.forEach((ref) => { + if (!ref) { + return + } + + if (typeof ref === "function") { + ref(targetRef.current) + } else { + ref.current = targetRef.current + } + }) + }, [refs]) + + return targetRef +} + +const IndeterminateCheckbox = forwardRef( + ({ indeterminate, ...rest }, ref: React.Ref) => { + const defaultRef = useRef(null) + const combinedRef = useCombinedRefs(ref, defaultRef) + + useEffect(() => { + if (combinedRef?.current) { + combinedRef.current.indeterminate = indeterminate ?? false + } + }, [combinedRef, indeterminate]) + + return ( +
+
combinedRef.current?.click()} + className={`w-5 h-5 flex justify-center text-grey-0 border-grey-30 border cursor-pointer rounded-base ${ + combinedRef.current?.checked && "bg-violet-60" + }`} + > + + {combinedRef.current?.checked && } + +
+ +
+ ) + } +) + +export default IndeterminateCheckbox diff --git a/src/components/organisms/add-product-modal/index.tsx b/src/components/organisms/add-product-modal/index.tsx index da010d3790..508c57a4e3 100644 --- a/src/components/organisms/add-product-modal/index.tsx +++ b/src/components/organisms/add-product-modal/index.tsx @@ -1,6 +1,4 @@ -import { useAdminProducts } from "medusa-react" -import React, { useState } from "react" -import { useDebounce } from "../../../hooks/use-debounce" +import React from "react" import Button from "../../fundamentals/button" import Modal from "../../molecules/modal" import CollectionProductTable from "../../templates/collection-product-table" @@ -8,29 +6,14 @@ import CollectionProductTable from "../../templates/collection-product-table" type AddProductModalProps = { handleClose: () => void onSubmit: () => void + collectionProducts: any[] } const AddProductModal: React.FC = ({ handleClose, onSubmit, + collectionProducts, }) => { - const [query, setQuery] = useState("") - const limit = 10 - const [offset, setOffset] = useState(0) - - const debouncedSearchTerm = useDebounce(query, 500) - - const { products, isLoading } = useAdminProducts({ - q: debouncedSearchTerm, - offset: offset, - limit: limit, - }) - - const handleSearch = (q) => { - setOffset(0) - setQuery(q) - } - return ( @@ -39,11 +22,7 @@ const AddProductModal: React.FC = ({
- +
diff --git a/src/components/organisms/metadata/metadata.stories.tsx b/src/components/organisms/metadata/add-metadata.stories.tsx similarity index 100% rename from src/components/organisms/metadata/metadata.stories.tsx rename to src/components/organisms/metadata/add-metadata.stories.tsx diff --git a/src/components/templates/collection-modal/index.tsx b/src/components/templates/collection-modal/index.tsx index 521bc1a22f..3c21f792b2 100644 --- a/src/components/templates/collection-modal/index.tsx +++ b/src/components/templates/collection-modal/index.tsx @@ -8,7 +8,7 @@ import Metadata, { MetadataField } from "../../organisms/metadata" type CollectionModalProps = { onClose: () => void - onSubmit: (values: any, metadata?: MetadataField[]) => void + onSubmit: (values: any, metadata: MetadataField[]) => void isEdit?: boolean collection?: any } diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index e9cab0e7d8..bf14802c28 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -1,34 +1,39 @@ -import React, { useEffect, useMemo, useState } from "react" -import { Column, TableOptions, usePagination, useTable } from "react-table" -import Spinner from "../../atoms/spinner" -import Table from "../../molecules/table" +import { useAdminProducts } from "medusa-react" +import React, { useEffect, useState } from "react" +import { Column, usePagination, useRowSelect, useTable } from "react-table" +import { useDebounce } from "../../../hooks/use-debounce" +import IndeterminateCheckbox from "../../molecules/indeterminate-checkbox" +import Table, { TablePagination } from "../../molecules/table" import { FilteringOptionProps } from "../../molecules/table/filtering-option" - -type CollectionProductTableItem = { - id: string - thumbnail?: string - title: string - status: string -} +import useCollectionProductColumns from "./use-collection-product-columns" type CollectionProductTableProps = { - loadingProducts: boolean - products?: CollectionProductTableItem[] - handleSearch: (value: string) => void - rowElement: (item: CollectionProductTableItem) => React.ReactNode + addedProducts: any[] + setProducts: (products: any) => void } const CollectionProductTable: React.FC = ({ - loadingProducts, - products, - handleSearch, + addedProducts, + setProducts, }) => { + const [query, setQuery] = useState("") + const [limit, setLimit] = useState(5) + const [offset, setOffset] = useState(0) + const [numPages, setNumPages] = useState(0) + const [currentPage, setCurrentPage] = useState(0) const [filteringOptions, setFilteringOptions] = useState< FilteringOptionProps[] >([]) - const [shownProducts, setShownProducts] = useState< - CollectionProductTableItem[] - >([]) + + const [selectedProducts, setSelectedProducts] = useState([]) + + const debouncedSearchTerm = useDebounce(query, 500) + + const { isLoading, count, products } = useAdminProducts({ + q: debouncedSearchTerm, + limit: limit, + offset, + }) useEffect(() => { setFilteringOptions([ @@ -50,63 +55,119 @@ const CollectionProductTable: React.FC = ({ ], }, ]) - - setShownProducts(products ?? []) }, [products]) - const columns: Column[] = useMemo( - () => [ - { - accessor: "thumbnail", - Cell: ({ cell: { value } }) => ( -
{value ? : null}
- ), - }, - { - accessor: "title", - Cell: ({ cell: { value } }) =>
{value}
, - }, - ], - [] - ) + const columns = useCollectionProductColumns() as readonly Column[] - const { rows, prepareRow, getTableBodyProps, getTableProps } = useTable( + const { + rows, + prepareRow, + getTableBodyProps, + getTableProps, + canPreviousPage, + canNextPage, + pageCount, + nextPage, + previousPage, + // Get the state from the instance + state: { pageIndex, pageSize, selectedRowIds }, + } = useTable( { - data: shownProducts, + data: products || [], columns: columns, + manualPagination: true, + initialState: { + pageIndex: currentPage, + pageSize: limit, + selectedRowIds: addedProducts?.reduce((prev, { id }) => { + prev[id] = true + return prev + }, {}), + }, + pageCount: numPages, + autoResetSelectedRows: false, + autoResetPage: false, + getRowId: (row) => row.id, }, - usePagination + usePagination, + useRowSelect, + (hooks) => { + hooks.visibleColumns.push((columns) => [ + { + id: "selection", + Cell: ({ row }) => { + return ( + + + + ) + }, + }, + ...columns, + ]) + } ) + useEffect(() => { + setSelectedProducts((selectedProducts) => [ + ...selectedProducts.filter( + (sv) => Object.keys(selectedRowIds).findIndex((id) => id === sv.id) > -1 + ), + ...(products?.filter( + (p) => + selectedProducts.findIndex((sv) => sv.id === p.id) < 0 && + Object.keys(selectedRowIds).findIndex((id) => id === p.id) > -1 + ) || []), + ]) + }, [selectedRowIds]) + + const handleNext = () => { + if (canNextPage) { + setOffset((old) => old + pageSize) + setCurrentPage((old) => old + 1) + nextPage() + } + } + + const handlePrev = () => { + if (canPreviousPage) { + setOffset((old) => old - pageSize) + setCurrentPage((old) => old - 1) + previousPage() + } + } + + const handleSearch = (q) => { + setOffset(0) + setQuery(q) + } + + useEffect(() => { + console.log("products", selectedProducts) + }, [selectedProducts]) + return ( -
+
- {loadingProducts || !products ? ( -
- -
- ) : ( + {isLoading || !products ? null : ( {rows.map((row) => { prepareRow(row) return ( {row.cells.map((cell, index) => { - return ( - - {cell.render("Cell", { index })} - - ) + return cell.render("Cell", { index }) })} ) @@ -114,6 +175,19 @@ const CollectionProductTable: React.FC = ({ )}
+
) } diff --git a/src/components/templates/collection-product-table/types.ts b/src/components/templates/collection-product-table/types.ts new file mode 100644 index 0000000000..929b38730e --- /dev/null +++ b/src/components/templates/collection-product-table/types.ts @@ -0,0 +1,11 @@ +export type CollectionProductTableItem = { + id: string + thumbnail?: string + title: string + status: string + created_at: Date +} + +export type AddCollectionProductTableItem = { + selected: boolean +} & CollectionProductTableItem diff --git a/src/components/templates/collection-product-table/use-collection-product-columns.tsx b/src/components/templates/collection-product-table/use-collection-product-columns.tsx new file mode 100644 index 0000000000..260774f7f1 --- /dev/null +++ b/src/components/templates/collection-product-table/use-collection-product-columns.tsx @@ -0,0 +1,66 @@ +import React, { useMemo } from "react" +import { Column } from "react-table" +import StatusIndicator from "../../fundamentals/status-indicator" +import Table from "../../molecules/table" +import { AddCollectionProductTableItem } from "./types" + +const useCollectionProductColumns = () => { + const decideStatus = (status: string) => { + switch (status) { + case "published": + return ( +
+ +
+ ) + case "draft": + return + case "proposed": + return + case "rejected": + return + default: + return null + } + } + + const columns: Column[] = useMemo( + () => [ + { + accessor: "thumbnail", + Cell: ({ cell: { value } }) => ( + +
+ {value ? ( + Thumbnail + ) : null} +
+
+ ), + }, + { + accessor: "title", + Cell: ({ cell: { value } }) => ( + {value} + ), + }, + { + accessor: "status", + Cell: ({ cell: { value } }) => ( + + {decideStatus(value)} + + ), + }, + ], + [] + ) + + return columns +} + +export default useCollectionProductColumns diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index d809ea1d8f..91b434a8f6 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -1,3 +1,5 @@ +import { RouteComponentProps } from "@reach/router" +import { navigate } from "gatsby" import { useAdminCollection, useAdminDeleteCollection, @@ -5,20 +7,17 @@ import { } from "medusa-react" import React, { useState } from "react" import Spinner from "../../../components/atoms/spinner" -import Breadcrumb from "../../../components/molecules/breadcrumb" -import BodyCard from "../../../components/organisms/body-card" -import { RouteComponentProps } from "@reach/router" -import Actionables from "../../../components/molecules/actionables" -import TrashIcon from "../../../components/fundamentals/icons/trash-icon" -import ViewRaw from "../../../components/molecules/view-raw" import EditIcon from "../../../components/fundamentals/icons/edit-icon" -import CollectionModal from "../../../components/templates/collection-modal" -import CollectionProductTable from "../../../components/templates/collection-product-table" -import DeletePrompt from "../../../components/organisms/delete-prompt" -import { navigate } from "gatsby" import PlusIcon from "../../../components/fundamentals/icons/plus-icon" +import TrashIcon from "../../../components/fundamentals/icons/trash-icon" +import Actionables from "../../../components/molecules/actionables" +import Breadcrumb from "../../../components/molecules/breadcrumb" +import ViewRaw from "../../../components/molecules/view-raw" import AddProductModal from "../../../components/organisms/add-product-modal" +import BodyCard from "../../../components/organisms/body-card" +import DeletePrompt from "../../../components/organisms/delete-prompt" import { MetadataField } from "../../../components/organisms/metadata" +import CollectionModal from "../../../components/templates/collection-modal" const CollectionDetails: React.FC = ({ location }) => { const ensuredPath = location!.pathname.replace("/a/collections/", ``) @@ -130,11 +129,18 @@ const CollectionDetails: React.FC = ({ location }) => { ]} >
- ({ + title: p.title, + id: p.id, + thumbnail: p.thumbnail, + status: p.status, + })) ?? [] + } handleSearch={console.log} - loadingProducts={true} - /> + loadingProducts={!collection} + /> */}
@@ -159,6 +165,7 @@ const CollectionDetails: React.FC = ({ location }) => { setShowAddProducts(!showAddProducts)} onSubmit={() => {}} + collectionProducts={collection?.products ?? []} /> )} diff --git a/tailwind.config.js b/tailwind.config.js index ab2f262275..50fa3c608a 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -274,5 +274,10 @@ module.exports = { }, }, }, - plugins: [require("@tailwindcss/line-clamp")], + plugins: [ + require("@tailwindcss/line-clamp"), + require("@tailwindcss/forms")({ + strategy: "class", + }), + ], } diff --git a/yarn.lock b/yarn.lock index cc6fae3147..9d2704e939 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3857,6 +3857,13 @@ dependencies: defer-to-connect "^2.0.0" +"@tailwindcss/forms@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.4.0.tgz#a46715e347a32d216a3973eb67473bd29ae3798e" + integrity sha512-DeaQBx6EgEeuZPQACvC+mKneJsD8am1uiJugjgQK1+/Vt+Ai0GpFBC2T2fqnUad71WgOxyrZPE6BG1VaI6YqfQ== + dependencies: + mini-svg-data-uri "^1.2.3" + "@tailwindcss/line-clamp@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@tailwindcss/line-clamp/-/line-clamp-0.3.1.tgz#4d8441b509b87ece84e94f28a4aa9998413ab849" @@ -12024,7 +12031,7 @@ medusa-react@^0.1.4-adminc.21: integrity sha512-Xxt5C/xvUtBZXp+Gj6RGCzqPr187JRaiDjaG+iwGNS9NYW9wW8MJNLUJ7zFidAR5xmjoPk95x8pKC9eGzu1Jeg== dependencies: "@medusajs/medusa" "^1.1.63-adminc.21+1b3f63b6" - "@medusajs/medusa-js" "1.0.11-adminc.21+1b3f63b6" + "@medusajs/medusa-js" "^1.0.11-adminc.21+1b3f63b6" lodash "^4.17.21" lodash-es "^4.17.21" react-query "^3.31.0" @@ -12243,7 +12250,7 @@ mini-css-extract-plugin@1.6.2: schema-utils "^3.0.0" webpack-sources "^1.1.0" -mini-svg-data-uri@^1.4.3: +mini-svg-data-uri@^1.2.3, mini-svg-data-uri@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz#43177b2e93766ba338931a3e2a84a3dfd3a222b8" integrity sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA== From c74f8605094377160cf465c35b838f4fdc1d7924 Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 20:21:35 +0100 Subject: [PATCH 14/19] metadata --- src/components/templates/collection-product-table/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index bf14802c28..fbc54ef4d2 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -17,7 +17,7 @@ const CollectionProductTable: React.FC = ({ setProducts, }) => { const [query, setQuery] = useState("") - const [limit, setLimit] = useState(5) + const [limit, setLimit] = useState(10) const [offset, setOffset] = useState(0) const [numPages, setNumPages] = useState(0) const [currentPage, setCurrentPage] = useState(0) From c7ad4542604668bff7f1f0cb854fcda9c96bb9ca Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 2 Feb 2022 21:20:44 +0100 Subject: [PATCH 15/19] add products finished except api request --- .../fundamentals/icons/check-icon.tsx | 8 +++---- src/components/molecules/view-raw/index.tsx | 13 ++++++----- .../organisms/add-product-modal/index.tsx | 22 ++++++++++++++++--- .../collection-product-table/index.tsx | 6 ++--- .../collection-product-table/types.ts | 6 +---- .../use-collection-product-columns.tsx | 4 ++-- src/domain/collections/details/index.tsx | 7 +++++- 7 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/components/fundamentals/icons/check-icon.tsx b/src/components/fundamentals/icons/check-icon.tsx index 31b42ec1f7..7e7ccb3009 100644 --- a/src/components/fundamentals/icons/check-icon.tsx +++ b/src/components/fundamentals/icons/check-icon.tsx @@ -4,7 +4,7 @@ import IconProps from "./types/icon-type" const CheckIcon: React.FC = ({ size = "24px", color = "currentColor", - attributes, + ...attributes }) => { return ( = ({ ) diff --git a/src/components/molecules/view-raw/index.tsx b/src/components/molecules/view-raw/index.tsx index 8f9399bf63..37b449d58c 100644 --- a/src/components/molecules/view-raw/index.tsx +++ b/src/components/molecules/view-raw/index.tsx @@ -1,10 +1,10 @@ -import React, { useState } from "react" -import ReactJson from "react-json-view" import * as Collapsible from "@radix-ui/react-collapsible" -import ChevronDownIcon from "../../fundamentals/icons/chevron-down" -import Button from "../../fundamentals/button" import clsx from "clsx" +import React, { useState } from "react" +import ReactJson from "react-json-view" import useClipboard from "../../../hooks/use-clipboard" +import Button from "../../fundamentals/button" +import ChevronDownIcon from "../../fundamentals/icons/chevron-down" import ClipboardCopyIcon from "../../fundamentals/icons/clipboard-copy-icon" type ViewRawProps = { @@ -31,7 +31,8 @@ const ViewRaw: React.FC = ({ raw, title = "Data", name }) => {

{title}{" "} - ({Object.keys(raw).length}) + ({Object.keys(raw).length}{" "} + {Object.keys(raw).length === 1 ? "item" : "items"})

diff --git a/src/components/organisms/add-product-modal/index.tsx b/src/components/organisms/add-product-modal/index.tsx index 508c57a4e3..2d78d16b1b 100644 --- a/src/components/organisms/add-product-modal/index.tsx +++ b/src/components/organisms/add-product-modal/index.tsx @@ -5,7 +5,7 @@ import CollectionProductTable from "../../templates/collection-product-table" type AddProductModalProps = { handleClose: () => void - onSubmit: () => void + onSubmit: (ids: any[]) => void collectionProducts: any[] } @@ -14,6 +14,14 @@ const AddProductModal: React.FC = ({ onSubmit, collectionProducts, }) => { + const [selectedProducts, setSelectedProducts] = React.useState([]) + + const handleSelectProduct = () => { + if (selectedProducts.length > 0) { + onSubmit(selectedProducts.map((p) => p.id)) + } + } + return ( @@ -22,7 +30,10 @@ const AddProductModal: React.FC = ({
- +
@@ -30,7 +41,12 @@ const AddProductModal: React.FC = ({ -
diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index fbc54ef4d2..781c77a517 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -1,6 +1,6 @@ import { useAdminProducts } from "medusa-react" import React, { useEffect, useState } from "react" -import { Column, usePagination, useRowSelect, useTable } from "react-table" +import { usePagination, useRowSelect, useTable } from "react-table" import { useDebounce } from "../../../hooks/use-debounce" import IndeterminateCheckbox from "../../molecules/indeterminate-checkbox" import Table, { TablePagination } from "../../molecules/table" @@ -57,7 +57,7 @@ const CollectionProductTable: React.FC = ({ ]) }, [products]) - const columns = useCollectionProductColumns() as readonly Column[] + const columns = useCollectionProductColumns() const { rows, @@ -143,7 +143,7 @@ const CollectionProductTable: React.FC = ({ } useEffect(() => { - console.log("products", selectedProducts) + setProducts(selectedProducts) }, [selectedProducts]) return ( diff --git a/src/components/templates/collection-product-table/types.ts b/src/components/templates/collection-product-table/types.ts index 929b38730e..2793ed0aee 100644 --- a/src/components/templates/collection-product-table/types.ts +++ b/src/components/templates/collection-product-table/types.ts @@ -1,11 +1,7 @@ -export type CollectionProductTableItem = { +export type SimpleProductType = { id: string thumbnail?: string title: string status: string created_at: Date } - -export type AddCollectionProductTableItem = { - selected: boolean -} & CollectionProductTableItem diff --git a/src/components/templates/collection-product-table/use-collection-product-columns.tsx b/src/components/templates/collection-product-table/use-collection-product-columns.tsx index 260774f7f1..eb208e08fd 100644 --- a/src/components/templates/collection-product-table/use-collection-product-columns.tsx +++ b/src/components/templates/collection-product-table/use-collection-product-columns.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from "react" import { Column } from "react-table" import StatusIndicator from "../../fundamentals/status-indicator" import Table from "../../molecules/table" -import { AddCollectionProductTableItem } from "./types" +import { SimpleProductType } from "./types" const useCollectionProductColumns = () => { const decideStatus = (status: string) => { @@ -24,7 +24,7 @@ const useCollectionProductColumns = () => { } } - const columns: Column[] = useMemo( + const columns: Column[] = useMemo( () => [ { accessor: "thumbnail", diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index 91b434a8f6..4e3536b8ad 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -65,6 +65,11 @@ const CollectionDetails: React.FC = ({ location }) => { }) } + const handleAddProducts = (productIds: any[]) => { + console.log("should add these products:", productIds) // TODO: API does not support this yet + setShowAddProducts(false) + } + return ( <>
@@ -164,7 +169,7 @@ const CollectionDetails: React.FC = ({ location }) => { {showAddProducts && ( setShowAddProducts(!showAddProducts)} - onSubmit={() => {}} + onSubmit={handleAddProducts} collectionProducts={collection?.products ?? []} /> )} From 18fb636f3e073ef2b7f94a42bd64281db257e494 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 3 Feb 2022 09:48:45 +0100 Subject: [PATCH 16/19] refractor --- ...ories.tsx => collection-modal.stories.tsx} | 0 yarn.lock | 63 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) rename src/components/templates/collection-modal/{add-collection-modal.stories.tsx => collection-modal.stories.tsx} (100%) diff --git a/src/components/templates/collection-modal/add-collection-modal.stories.tsx b/src/components/templates/collection-modal/collection-modal.stories.tsx similarity index 100% rename from src/components/templates/collection-modal/add-collection-modal.stories.tsx rename to src/components/templates/collection-modal/collection-modal.stories.tsx diff --git a/yarn.lock b/yarn.lock index 9d2704e939..002477db57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2073,7 +2073,7 @@ winston "^3.3.3" yargs "^15.3.1" -"@medusajs/medusa-js@1.0.11-adminc.21+1b3f63b6", "@medusajs/medusa-js@^1.0.11-adminc.21": +"@medusajs/medusa-js@^1.0.11-adminc.21": version "1.0.11-adminc.21" resolved "https://registry.yarnpkg.com/@medusajs/medusa-js/-/medusa-js-1.0.11-adminc.21.tgz#13eb30431381af86944723c36dcc8a8237fc03ca" integrity sha512-0G8aynrZ8oYnnwvt61QHDtZUFcVaCes64nicYy6CbBxyusQgUjXjIt5scEa7LJcdvxeROE/3WIyccd+aiJLpfQ== @@ -2084,6 +2084,17 @@ qs "^6.10.3" retry-axios "^2.6.0" +"@medusajs/medusa-js@^1.0.11-adminc.21+1b3f63b6": + version "1.0.11-alpha.7" + resolved "https://registry.yarnpkg.com/@medusajs/medusa-js/-/medusa-js-1.0.11-alpha.7.tgz#23a950f8927521641192e31f29b4eae55f32ef2b" + integrity sha512-3XhYRyKZU3z/gkf5RxD934S+c6YB0GCGKNXgHVPSiwtMSXRGBho5vxbP4V3VihtwTnYIYjcXBbCL952YAAIpsQ== + dependencies: + "@medusajs/medusa" "^1.1.63-alpha.7+a81227fa" + axios "^0.24.0" + form-data "^4.0.0" + qs "^6.10.3" + retry-axios "^2.6.0" + "@medusajs/medusa@^1.1.61-alpha.14": version "1.1.62" resolved "https://registry.yarnpkg.com/@medusajs/medusa/-/medusa-1.1.62.tgz#edb37da7da3e5fef77820ea6fb2b5326af958037" @@ -2184,6 +2195,56 @@ uuid "^8.3.1" winston "^3.2.1" +"@medusajs/medusa@^1.1.63-alpha.7+a81227fa": + version "1.1.63-alpha.7" + resolved "https://registry.yarnpkg.com/@medusajs/medusa/-/medusa-1.1.63-alpha.7.tgz#76cac5096ac9bc2fc5641654c7e75ba2a3a09498" + integrity sha512-PvYLUVdC6IwwHa5/+U8TZVItT5MOP0oibia2Ewdwf3x/IN6HJHx4qdUPTqdVYy03p0WQiZfgw2qewlwiZ/8PXA== + dependencies: + "@hapi/joi" "^16.1.8" + "@medusajs/medusa-cli" "^1.1.27" + "@types/lodash" "^4.14.168" + awilix "^4.2.3" + body-parser "^1.19.0" + bull "^3.12.1" + chokidar "^3.4.2" + class-transformer "^0.5.1" + class-validator "^0.13.1" + connect-redis "^5.0.0" + cookie-parser "^1.4.4" + core-js "^3.6.5" + cors "^2.8.5" + cross-spawn "^7.0.3" + dotenv "^8.2.0" + express "^4.17.1" + express-session "^1.17.1" + fs-exists-cached "^1.0.0" + glob "^7.1.6" + ioredis "^4.17.3" + ioredis-mock "^5.6.0" + iso8601-duration "^1.3.0" + joi "^17.3.0" + joi-objectid "^3.0.1" + jsonwebtoken "^8.5.1" + medusa-core-utils "^1.1.31" + medusa-test-utils "^1.1.37" + morgan "^1.9.1" + multer "^1.4.2" + passport "^0.4.0" + passport-http-bearer "^1.0.1" + passport-jwt "^4.0.0" + passport-local "^1.0.0" + pg "^8.5.1" + randomatic "^3.1.1" + redis "^3.0.2" + reflect-metadata "^0.1.13" + request-ip "^2.1.3" + resolve-cwd "^3.0.0" + scrypt-kdf "^2.0.1" + sqlite3 "^5.0.2" + ulid "^2.3.0" + uuid "^8.3.1" + winston "^3.2.1" + "@microsoft/fetch-event-source@2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d" From aaeb69b1703d8dbcf21acb402bc040ebf4fcd369 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 3 Feb 2022 10:01:33 +0100 Subject: [PATCH 17/19] pagination --- src/components/templates/collection-product-table/index.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/index.tsx index 781c77a517..8a2273690b 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/index.tsx @@ -121,6 +121,11 @@ const CollectionProductTable: React.FC = ({ ]) }, [selectedRowIds]) + useEffect(() => { + const controlledPageCount = Math.ceil(count! / limit) + setNumPages(controlledPageCount) + }, [products, count, limit]) + const handleNext = () => { if (canNextPage) { setOffset((old) => old + pageSize) From 7c0477f19eea091af4868fc86097cbd5265a6174 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 3 Feb 2022 14:05:44 +0100 Subject: [PATCH 18/19] final collection details --- src/components/molecules/table/index.tsx | 4 +- .../organisms/add-product-modal/index.tsx | 4 +- .../{index.tsx => add-product-table.tsx} | 63 ++---- .../collection-product-table/types.ts | 7 - .../use-collection-product-columns.tsx | 26 +-- .../use-sorting-options.tsx | 88 ++++++++ .../use-view-product-columns.tsx | 59 ++++++ .../collection-product-table/utils.tsx | 25 +++ .../view-products-table.tsx | 200 ++++++++++++++++++ src/components/templates/layout.tsx | 2 +- src/domain/collections/details/index.tsx | 26 ++- src/domain/collections/index.js | 24 +-- 12 files changed, 425 insertions(+), 103 deletions(-) rename src/components/templates/collection-product-table/{index.tsx => add-product-table.tsx} (77%) delete mode 100644 src/components/templates/collection-product-table/types.ts create mode 100644 src/components/templates/collection-product-table/use-sorting-options.tsx create mode 100644 src/components/templates/collection-product-table/use-view-product-columns.tsx create mode 100644 src/components/templates/collection-product-table/utils.tsx create mode 100644 src/components/templates/collection-product-table/view-products-table.tsx diff --git a/src/components/molecules/table/index.tsx b/src/components/molecules/table/index.tsx index 2aac3fe897..cc57fa2c48 100644 --- a/src/components/molecules/table/index.tsx +++ b/src/components/molecules/table/index.tsx @@ -83,12 +83,14 @@ const Table: TableType = React.forwardRef( return (
- {filteringOptions && ( + {filteringOptions ? (
{Array.isArray(filteringOptions) ? filteringOptions.map((fo) => ) : filteringOptions}
+ ) : ( + )}
{enableSearch && ( diff --git a/src/components/organisms/add-product-modal/index.tsx b/src/components/organisms/add-product-modal/index.tsx index 2d78d16b1b..622f208016 100644 --- a/src/components/organisms/add-product-modal/index.tsx +++ b/src/components/organisms/add-product-modal/index.tsx @@ -1,7 +1,7 @@ import React from "react" import Button from "../../fundamentals/button" import Modal from "../../molecules/modal" -import CollectionProductTable from "../../templates/collection-product-table" +import AddProductsTable from "../../templates/collection-product-table/add-product-table" type AddProductModalProps = { handleClose: () => void @@ -30,7 +30,7 @@ const AddProductModal: React.FC = ({
- diff --git a/src/components/templates/collection-product-table/index.tsx b/src/components/templates/collection-product-table/add-product-table.tsx similarity index 77% rename from src/components/templates/collection-product-table/index.tsx rename to src/components/templates/collection-product-table/add-product-table.tsx index 8a2273690b..f95a85f82d 100644 --- a/src/components/templates/collection-product-table/index.tsx +++ b/src/components/templates/collection-product-table/add-product-table.tsx @@ -2,28 +2,25 @@ import { useAdminProducts } from "medusa-react" import React, { useEffect, useState } from "react" import { usePagination, useRowSelect, useTable } from "react-table" import { useDebounce } from "../../../hooks/use-debounce" +import Spinner from "../../atoms/spinner" import IndeterminateCheckbox from "../../molecules/indeterminate-checkbox" import Table, { TablePagination } from "../../molecules/table" -import { FilteringOptionProps } from "../../molecules/table/filtering-option" import useCollectionProductColumns from "./use-collection-product-columns" -type CollectionProductTableProps = { +type AddProductsTableProps = { addedProducts: any[] setProducts: (products: any) => void } -const CollectionProductTable: React.FC = ({ +const AddProductsTable: React.FC = ({ addedProducts, setProducts, }) => { + const limit = 10 const [query, setQuery] = useState("") - const [limit, setLimit] = useState(10) const [offset, setOffset] = useState(0) const [numPages, setNumPages] = useState(0) const [currentPage, setCurrentPage] = useState(0) - const [filteringOptions, setFilteringOptions] = useState< - FilteringOptionProps[] - >([]) const [selectedProducts, setSelectedProducts] = useState([]) @@ -35,28 +32,6 @@ const CollectionProductTable: React.FC = ({ offset, }) - useEffect(() => { - setFilteringOptions([ - { - title: "Sort by", - options: [ - { - title: "All", - onClick: () => {}, - }, - { - title: "Newest", - onClick: () => {}, - }, - { - title: "Oldest", - onClick: () => {}, - }, - ], - }, - ]) - }, [products]) - const columns = useCollectionProductColumns() const { @@ -69,7 +44,6 @@ const CollectionProductTable: React.FC = ({ pageCount, nextPage, previousPage, - // Get the state from the instance state: { pageIndex, pageSize, selectedRowIds }, } = useTable( { @@ -79,7 +53,7 @@ const CollectionProductTable: React.FC = ({ initialState: { pageIndex: currentPage, pageSize: limit, - selectedRowIds: addedProducts?.reduce((prev, { id }) => { + selectedRowIds: addedProducts.reduce((prev, { id }) => { prev[id] = true return prev }, {}), @@ -153,15 +127,18 @@ const CollectionProductTable: React.FC = ({ return (
- - {isLoading || !products ? null : ( + {isLoading || !products ? ( +
+ +
+ ) : ( +
{rows.map((row) => { prepareRow(row) @@ -178,8 +155,8 @@ const CollectionProductTable: React.FC = ({ ) })} - )} -
+ + )} = ({ ) } -export default CollectionProductTable +export default AddProductsTable diff --git a/src/components/templates/collection-product-table/types.ts b/src/components/templates/collection-product-table/types.ts deleted file mode 100644 index 2793ed0aee..0000000000 --- a/src/components/templates/collection-product-table/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type SimpleProductType = { - id: string - thumbnail?: string - title: string - status: string - created_at: Date -} diff --git a/src/components/templates/collection-product-table/use-collection-product-columns.tsx b/src/components/templates/collection-product-table/use-collection-product-columns.tsx index eb208e08fd..a71e401161 100644 --- a/src/components/templates/collection-product-table/use-collection-product-columns.tsx +++ b/src/components/templates/collection-product-table/use-collection-product-columns.tsx @@ -1,29 +1,9 @@ import React, { useMemo } from "react" import { Column } from "react-table" -import StatusIndicator from "../../fundamentals/status-indicator" import Table from "../../molecules/table" -import { SimpleProductType } from "./types" +import { decideStatus, SimpleProductType } from "./utils" const useCollectionProductColumns = () => { - const decideStatus = (status: string) => { - switch (status) { - case "published": - return ( -
- -
- ) - case "draft": - return - case "proposed": - return - case "rejected": - return - default: - return null - } - } - const columns: Column[] = useMemo( () => [ { @@ -52,7 +32,9 @@ const useCollectionProductColumns = () => { accessor: "status", Cell: ({ cell: { value } }) => ( - {decideStatus(value)} +
+ {decideStatus(value)} +
), }, diff --git a/src/components/templates/collection-product-table/use-sorting-options.tsx b/src/components/templates/collection-product-table/use-sorting-options.tsx new file mode 100644 index 0000000000..3a1f60d1c7 --- /dev/null +++ b/src/components/templates/collection-product-table/use-sorting-options.tsx @@ -0,0 +1,88 @@ +import { useEffect, useState } from "react" +import { FilteringOptionProps } from "../../molecules/table/filtering-option" +import { SimpleProductType } from "./utils" + +// TODO: Redo this with server side sorting + +const useSortingOptions = (products: SimpleProductType[]) => { + const [options, setOptions] = useState([]) + const [sortedProducts, setSortedProducts] = useState( + products + ) + + const sortByTitle = (a: SimpleProductType, b: SimpleProductType) => { + if (a.title < b.title) { + return -1 + } + if (a.title > b.title) { + return 1 + } + return 0 + } + + const sortByNewest = (a: SimpleProductType, b: SimpleProductType) => { + if (a.created_at < b.created_at) { + return -1 + } + if (a.created_at > b.created_at) { + return 1 + } + return 0 + } + + const sortByOldest = (a: SimpleProductType, b: SimpleProductType) => { + if (a.created_at > b.created_at) { + return -1 + } + if (a.created_at < b.created_at) { + return 1 + } + return 0 + } + + useEffect(() => { + setOptions([ + { + title: "Sort by", + options: [ + { + title: "All", + onClick: () => { + setSortedProducts(products) + }, + }, + { + title: "Newest", + onClick: () => { + const sorted = products.sort(sortByNewest) + console.log(sorted) + setSortedProducts(sorted) + }, + }, + { + title: "Oldest", + onClick: () => { + const sorted = products.sort(sortByOldest) + console.log(sorted) + setSortedProducts(sorted) + }, + }, + { + title: "Title", + onClick: () => { + const sorted = products.sort(sortByTitle) + console.log(sorted) + setSortedProducts(sorted) + }, + }, + ], + }, + ]) + + setSortedProducts(products) + }, [products]) + + return [sortedProducts, options] +} + +export default useSortingOptions diff --git a/src/components/templates/collection-product-table/use-view-product-columns.tsx b/src/components/templates/collection-product-table/use-view-product-columns.tsx new file mode 100644 index 0000000000..5afa41663b --- /dev/null +++ b/src/components/templates/collection-product-table/use-view-product-columns.tsx @@ -0,0 +1,59 @@ +import { Link } from "gatsby" +import React, { useMemo } from "react" +import { Column } from "react-table" +import Table from "../../molecules/table" +import { decideStatus, SimpleProductType } from "./utils" + +const useViewProductColumns = () => { + const columns: Column[] = useMemo( + () => [ + { + id: "selection", + Cell: ({ row }) => { + return ( + +
{row.index + 1}
+
+ ) + }, + }, + { + accessor: "thumbnail", + Cell: ({ cell: { value } }) => ( + +
+ {value ? ( + Thumbnail + ) : null} +
+
+ ), + }, + { + accessor: "title", + Cell: ({ cell: { row, value } }) => ( + + {value} + + ), + }, + { + accessor: "status", + Cell: ({ cell: { value } }) => ( + + {decideStatus(value)} + + ), + }, + ], + [] + ) + + return columns +} + +export default useViewProductColumns diff --git a/src/components/templates/collection-product-table/utils.tsx b/src/components/templates/collection-product-table/utils.tsx new file mode 100644 index 0000000000..2e10aee6c4 --- /dev/null +++ b/src/components/templates/collection-product-table/utils.tsx @@ -0,0 +1,25 @@ +import React from "react" +import StatusIndicator from "../../fundamentals/status-indicator" + +export type SimpleProductType = { + id: string + thumbnail?: string + title: string + status: string + created_at: Date +} + +export const decideStatus = (status: string) => { + switch (status) { + case "published": + return + case "draft": + return + case "proposed": + return + case "rejected": + return + default: + return null + } +} diff --git a/src/components/templates/collection-product-table/view-products-table.tsx b/src/components/templates/collection-product-table/view-products-table.tsx new file mode 100644 index 0000000000..442abd404e --- /dev/null +++ b/src/components/templates/collection-product-table/view-products-table.tsx @@ -0,0 +1,200 @@ +import { useAdminProducts } from "medusa-react" +import React, { useEffect, useState } from "react" +import { usePagination, useTable } from "react-table" +import { useDebounce } from "../../../hooks/use-debounce" +import useToaster from "../../../hooks/use-toaster" +import Medusa from "../../../services/api" +import Spinner from "../../atoms/spinner" +import Button from "../../fundamentals/button" +import TrashIcon from "../../fundamentals/icons/trash-icon" +import Table, { TablePagination } from "../../molecules/table" +import DeletePrompt from "../../organisms/delete-prompt" +import useViewProductColumns from "./use-view-product-columns" + +type ViewProductsTableProps = { + collectionId: string +} + +const ViewProductsTable: React.FC = ({ + collectionId, +}) => { + const limit = 10 + const [query, setQuery] = useState("") + const [offset, setOffset] = useState(0) + const [numPages, setNumPages] = useState(0) + const [currentPage, setCurrentPage] = useState(0) + const debouncedSearchTerm = useDebounce(query, 500) + + const toaster = useToaster() + const [showDelete, setShowDelete] = useState(false) + const [idToDelete, setIdToDelete] = useState(undefined) + + const { isLoading, count, products, refetch } = useAdminProducts({ + q: debouncedSearchTerm, + collection_id: [collectionId], + limit: limit, + offset, + }) + + useEffect(() => { + refetch() // Ensure we get the latest data + }, []) + + const handleRemoveProduct = () => { + if (idToDelete) { + Medusa.products + .update(idToDelete, { + collection_id: null, + }) + .then(() => { + refetch() + }) + } + } + + const columns = useViewProductColumns() + + // const [sorted, sortingOptions] = useSortingOptions(products ?? []) TODO: Implement this with server side sorting + + const { + rows, + prepareRow, + getTableBodyProps, + getTableProps, + canPreviousPage, + canNextPage, + pageCount, + nextPage, + previousPage, + // Get the state from the instance + state: { pageIndex, pageSize }, + } = useTable( + { + data: products || [], + columns: columns, + manualPagination: true, + initialState: { + pageIndex: currentPage, + pageSize: limit, + }, + pageCount: numPages, + getRowId: (row) => row.id, + }, + usePagination, + (hooks) => { + hooks.visibleColumns.push((columns) => [ + ...columns, + { + id: "actions", + Cell: ({ row }) => { + return ( + + + + ) + }, + }, + ]) + } + ) + + useEffect(() => { + const controlledPageCount = Math.ceil(count! / limit) + setNumPages(controlledPageCount) + }, [products, count, limit]) + + const handleNext = () => { + if (canNextPage) { + setOffset((old) => old + pageSize) + setCurrentPage((old) => old + 1) + nextPage() + } + } + + const handlePrev = () => { + if (canPreviousPage) { + setOffset((old) => old - pageSize) + setCurrentPage((old) => old - 1) + previousPage() + } + } + + const handleSearch = (q) => { + setOffset(0) + setQuery(q) + } + + return ( + <> +
+ {!products?.length ? ( +
+ {isLoading ? ( + + ) : ( + "No products yet" + )} +
+ ) : ( + + + {rows.map((row) => { + prepareRow(row) + return ( + + {row.cells.map((cell, index) => { + return cell.render("Cell", { index }) + })} + + ) + })} + +
+ )} + +
+ {showDelete && ( + handleRemoveProduct()} + handleClose={() => setShowDelete(!showDelete)} + heading="Remove product from collection" + successText="Product removed from collection" + /> + )} + + ) +} + +export default ViewProductsTable diff --git a/src/components/templates/layout.tsx b/src/components/templates/layout.tsx index ef83be7465..2960ab4843 100644 --- a/src/components/templates/layout.tsx +++ b/src/components/templates/layout.tsx @@ -9,7 +9,7 @@ const Layout: React.FC = ({ children }) => {
-
+
{children}
diff --git a/src/domain/collections/details/index.tsx b/src/domain/collections/details/index.tsx index 4e3536b8ad..277dd87ad6 100644 --- a/src/domain/collections/details/index.tsx +++ b/src/domain/collections/details/index.tsx @@ -18,10 +18,12 @@ import BodyCard from "../../../components/organisms/body-card" import DeletePrompt from "../../../components/organisms/delete-prompt" import { MetadataField } from "../../../components/organisms/metadata" import CollectionModal from "../../../components/templates/collection-modal" +import ViewProductsTable from "../../../components/templates/collection-product-table/view-products-table" const CollectionDetails: React.FC = ({ location }) => { const ensuredPath = location!.pathname.replace("/a/collections/", ``) const { collection, isLoading, refetch } = useAdminCollection(ensuredPath) + const deleteCollection = useAdminDeleteCollection(ensuredPath) const updateCollection = useAdminUpdateCollection(ensuredPath) const [showEdit, setShowEdit] = useState(false) @@ -72,7 +74,7 @@ const CollectionDetails: React.FC = ({ location }) => { return ( <> -
+
= ({ location }) => { = ({ location }) => { }, ]} > -
- {/* ({ - title: p.title, - id: p.id, - thumbnail: p.thumbnail, - status: p.status, - })) ?? [] - } - handleSearch={console.log} - loadingProducts={!collection} - /> */} +
+ {isLoading || !collection ? ( +
+ +
+ ) : ( + + )}
diff --git a/src/domain/collections/index.js b/src/domain/collections/index.js index 4b0d46effa..bef7e581e7 100644 --- a/src/domain/collections/index.js +++ b/src/domain/collections/index.js @@ -1,27 +1,25 @@ -import React, { useState } from "react" -import { navigate } from "gatsby" import { Router } from "@reach/router" -import { Text, Flex, Box } from "rebass" +import { navigate } from "gatsby" +import { useAdminCreateCollection } from "medusa-react" import qs from "query-string" -import CollectionDetails from "./details" - -import useMedusa from "../../hooks/use-medusa" - +import React, { useState } from "react" +import { Box, Flex, Text } from "rebass" +import Button from "../../components/button" +import Spinner from "../../components/spinner" import { Table, TableBody, + TableDataCell, TableHead, TableHeaderCell, - TableRow, - TableDataCell, TableHeaderRow, + TableRow, } from "../../components/table" -import Button from "../../components/button" -import Spinner from "../../components/spinner" import CollectionModal from "../../components/templates/collection-modal" -import { useAdminCreateCollection } from "medusa-react" +import useMedusa from "../../hooks/use-medusa" import useToaster from "../../hooks/use-toaster" import { getErrorMessage } from "../../utils/error-messages" +import CollectionDetails from "./details" const CollectionsIndex = () => { const [showNew, setShowNew] = useState(false) @@ -180,7 +178,7 @@ const CollectionsIndex = () => { const Collections = () => { return ( - + From 01b8649b5d148e998d91effd6fe8cb001388be46 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 3 Feb 2022 14:06:33 +0100 Subject: [PATCH 19/19] clean up --- .../templates/collection-product-table/view-products-table.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/templates/collection-product-table/view-products-table.tsx b/src/components/templates/collection-product-table/view-products-table.tsx index 442abd404e..045ac8f690 100644 --- a/src/components/templates/collection-product-table/view-products-table.tsx +++ b/src/components/templates/collection-product-table/view-products-table.tsx @@ -2,7 +2,6 @@ import { useAdminProducts } from "medusa-react" import React, { useEffect, useState } from "react" import { usePagination, useTable } from "react-table" import { useDebounce } from "../../../hooks/use-debounce" -import useToaster from "../../../hooks/use-toaster" import Medusa from "../../../services/api" import Spinner from "../../atoms/spinner" import Button from "../../fundamentals/button" @@ -25,7 +24,6 @@ const ViewProductsTable: React.FC = ({ const [currentPage, setCurrentPage] = useState(0) const debouncedSearchTerm = useDebounce(query, 500) - const toaster = useToaster() const [showDelete, setShowDelete] = useState(false) const [idToDelete, setIdToDelete] = useState(undefined)