feat(admin-ui): Multi-language support (#4962)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Product } from "@medusajs/medusa"
|
||||
import { useAdminProducts } from "medusa-react"
|
||||
import * as React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import Button from "../../fundamentals/button"
|
||||
import Modal from "../../molecules/modal"
|
||||
import { SelectableTable } from "../selectable-table"
|
||||
@@ -24,6 +25,7 @@ const AddProductsModal = ({
|
||||
initialSelection,
|
||||
onSave,
|
||||
}: AddProductsModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
/* ************* Data ************ */
|
||||
|
||||
const params = useQueryFilters(defaultQueryProps)
|
||||
@@ -62,7 +64,9 @@ const AddProductsModal = ({
|
||||
<Modal open handleClose={close}>
|
||||
<Modal.Body>
|
||||
<Modal.Header handleClose={close}>
|
||||
<h2 className="inter-xlarge-semibold">Add Products</h2>
|
||||
<h2 className="inter-xlarge-semibold">
|
||||
{t("add-products-modal-add-products", "Add Products")}
|
||||
</h2>
|
||||
</Modal.Header>
|
||||
<Modal.Content>
|
||||
<div className="flex h-full min-h-[300px] w-full flex-col justify-between ">
|
||||
@@ -78,7 +82,10 @@ const AddProductsModal = ({
|
||||
totalCount={count}
|
||||
options={{
|
||||
enableSearch: true,
|
||||
searchPlaceholder: "Search by name or description...",
|
||||
searchPlaceholder: t(
|
||||
"add-products-modal-search-by-name-or-description",
|
||||
"Search by name or description..."
|
||||
),
|
||||
}}
|
||||
{...params}
|
||||
/>
|
||||
@@ -91,14 +98,14 @@ const AddProductsModal = ({
|
||||
className="rounded-rounded h-8 w-[128px]"
|
||||
onClick={close}
|
||||
>
|
||||
Cancel
|
||||
{t("add-products-modal-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="rounded-rounded h-8 w-[128px]"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Save
|
||||
{t("add-products-modal-save", "Save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
||||
+22
-3
@@ -1,13 +1,20 @@
|
||||
import { Product } from "@medusajs/medusa"
|
||||
import clsx from "clsx"
|
||||
import { Column, HeaderGroup, Row } from "react-table"
|
||||
import { Translation } from "react-i18next"
|
||||
import ImagePlaceholder from "../../fundamentals/image-placeholder"
|
||||
import Table from "../../molecules/table"
|
||||
import { decideStatus } from "../collection-product-table/utils"
|
||||
|
||||
export const columns: Column<Product>[] = [
|
||||
{
|
||||
Header: <div className="pl-4">Product Details</div>,
|
||||
Header: (
|
||||
<div className="pl-4">
|
||||
<Translation>
|
||||
{(t) => t("add-products-modal-product-details", "Product Details")}
|
||||
</Translation>
|
||||
</div>
|
||||
),
|
||||
accessor: "title",
|
||||
Cell: ({ row: { original } }) => (
|
||||
<div className="flex w-[400px] items-center pl-4">
|
||||
@@ -33,7 +40,13 @@ export const columns: Column<Product>[] = [
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: <div>Status</div>,
|
||||
Header: (
|
||||
<div>
|
||||
<Translation>
|
||||
{(t) => t("add-products-modal-status", "Status")}
|
||||
</Translation>
|
||||
</div>
|
||||
),
|
||||
accessor: "status",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<Table.Cell className="pr-base w-[10%]">
|
||||
@@ -42,7 +55,13 @@ export const columns: Column<Product>[] = [
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: <div className="flex items-center justify-end pr-4">Variants</div>,
|
||||
Header: (
|
||||
<div className="flex items-center justify-end pr-4">
|
||||
<Translation>
|
||||
{(t) => t("add-products-modal-variants", "Variants")}
|
||||
</Translation>
|
||||
</div>
|
||||
),
|
||||
accessor: "variants",
|
||||
Cell: ({ row: { original } }) => (
|
||||
<Table.Cell className="flex items-center justify-end pr-4">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Controller } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Option } from "../../types/shared"
|
||||
import FormValidator from "../../utils/form-validator"
|
||||
import { nestedForm, NestedForm } from "../../utils/nested-form"
|
||||
@@ -47,11 +48,14 @@ const AddressForm = ({
|
||||
control,
|
||||
formState: { errors },
|
||||
} = form
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div>
|
||||
{(type === AddressType.SHIPPING || type === AddressType.BILLING) && (
|
||||
<>
|
||||
<span className="inter-base-semibold">General</span>
|
||||
<span className="inter-base-semibold">
|
||||
{t("templates-general", "General")}
|
||||
</span>
|
||||
<div className="gap-large mt-4 mb-8 grid grid-cols-2">
|
||||
<Input
|
||||
{...register(path("first_name"), {
|
||||
@@ -60,8 +64,8 @@ const AddressForm = ({
|
||||
: false,
|
||||
pattern: FormValidator.whiteSpaceRule("First name"),
|
||||
})}
|
||||
placeholder="First Name"
|
||||
label="First Name"
|
||||
placeholder={t("templates-first-name", "First Name")}
|
||||
label={t("templates-first-name", "First Name")}
|
||||
required={required}
|
||||
errors={errors}
|
||||
/>
|
||||
@@ -72,8 +76,8 @@ const AddressForm = ({
|
||||
: false,
|
||||
pattern: FormValidator.whiteSpaceRule("Last name"),
|
||||
})}
|
||||
placeholder="Last Name"
|
||||
label="Last Name"
|
||||
placeholder={t("templates-last-name", "Last Name")}
|
||||
label={t("templates-last-name", "Last Name")}
|
||||
required={required}
|
||||
errors={errors}
|
||||
/>
|
||||
@@ -81,14 +85,14 @@ const AddressForm = ({
|
||||
{...form.register(path("company"), {
|
||||
pattern: FormValidator.whiteSpaceRule("Company"),
|
||||
})}
|
||||
placeholder="Company"
|
||||
label="Company"
|
||||
placeholder={t("templates-company", "Company")}
|
||||
label={t("templates-company", "Company")}
|
||||
errors={errors}
|
||||
/>
|
||||
<Input
|
||||
{...form.register(path("phone"))}
|
||||
placeholder="Phone"
|
||||
label="Phone"
|
||||
placeholder={t("templates-phone", "Phone")}
|
||||
label={t("templates-phone", "Phone")}
|
||||
errors={errors}
|
||||
/>
|
||||
</div>
|
||||
@@ -98,10 +102,10 @@ const AddressForm = ({
|
||||
<span className="inter-base-semibold">
|
||||
{`${
|
||||
type === AddressType.BILLING
|
||||
? "Billing Address"
|
||||
? t("templates-billing-address", "Billing Address")
|
||||
: type === AddressType.SHIPPING
|
||||
? "Shipping Address"
|
||||
: "Address"
|
||||
? t("templates-shipping-address", "Shipping Address")
|
||||
: t("templates-address", "Address")
|
||||
}`}
|
||||
</span>
|
||||
)}
|
||||
@@ -111,8 +115,8 @@ const AddressForm = ({
|
||||
required: required ? FormValidator.required("Address 1") : false,
|
||||
pattern: FormValidator.whiteSpaceRule("Address 1"),
|
||||
})}
|
||||
placeholder="Address 1"
|
||||
label="Address 1"
|
||||
placeholder={t("templates-address-1", "Address 1")}
|
||||
label={t("templates-address-1", "Address 1")}
|
||||
required={required}
|
||||
errors={errors}
|
||||
/>
|
||||
@@ -120,8 +124,8 @@ const AddressForm = ({
|
||||
{...form.register(path("address_2"), {
|
||||
pattern: FormValidator.whiteSpaceRule("Address 2"),
|
||||
})}
|
||||
placeholder="Address 2"
|
||||
label="Address 2"
|
||||
placeholder={t("templates-address-2", "Address 2")}
|
||||
label={t("templates-address-2", "Address 2")}
|
||||
errors={errors}
|
||||
/>
|
||||
<Input
|
||||
@@ -129,15 +133,15 @@ const AddressForm = ({
|
||||
required: required ? FormValidator.required("Postal code") : false,
|
||||
pattern: FormValidator.whiteSpaceRule("Postal code"),
|
||||
})}
|
||||
placeholder="Postal code"
|
||||
label="Postal code"
|
||||
placeholder={t("templates-postal-code", "Postal code")}
|
||||
label={t("templates-postal-code", "Postal code")}
|
||||
required={required}
|
||||
autoComplete="off"
|
||||
errors={errors}
|
||||
/>
|
||||
<Input
|
||||
placeholder="City"
|
||||
label="City"
|
||||
placeholder={t("templates-city", "City")}
|
||||
label={t("templates-city", "City")}
|
||||
{...form.register(path("city"), {
|
||||
required: required ? FormValidator.required("City") : false,
|
||||
pattern: FormValidator.whiteSpaceRule("City"),
|
||||
@@ -149,8 +153,8 @@ const AddressForm = ({
|
||||
{...form.register(path("province"), {
|
||||
pattern: FormValidator.whiteSpaceRule("Province"),
|
||||
})}
|
||||
placeholder="Province"
|
||||
label="Province"
|
||||
placeholder={t("templates-province", "Province")}
|
||||
label={t("templates-province", "Province")}
|
||||
errors={errors}
|
||||
/>
|
||||
<Controller
|
||||
@@ -162,7 +166,7 @@ const AddressForm = ({
|
||||
render={({ field: { value, onChange } }) => {
|
||||
return (
|
||||
<NextSelect
|
||||
label="Country"
|
||||
label={t("templates-country", "Country")}
|
||||
required={required}
|
||||
value={value}
|
||||
options={countryOptions}
|
||||
@@ -176,7 +180,9 @@ const AddressForm = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-xlarge gap-y-base flex flex-col">
|
||||
<span className="inter-base-semibold">Metadata</span>
|
||||
<span className="inter-base-semibold">
|
||||
{t("templates-metadata", "Metadata")}
|
||||
</span>
|
||||
<MetadataForm form={nestedForm(form, "metadata")} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "medusa-react"
|
||||
import React, { useEffect } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
import { getErrorMessage } from "../../../utils/error-messages"
|
||||
import { nestedForm } from "../../../utils/nested-form"
|
||||
@@ -36,6 +37,7 @@ const CollectionModal: React.FC<CollectionModalProps> = ({
|
||||
isEdit = false,
|
||||
collection,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { mutate: update, isLoading: updating } = useAdminUpdateCollection(
|
||||
collection?.id!
|
||||
)
|
||||
@@ -93,14 +95,21 @@ const CollectionModal: React.FC<CollectionModalProps> = ({
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
"Successfully updated collection",
|
||||
t("collection-modal-success", "Success"),
|
||||
t(
|
||||
"collection-modal-successfully-updated-collection",
|
||||
"Successfully updated collection"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
onClose()
|
||||
},
|
||||
onError: (error) => {
|
||||
notification("Error", getErrorMessage(error), "error")
|
||||
notification(
|
||||
t("collection-modal-error", "Error"),
|
||||
getErrorMessage(error),
|
||||
"error"
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -114,14 +123,21 @@ const CollectionModal: React.FC<CollectionModalProps> = ({
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
"Successfully created collection",
|
||||
t("collection-modal-success", "Success"),
|
||||
t(
|
||||
"collection-modal-successfully-created-collection",
|
||||
"Successfully created collection"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
onClose()
|
||||
},
|
||||
onError: (error) => {
|
||||
notification("Error", getErrorMessage(error), "error")
|
||||
notification(
|
||||
t("collection-modal-error", "Error"),
|
||||
getErrorMessage(error),
|
||||
"error"
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -134,37 +150,57 @@ const CollectionModal: React.FC<CollectionModalProps> = ({
|
||||
<Modal.Header handleClose={onClose}>
|
||||
<div>
|
||||
<h1 className="inter-xlarge-semibold mb-2xsmall">
|
||||
{isEdit ? "Edit Collection" : "Add Collection"}
|
||||
{isEdit
|
||||
? t("collection-modal-edit-collection", "Edit Collection")
|
||||
: t("collection-modal-add-collection", "Add Collection")}
|
||||
</h1>
|
||||
<p className="inter-small-regular text-grey-50">
|
||||
To create a collection, all you need is a title and a handle.
|
||||
{t(
|
||||
"collection-modal-description",
|
||||
"To create a collection, all you need is a title and a handle."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<form onSubmit={handleSubmit(submit)}>
|
||||
<Modal.Content>
|
||||
<div>
|
||||
<h2 className="inter-base-semibold mb-base">Details</h2>
|
||||
<h2 className="inter-base-semibold mb-base">
|
||||
{t("collection-modal-details", "Details")}
|
||||
</h2>
|
||||
<div className="gap-x-base flex items-center">
|
||||
<InputField
|
||||
label="Title"
|
||||
label={t("collection-modal-title-label", "Title")}
|
||||
required
|
||||
placeholder="Sunglasses"
|
||||
placeholder={t(
|
||||
"collection-modal-title-placeholder",
|
||||
"Sunglasses"
|
||||
)}
|
||||
{...register("title", { required: true })}
|
||||
/>
|
||||
<InputField
|
||||
label="Handle"
|
||||
placeholder="sunglasses"
|
||||
label={t("collection-modal-handle-label", "Handle")}
|
||||
placeholder={t(
|
||||
"collection-modal-handle-placeholder",
|
||||
"sunglasses"
|
||||
)}
|
||||
{...register("handle")}
|
||||
prefix="/"
|
||||
tooltip={
|
||||
<IconTooltip content="URL Slug for the collection. Will be auto generated if left blank." />
|
||||
<IconTooltip
|
||||
content={t(
|
||||
"collection-modal-slug-description",
|
||||
"URL Slug for the collection. Will be auto generated if left blank."
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-xlarge">
|
||||
<h2 className="inter-base-semibold mb-base">Metadata</h2>
|
||||
<h2 className="inter-base-semibold mb-base">
|
||||
{t("collection-modal-metadata", "Metadata")}
|
||||
</h2>
|
||||
<MetadataForm form={nestedForm(form, "metadata")} />
|
||||
</div>
|
||||
</Modal.Content>
|
||||
@@ -176,14 +212,19 @@ const CollectionModal: React.FC<CollectionModalProps> = ({
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
{t("collection-modal-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="small"
|
||||
loading={isEdit ? updating : creating}
|
||||
>
|
||||
{`${isEdit ? "Save" : "Publish"} collection`}
|
||||
{isEdit
|
||||
? t("collection-modal-save-collection", "Save collection")
|
||||
: t(
|
||||
"collection-modal-publish-collection",
|
||||
"Publish collection"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
||||
+12
-5
@@ -1,6 +1,7 @@
|
||||
import { useAdminProducts } from "medusa-react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { usePagination, useRowSelect, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useDebounce } from "../../../hooks/use-debounce"
|
||||
import Button from "../../fundamentals/button"
|
||||
import IndeterminateCheckbox from "../../molecules/indeterminate-checkbox"
|
||||
@@ -21,6 +22,7 @@ const AddProductsTable: React.FC<AddProductsTableProps> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
const PAGE_SIZE = 10
|
||||
const { t } = useTranslation()
|
||||
const [query, setQuery] = useState("")
|
||||
const [offset, setOffset] = useState(0)
|
||||
const [numPages, setNumPages] = useState(0)
|
||||
@@ -157,7 +159,9 @@ const AddProductsTable: React.FC<AddProductsTableProps> = ({
|
||||
<Modal handleClose={onClose}>
|
||||
<Modal.Body>
|
||||
<Modal.Header handleClose={onClose}>
|
||||
<h3 className="inter-xlarge-semibold">Add Products</h3>
|
||||
<h3 className="inter-xlarge-semibold">
|
||||
{t("collection-product-table-add-products", "Add Products")}
|
||||
</h3>
|
||||
</Modal.Header>
|
||||
<Modal.Content>
|
||||
<TableContainer
|
||||
@@ -168,7 +172,7 @@ const AddProductsTable: React.FC<AddProductsTableProps> = ({
|
||||
count: count!,
|
||||
offset: offset,
|
||||
pageSize: offset + rows.length,
|
||||
title: "Products",
|
||||
title: t("collection-product-table-products", "Products"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -181,7 +185,10 @@ const AddProductsTable: React.FC<AddProductsTableProps> = ({
|
||||
enableSearch
|
||||
handleSearch={handleSearch}
|
||||
searchValue={query}
|
||||
searchPlaceholder="Search Products"
|
||||
searchPlaceholder={t(
|
||||
"collection-product-table-search-products",
|
||||
"Search Products"
|
||||
)}
|
||||
{...getTableProps()}
|
||||
className="flex-grow"
|
||||
>
|
||||
@@ -212,7 +219,7 @@ const AddProductsTable: React.FC<AddProductsTableProps> = ({
|
||||
className="w-eventButton"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
{t("collection-product-table-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -221,7 +228,7 @@ const AddProductsTable: React.FC<AddProductsTableProps> = ({
|
||||
onClick={handleSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
Save
|
||||
{t("collection-product-table-save", "Save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useAdminProducts } from "medusa-react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { Column, usePagination, useRowSelect, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useDebounce } from "../../../hooks/use-debounce"
|
||||
import IndeterminateCheckbox from "../../molecules/indeterminate-checkbox"
|
||||
import Table from "../../molecules/table"
|
||||
@@ -17,6 +18,7 @@ const CollectionProductTable: React.FC<CollectionProductTableProps> = ({
|
||||
addedProducts,
|
||||
setProducts,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [query, setQuery] = useState("")
|
||||
const [limit, setLimit] = useState(10)
|
||||
const [offset, setOffset] = useState(0)
|
||||
@@ -39,18 +41,18 @@ const CollectionProductTable: React.FC<CollectionProductTableProps> = ({
|
||||
useEffect(() => {
|
||||
setFilteringOptions([
|
||||
{
|
||||
title: "Sort by",
|
||||
title: t("collection-product-table-sort-by", "Sort by"),
|
||||
options: [
|
||||
{
|
||||
title: "All",
|
||||
title: t("collection-product-table-all", "All"),
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
title: "Newest",
|
||||
title: t("collection-product-table-newest", "Newest"),
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
title: "Oldest",
|
||||
title: t("collection-product-table-oldest", "Oldest"),
|
||||
onClick: () => {},
|
||||
},
|
||||
],
|
||||
@@ -152,7 +154,7 @@ const CollectionProductTable: React.FC<CollectionProductTableProps> = ({
|
||||
count: count!,
|
||||
offset: offset,
|
||||
pageSize: offset + rows.length,
|
||||
title: "Products",
|
||||
title: t("collection-product-table-products", "Products"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -164,7 +166,10 @@ const CollectionProductTable: React.FC<CollectionProductTableProps> = ({
|
||||
<Table
|
||||
enableSearch
|
||||
handleSearch={handleSearch}
|
||||
searchPlaceholder="Search Products"
|
||||
searchPlaceholder={t(
|
||||
"collection-product-table-search-products",
|
||||
"Search Products"
|
||||
)}
|
||||
filteringOptions={filteringOptions}
|
||||
{...getTableProps()}
|
||||
className="h-full"
|
||||
|
||||
+7
-5
@@ -1,10 +1,12 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { FilteringOptionProps } from "../../molecules/table/filtering-option"
|
||||
import { SimpleProductType } from "./utils"
|
||||
|
||||
// TODO: Redo this with server side sorting
|
||||
|
||||
const useSortingOptions = (products: SimpleProductType[]) => {
|
||||
const { t } = useTranslation()
|
||||
const [options, setOptions] = useState<FilteringOptionProps[]>([])
|
||||
const [sortedProducts, setSortedProducts] =
|
||||
useState<SimpleProductType[]>(products)
|
||||
@@ -42,16 +44,16 @@ const useSortingOptions = (products: SimpleProductType[]) => {
|
||||
useEffect(() => {
|
||||
setOptions([
|
||||
{
|
||||
title: "Sort by",
|
||||
title: t("collection-product-table-sort-by", "Sort by"),
|
||||
options: [
|
||||
{
|
||||
title: "All",
|
||||
title: t("collection-product-table-all", "All"),
|
||||
onClick: () => {
|
||||
setSortedProducts(products)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Newest",
|
||||
title: t("collection-product-table-newest", "Newest"),
|
||||
onClick: () => {
|
||||
const sorted = products.sort(sortByNewest)
|
||||
console.log(sorted)
|
||||
@@ -59,7 +61,7 @@ const useSortingOptions = (products: SimpleProductType[]) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Oldest",
|
||||
title: t("collection-product-table-oldest", "Oldest"),
|
||||
onClick: () => {
|
||||
const sorted = products.sort(sortByOldest)
|
||||
console.log(sorted)
|
||||
@@ -67,7 +69,7 @@ const useSortingOptions = (products: SimpleProductType[]) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Title",
|
||||
title: t("collection-product-table-title", "Title"),
|
||||
onClick: () => {
|
||||
const sorted = products.sort(sortByTitle)
|
||||
console.log(sorted)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Translation } from "react-i18next"
|
||||
import StatusIndicator from "../../fundamentals/status-indicator"
|
||||
|
||||
export type SimpleProductType = {
|
||||
@@ -11,13 +12,52 @@ export type SimpleProductType = {
|
||||
export const decideStatus = (status: string) => {
|
||||
switch (status) {
|
||||
case "published":
|
||||
return <StatusIndicator title="Published" variant="success" />
|
||||
return (
|
||||
<Translation>
|
||||
{(t) => (
|
||||
<StatusIndicator
|
||||
title={t(
|
||||
"collection-product-table-decide-status-published",
|
||||
"Published"
|
||||
)}
|
||||
variant="success"
|
||||
/>
|
||||
)}
|
||||
</Translation>
|
||||
)
|
||||
case "draft":
|
||||
return <StatusIndicator title="Draft" variant="default" />
|
||||
return (
|
||||
<Translation>
|
||||
{(t) => (
|
||||
<StatusIndicator
|
||||
title={t("collection-product-table-draft", "Draft")}
|
||||
variant="default"
|
||||
/>
|
||||
)}
|
||||
</Translation>
|
||||
)
|
||||
case "proposed":
|
||||
return <StatusIndicator title="Proposed" variant="warning" />
|
||||
return (
|
||||
<Translation>
|
||||
{(t) => (
|
||||
<StatusIndicator
|
||||
title={t("collection-product-table-proposed", "Proposed")}
|
||||
variant="warning"
|
||||
/>
|
||||
)}
|
||||
</Translation>
|
||||
)
|
||||
case "rejected":
|
||||
return <StatusIndicator title="Rejected" variant="danger" />
|
||||
return (
|
||||
<Translation>
|
||||
{(t) => (
|
||||
<StatusIndicator
|
||||
title={t("collection-product-table-rejected", "Rejected")}
|
||||
variant="danger"
|
||||
/>
|
||||
)}
|
||||
</Translation>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
+14
-3
@@ -1,6 +1,7 @@
|
||||
import { useAdminProducts } from "medusa-react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { usePagination, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useDebounce } from "../../../hooks/use-debounce"
|
||||
import Medusa from "../../../services/api"
|
||||
import Button from "../../fundamentals/button"
|
||||
@@ -25,6 +26,7 @@ const ViewProductsTable: React.FC<ViewProductsTableProps> = ({
|
||||
const [numPages, setNumPages] = useState(0)
|
||||
const [currentPage, setCurrentPage] = useState(0)
|
||||
const debouncedSearchTerm = useDebounce(query, 500)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [showDelete, setShowDelete] = useState(false)
|
||||
const [idToDelete, setIdToDelete] = useState<string | undefined>(undefined)
|
||||
@@ -157,7 +159,10 @@ const ViewProductsTable: React.FC<ViewProductsTableProps> = ({
|
||||
<Table
|
||||
enableSearch
|
||||
handleSearch={handleSearch}
|
||||
searchPlaceholder="Search Products"
|
||||
searchPlaceholder={t(
|
||||
"collection-product-table-search-products",
|
||||
"Search Products"
|
||||
)}
|
||||
{...getTableProps()}
|
||||
className="h-full"
|
||||
>
|
||||
@@ -183,8 +188,14 @@ const ViewProductsTable: React.FC<ViewProductsTableProps> = ({
|
||||
<DeletePrompt
|
||||
onDelete={async () => handleRemoveProduct()}
|
||||
handleClose={() => setShowDelete(!showDelete)}
|
||||
heading="Remove product from collection"
|
||||
successText="Product removed from collection"
|
||||
heading={t(
|
||||
"collection-product-table-remove-product-from-collection",
|
||||
"Remove product from collection"
|
||||
)}
|
||||
successText={t(
|
||||
"collection-product-table-product-removed-from-collection",
|
||||
"Product removed from collection"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
+9
-4
@@ -1,19 +1,24 @@
|
||||
import { useAdminDeleteCollection } from "medusa-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useImperativeDialog from "../../../hooks/use-imperative-dialog"
|
||||
import EditIcon from "../../fundamentals/icons/edit-icon"
|
||||
import TrashIcon from "../../fundamentals/icons/trash-icon"
|
||||
import { ActionType } from "../../molecules/actionables"
|
||||
|
||||
const useCollectionActions = (collection) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const dialog = useImperativeDialog()
|
||||
const deleteCollection = useAdminDeleteCollection(collection?.id)
|
||||
|
||||
const handleDelete = async () => {
|
||||
const shouldDelete = await dialog({
|
||||
heading: "Delete Collection",
|
||||
text: "Are you sure you want to delete this collection?",
|
||||
heading: t("collections-table-delete-collection", "Delete Collection"),
|
||||
text: t(
|
||||
"collections-table-confirm-delete",
|
||||
"Are you sure you want to delete this collection?"
|
||||
),
|
||||
})
|
||||
|
||||
if (shouldDelete) {
|
||||
@@ -23,12 +28,12 @@ const useCollectionActions = (collection) => {
|
||||
|
||||
const getActions = (coll): ActionType[] => [
|
||||
{
|
||||
label: "Edit",
|
||||
label: t("collections-table-edit", "Edit"),
|
||||
onClick: () => navigate(`/a/collections/${coll.id}`),
|
||||
icon: <EditIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: t("collections-table-delete", "Delete"),
|
||||
variant: "danger",
|
||||
onClick: handleDelete,
|
||||
icon: <TrashIcon size={20} />,
|
||||
|
||||
+7
-5
@@ -1,24 +1,26 @@
|
||||
import moment from "moment"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import Tooltip from "../../atoms/tooltip"
|
||||
|
||||
const useCollectionTableColumn = () => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: "Title",
|
||||
Header: t("collections-table-title", "Title"),
|
||||
accessor: "title",
|
||||
Cell: ({ row: { original } }) => {
|
||||
return <div className="flex items-center">{original.title}</div>
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Handle",
|
||||
Header: t("collections-table-handle", "Handle"),
|
||||
accessor: "handle",
|
||||
Cell: ({ cell: { value } }) => <div>/{value}</div>,
|
||||
},
|
||||
{
|
||||
Header: "Created At",
|
||||
Header: t("collections-table-created-at", "Created At"),
|
||||
accessor: "created_at",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<Tooltip content={moment(value).format("DD MMM YYYY hh:mm A")}>
|
||||
@@ -27,7 +29,7 @@ const useCollectionTableColumn = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Updated At",
|
||||
Header: t("collections-table-updated-at", "Updated At"),
|
||||
accessor: "updated_at",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<Tooltip content={moment(value).format("DD MMM YYYY hh:mm A")}>
|
||||
@@ -36,7 +38,7 @@ const useCollectionTableColumn = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Products",
|
||||
Header: t("collections-table-products", "Products"),
|
||||
accessor: "products",
|
||||
Cell: ({ cell: { value } }) => {
|
||||
return <div>{value?.length || "-"}</div>
|
||||
|
||||
+19
-5
@@ -13,6 +13,7 @@ import {
|
||||
useSortBy,
|
||||
useTable,
|
||||
} from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useQueryFilters from "../../../hooks/use-query-filters"
|
||||
import useSetSearchParams from "../../../hooks/use-set-search-params"
|
||||
import DetailsIcon from "../../fundamentals/details-icon"
|
||||
@@ -95,22 +96,34 @@ function CustomerGroupsTableRow(props: CustomerGroupsTableRowProps) {
|
||||
const navigate = useNavigate()
|
||||
const notification = useNotification()
|
||||
const { mutate } = useAdminDeleteCustomerGroup(row.original.id)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const actions: ActionType[] = [
|
||||
{
|
||||
label: "Details",
|
||||
label: t("customer-group-table-details", "Details"),
|
||||
onClick: () => navigate(row.original.id),
|
||||
icon: <DetailsIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: t("customer-group-table-delete", "Delete"),
|
||||
onClick: () => {
|
||||
mutate(undefined, {
|
||||
onSuccess: () => {
|
||||
notification("Success", "Group deleted", "success")
|
||||
notification(
|
||||
t("customer-group-table-success", "Success"),
|
||||
t("customer-group-table-group-deleted", "Group deleted"),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
onError: () => {
|
||||
notification("Error", "Failed to delete the group", "error")
|
||||
notification(
|
||||
t("customer-group-table-error", "Error"),
|
||||
t(
|
||||
"customer-group-table-failed-to-delete-the-group",
|
||||
"Failed to delete the group"
|
||||
),
|
||||
"error"
|
||||
)
|
||||
},
|
||||
})
|
||||
},
|
||||
@@ -151,6 +164,7 @@ type CustomerGroupsTableProps = ReturnType<typeof useQueryFilters> & {
|
||||
function CustomerGroupsTable(props: CustomerGroupsTableProps) {
|
||||
const { customerGroups, queryObject, count, paginate, setQuery, isLoading } =
|
||||
props
|
||||
const { t } = useTranslation()
|
||||
|
||||
const tableConfig: TableOptions<CustomerGroup> = {
|
||||
columns: CUSTOMER_GROUPS_TABLE_COLUMNS,
|
||||
@@ -209,7 +223,7 @@ function CustomerGroupsTable(props: CustomerGroupsTableProps) {
|
||||
count: count,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + table.rows.length,
|
||||
title: "Customer groups",
|
||||
title: t("customer-group-table-customer-groups", "Customer groups"),
|
||||
currentPage: table.state.pageIndex + 1,
|
||||
pageCount: table.pageCount,
|
||||
nextPage: handleNext,
|
||||
|
||||
+12
-3
@@ -8,6 +8,7 @@ import {
|
||||
} from "react-table"
|
||||
|
||||
import { Customer } from "@medusajs/medusa"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import useQueryFilters from "../../../hooks/use-query-filters"
|
||||
@@ -81,10 +82,11 @@ function CustomersListTableRow(props: CustomersListTableRowProps) {
|
||||
const { row, removeCustomers } = props
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const actions = [
|
||||
{
|
||||
label: "Details",
|
||||
label: t("customer-group-table-details", "Details"),
|
||||
onClick: () => navigate(`/a/customers/${row.original.id}`),
|
||||
icon: <DetailsIcon size={20} />,
|
||||
},
|
||||
@@ -94,7 +96,10 @@ function CustomersListTableRow(props: CustomersListTableRowProps) {
|
||||
// icon: <MailIcon size={20} />,
|
||||
// },
|
||||
{
|
||||
label: "Delete from the group",
|
||||
label: t(
|
||||
"customer-group-table-delete-from-the-group",
|
||||
"Delete from the group"
|
||||
),
|
||||
variant: "danger",
|
||||
onClick: () =>
|
||||
removeCustomers({
|
||||
@@ -124,6 +129,7 @@ function CustomersListTableRow(props: CustomersListTableRowProps) {
|
||||
* Render a list of customers that belong to a customer group.
|
||||
*/
|
||||
function CustomersListTable(props: CustomersListTableProps) {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
customers,
|
||||
removeCustomers,
|
||||
@@ -186,7 +192,10 @@ function CustomersListTable(props: CustomersListTableProps) {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + table.rows.length,
|
||||
title: "Customer Groups",
|
||||
title: t(
|
||||
"customer-group-table-customer-groups-title",
|
||||
"Customer Groups"
|
||||
),
|
||||
currentPage: table.state.pageIndex + 1,
|
||||
pageCount: table.pageCount,
|
||||
nextPage: handleNext,
|
||||
|
||||
+10
-6
@@ -1,6 +1,7 @@
|
||||
import { Customer } from "@medusajs/medusa"
|
||||
import { useAdminCustomerGroups, useAdminCustomers } from "medusa-react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import {
|
||||
HeaderGroup,
|
||||
Row,
|
||||
@@ -74,6 +75,7 @@ type EditCustomersTableProps = {
|
||||
* Container for the "edit customers" table.
|
||||
*/
|
||||
function EditCustomersTable(props: EditCustomersTableProps) {
|
||||
const { t } = useTranslation()
|
||||
const { setSelectedCustomerIds, selectedCustomerIds, handleSubmit, onClose } =
|
||||
props
|
||||
|
||||
@@ -131,10 +133,10 @@ function EditCustomersTable(props: EditCustomersTableProps) {
|
||||
|
||||
const filteringOptions = [
|
||||
{
|
||||
title: "Groups",
|
||||
title: t("customer-group-table-groups", "Groups"),
|
||||
options: [
|
||||
{
|
||||
title: "All",
|
||||
title: t("customer-group-table-all", "All"),
|
||||
onClick: () => setActiveGroupId(null),
|
||||
},
|
||||
...(customer_groups || []).map((g) => ({
|
||||
@@ -176,7 +178,9 @@ function EditCustomersTable(props: EditCustomersTableProps) {
|
||||
<Modal handleClose={onClose}>
|
||||
<Modal.Body>
|
||||
<Modal.Header handleClose={onClose}>
|
||||
<h3 className="inter-xlarge-semibold">Edit Customers</h3>
|
||||
<h3 className="inter-xlarge-semibold">
|
||||
{t("customer-group-table-edit-customers", "Edit Customers")}
|
||||
</h3>
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Content>
|
||||
@@ -188,7 +192,7 @@ function EditCustomersTable(props: EditCustomersTableProps) {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + table.rows.length,
|
||||
title: "Customers",
|
||||
title: t("customer-group-table-customers", "Customers"),
|
||||
currentPage: table.state.pageIndex + 1,
|
||||
pageCount: table.pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -228,7 +232,7 @@ function EditCustomersTable(props: EditCustomersTableProps) {
|
||||
className="w-eventButton"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
{t("customer-group-table-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -236,7 +240,7 @@ function EditCustomersTable(props: EditCustomersTableProps) {
|
||||
className="w-eventButton"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Save
|
||||
{t("customer-group-table-save", "Save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Order } from "@medusajs/medusa"
|
||||
import { useAdminOrders } from "medusa-react"
|
||||
import { useState } from "react"
|
||||
import { useTable, usePagination } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import RefreshIcon from "../../fundamentals/icons/refresh-icon"
|
||||
import Table from "../../molecules/table"
|
||||
import TableContainer from "../../organisms/table-container"
|
||||
@@ -15,6 +16,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const CustomerOrdersTable = ({ id }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [selectedOrderForTransfer, setSelectedOrderForTransfer] =
|
||||
useState<Order | null>(null)
|
||||
|
||||
@@ -85,7 +87,7 @@ const CustomerOrdersTable = ({ id }: Props) => {
|
||||
count: count!,
|
||||
offset,
|
||||
pageSize: offset + rows.length,
|
||||
title: "Orders",
|
||||
title: t("customer-orders-table-orders", "Orders"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -118,7 +120,10 @@ const CustomerOrdersTable = ({ id }: Props) => {
|
||||
forceDropdown
|
||||
actions={[
|
||||
{
|
||||
label: "Transfer order",
|
||||
label: t(
|
||||
"customer-orders-table-transfer-order",
|
||||
"Transfer order"
|
||||
),
|
||||
icon: <RefreshIcon size={"20"} />,
|
||||
onClick: () => {
|
||||
setSelectedOrderForTransfer(row.original as Order)
|
||||
|
||||
+93
-21
@@ -2,49 +2,113 @@ import { Order } from "@medusajs/medusa"
|
||||
import moment from "moment"
|
||||
import { useMemo, useRef } from "react"
|
||||
import { Column } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useObserveWidth } from "../../../hooks/use-observe-width"
|
||||
import { stringDisplayPrice } from "../../../utils/prices"
|
||||
import Tooltip from "../../atoms/tooltip"
|
||||
import ImagePlaceholder from "../../fundamentals/image-placeholder"
|
||||
import StatusIndicator from "../../fundamentals/status-indicator"
|
||||
import { TFunction } from "i18next"
|
||||
|
||||
const decidePaymentStatus = (status: string) => {
|
||||
const decidePaymentStatus = (status: string, t: TFunction) => {
|
||||
switch (status) {
|
||||
case "captured":
|
||||
return <StatusIndicator variant="success" title={"Paid"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="success"
|
||||
title={t("customer-orders-table-paid", "Paid")}
|
||||
/>
|
||||
)
|
||||
case "awaiting":
|
||||
return <StatusIndicator variant="warning" title={"Awaiting"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="warning"
|
||||
title={t("customer-orders-table-awaiting", "Awaiting")}
|
||||
/>
|
||||
)
|
||||
case "requires":
|
||||
return <StatusIndicator variant="danger" title={"Requires action"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="danger"
|
||||
title={t("customer-orders-table-requires-action", "Requires action")}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <StatusIndicator variant="primary" title={"N/A"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="primary"
|
||||
title={t("customer-orders-table-n-a", "N/A")}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const decideFulfillmentStatus = (status: string) => {
|
||||
const decideFulfillmentStatus = (status: string, t: TFunction) => {
|
||||
switch (status) {
|
||||
case "fulfilled":
|
||||
return <StatusIndicator variant="success" title={"Fulfilled"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="success"
|
||||
title={t("customer-orders-table-fulfilled", "Fulfilled")}
|
||||
/>
|
||||
)
|
||||
case "shipped":
|
||||
return <StatusIndicator variant="success" title={"Shipped"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="success"
|
||||
title={t("customer-orders-table-shipped", "Shipped")}
|
||||
/>
|
||||
)
|
||||
case "not_fulfilled":
|
||||
return <StatusIndicator variant="default" title={"Not fulfilled"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="default"
|
||||
title={t("customer-orders-table-not-fulfilled", "Not fulfilled")}
|
||||
/>
|
||||
)
|
||||
case "partially_fulfilled":
|
||||
return <StatusIndicator variant="warning" title={"Partially fulfilled"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="warning"
|
||||
title={t(
|
||||
"customer-orders-table-partially-fulfilled",
|
||||
"Partially fulfilled"
|
||||
)}
|
||||
/>
|
||||
)
|
||||
case "partially_shipped":
|
||||
return <StatusIndicator variant="warning" title={"Partially shipped"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="warning"
|
||||
title={t(
|
||||
"customer-orders-table-partially-shipped",
|
||||
"Partially shipped"
|
||||
)}
|
||||
/>
|
||||
)
|
||||
case "requires":
|
||||
return <StatusIndicator variant="danger" title={"Requires action"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="danger"
|
||||
title={t("customer-orders-table-requires-action", "Requires action")}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <StatusIndicator variant="primary" title={"N/A"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
variant="primary"
|
||||
title={t("customer-orders-table-n-a", "N/A")}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const useCustomerOrdersColumns = (): Column<Order>[] => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
Header: "Order",
|
||||
Header: t("customer-orders-table-order", "Order"),
|
||||
accessor: "display_id",
|
||||
Cell: ({ value }) => {
|
||||
return <span className="text-grey-90">#{value}</span>
|
||||
@@ -95,7 +159,11 @@ export const useCustomerOrdersColumns = (): Column<Order>[] => {
|
||||
</div>
|
||||
{remainder > 0 && (
|
||||
<span className="text-grey-40 inter-small-regular">
|
||||
+ {remainder} more
|
||||
{t(
|
||||
"customer-orders-table-remainder-more",
|
||||
"+ {{remainder}} more",
|
||||
{ remainder }
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -103,28 +171,32 @@ export const useCustomerOrdersColumns = (): Column<Order>[] => {
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Date",
|
||||
Header: t("customer-orders-table-date", "Date"),
|
||||
accessor: "created_at",
|
||||
Cell: ({ value }) => {
|
||||
return moment(value).format("DD MMM YYYY hh:mm")
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Fulfillment",
|
||||
Header: t("customer-orders-table-fulfillment", "Fulfillment"),
|
||||
accessor: "fulfillment_status",
|
||||
Cell: ({ value }) => {
|
||||
return decideFulfillmentStatus(value)
|
||||
return decideFulfillmentStatus(value, t)
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Status",
|
||||
Header: t("customer-orders-table-status", "Status"),
|
||||
accessor: "payment_status",
|
||||
Cell: ({ value }) => {
|
||||
return decidePaymentStatus(value)
|
||||
return decidePaymentStatus(value, t)
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: () => <div className="text-right">Total</div>,
|
||||
Header: () => (
|
||||
<div className="text-right">
|
||||
{t("customer-orders-table-total", "Total")}
|
||||
</div>
|
||||
),
|
||||
accessor: "total",
|
||||
Cell: ({
|
||||
value,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useAdminCustomers } from "medusa-react"
|
||||
import qs from "qs"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { usePagination, useTable } from "react-table"
|
||||
import DetailsIcon from "../../fundamentals/details-icon"
|
||||
import EditIcon from "../../fundamentals/icons/edit-icon"
|
||||
@@ -19,6 +20,7 @@ const defaultQueryProps = {
|
||||
|
||||
const CustomerTable = () => {
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
reset,
|
||||
@@ -139,7 +141,7 @@ const CustomerTable = () => {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + rows.length,
|
||||
title: "Customers",
|
||||
title: t("customer-table-customers", "Customers"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -174,12 +176,12 @@ const CustomerTable = () => {
|
||||
color={"inherit"}
|
||||
actions={[
|
||||
{
|
||||
label: "Edit",
|
||||
label: t("customer-table-edit", "Edit"),
|
||||
onClick: () => navigate(row.original.id),
|
||||
icon: <EditIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Details",
|
||||
label: t("customer-table-details", "Details"),
|
||||
onClick: () => navigate(row.original.id),
|
||||
icon: <DetailsIcon size={20} />,
|
||||
},
|
||||
|
||||
+10
-4
@@ -1,18 +1,20 @@
|
||||
import moment from "moment"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { getColor } from "../../../utils/color"
|
||||
import CustomerAvatarItem from "../../molecules/customer-avatar-item"
|
||||
|
||||
export const useCustomerColumns = () => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: "Date added",
|
||||
Header: t("customer-table-date-added", "Date added"),
|
||||
accessor: "created_at", // accessor is the "key" in the data
|
||||
Cell: ({ cell: { value } }) => moment(value).format("DD MMM YYYY"),
|
||||
},
|
||||
{
|
||||
Header: "Name",
|
||||
Header: t("customer-table-name", "Name"),
|
||||
accessor: "customer",
|
||||
Cell: ({ row }) => (
|
||||
<CustomerAvatarItem
|
||||
@@ -22,7 +24,7 @@ export const useCustomerColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Email",
|
||||
Header: t("customer-table-email", "Email"),
|
||||
accessor: "email",
|
||||
},
|
||||
{
|
||||
@@ -31,7 +33,11 @@ export const useCustomerColumns = () => {
|
||||
},
|
||||
{
|
||||
accessor: "orders",
|
||||
Header: () => <div className="text-right">Orders</div>,
|
||||
Header: () => (
|
||||
<div className="text-right">
|
||||
{t("customer-table-orders", "Orders")}
|
||||
</div>
|
||||
),
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<div className="text-right">{value?.length || 0}</div>
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import clsx from "clsx"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import FilterDropdownContainer from "../../../components/molecules/filter-dropdown/container"
|
||||
import FilterDropdownItem from "../../../components/molecules/filter-dropdown/item"
|
||||
import SaveFilterItem from "../../../components/molecules/filter-dropdown/save-field"
|
||||
@@ -26,6 +27,7 @@ const DiscountFilters = ({
|
||||
submitFilters,
|
||||
clearFilters,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [tempState, setTempState] = useState(filters)
|
||||
const [name, setName] = useState("")
|
||||
|
||||
@@ -88,7 +90,7 @@ const DiscountFilters = ({
|
||||
)}
|
||||
>
|
||||
<div className="rounded-rounded bg-grey-5 border-grey-20 inter-small-semibold flex h-6 items-center border px-2">
|
||||
Filters
|
||||
{t("discount-filter-dropdown-filters", "Filters")}
|
||||
<div className="text-grey-40 ml-1 flex items-center rounded">
|
||||
<span className="text-violet-60 inter-small-semibold">
|
||||
{numberOfFilters ? numberOfFilters : "0"}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useAdminDiscounts } from "medusa-react"
|
||||
import qs from "qs"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { usePagination, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useAnalytics } from "../../../providers/analytics-provider"
|
||||
import Table from "../../molecules/table"
|
||||
import TableContainer from "../../organisms/table-container"
|
||||
@@ -16,6 +17,7 @@ const DEFAULT_PAGE_SIZE = 15
|
||||
const defaultQueryProps = {}
|
||||
|
||||
const DiscountTable: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
removeTab,
|
||||
setTab,
|
||||
@@ -157,7 +159,7 @@ const DiscountTable: React.FC = () => {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + rows.length,
|
||||
title: "Discounts",
|
||||
title: t("discount-table-discounts", "Discounts"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -181,7 +183,10 @@ const DiscountTable: React.FC = () => {
|
||||
}
|
||||
enableSearch
|
||||
handleSearch={setQuery}
|
||||
searchPlaceholder="Search by code or description..."
|
||||
searchPlaceholder={t(
|
||||
"discount-table-search-by-code-or-description",
|
||||
"Search by code or description..."
|
||||
)}
|
||||
searchValue={query}
|
||||
{...getTableProps()}
|
||||
>
|
||||
|
||||
+15
-2
@@ -1,10 +1,12 @@
|
||||
import { useAdminCreateDiscount } from "medusa-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
import { getErrorMessage } from "../../../utils/error-messages"
|
||||
import { removeFalsy } from "../../../utils/remove-nullish"
|
||||
|
||||
const useCopyPromotion = () => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const notification = useNotification()
|
||||
const createPromotion = useAdminCreateDiscount()
|
||||
@@ -60,10 +62,21 @@ const useCopyPromotion = () => {
|
||||
await createPromotion.mutate(copy, {
|
||||
onSuccess: (result) => {
|
||||
navigate(`/a/discounts/${result.discount.id}`)
|
||||
notification("Success", "Successfully copied discount", "success")
|
||||
notification(
|
||||
t("discount-table-success", "Success"),
|
||||
t(
|
||||
"discount-table-successfully-copied-discount",
|
||||
"Successfully copied discount"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
onError: (err) => {
|
||||
notification("Error", getErrorMessage(err), "error")
|
||||
notification(
|
||||
t("discount-table-error", "Error"),
|
||||
getErrorMessage(err),
|
||||
"error"
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+50
-15
@@ -1,5 +1,6 @@
|
||||
import { end, parse } from "iso8601-duration"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { formatAmountWithSymbol } from "../../../utils/prices"
|
||||
import Badge from "../../fundamentals/badge"
|
||||
import StatusDot from "../../fundamentals/status-indicator"
|
||||
@@ -34,19 +35,44 @@ const getPromotionStatus = (promotion) => {
|
||||
return PromotionStatus.DISABLED
|
||||
}
|
||||
|
||||
const getPromotionStatusDot = (promotion) => {
|
||||
const getPromotionStatusDot = (promotion, t) => {
|
||||
const status = getPromotionStatus(promotion)
|
||||
switch (status) {
|
||||
case PromotionStatus.SCHEDULED:
|
||||
return <StatusDot title="Scheduled" variant="warning" />
|
||||
return (
|
||||
<StatusDot
|
||||
title={t("discount-table-scheduled", "Scheduled")}
|
||||
variant="warning"
|
||||
/>
|
||||
)
|
||||
case PromotionStatus.EXPIRED:
|
||||
return <StatusDot title="Expired" variant="danger" />
|
||||
return (
|
||||
<StatusDot
|
||||
title={t("discount-table-expired", "Expired")}
|
||||
variant="danger"
|
||||
/>
|
||||
)
|
||||
case PromotionStatus.ACTIVE:
|
||||
return <StatusDot title="Active" variant="success" />
|
||||
return (
|
||||
<StatusDot
|
||||
title={t("discount-table-active", "Active")}
|
||||
variant="success"
|
||||
/>
|
||||
)
|
||||
case PromotionStatus.DISABLED:
|
||||
return <StatusDot title="Disabled" variant="default" />
|
||||
return (
|
||||
<StatusDot
|
||||
title={t("discount-table-disabled", "Disabled")}
|
||||
variant="default"
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <StatusDot title="Disabled" variant="default" />
|
||||
return (
|
||||
<StatusDot
|
||||
title={t("discount-table-disabled", "Disabled")}
|
||||
variant="default"
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +86,7 @@ const getCurrencySymbol = (promotion) => {
|
||||
return ""
|
||||
}
|
||||
|
||||
const getPromotionAmount = (promotion) => {
|
||||
const getPromotionAmount = (promotion, t) => {
|
||||
switch (promotion.rule.type) {
|
||||
case "fixed":
|
||||
if (!promotion.regions?.length) {
|
||||
@@ -73,17 +99,18 @@ const getPromotionAmount = (promotion) => {
|
||||
case "percentage":
|
||||
return `${promotion.rule.value}%`
|
||||
case "free_shipping":
|
||||
return "Free Shipping"
|
||||
return t("discount-table-free-shipping", "Free Shipping")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
export const usePromotionTableColumns = () => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: <div className="pl-2">Code</div>,
|
||||
Header: <div className="pl-2">{t("discount-table-code", "Code")}</div>,
|
||||
accessor: "code",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<div className="overflow-hidden">
|
||||
@@ -94,16 +121,20 @@ export const usePromotionTableColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Description",
|
||||
Header: t("discount-table-description", "Description"),
|
||||
accessor: "rule.description",
|
||||
Cell: ({ cell: { value } }) => value,
|
||||
},
|
||||
{
|
||||
Header: <div className="text-right">Amount</div>,
|
||||
Header: (
|
||||
<div className="text-right">
|
||||
{t("discount-table-amount", "Amount")}
|
||||
</div>
|
||||
),
|
||||
id: "amount",
|
||||
Cell: ({ row: { original } }) => {
|
||||
return (
|
||||
<div className="text-right">{getPromotionAmount(original)}</div>
|
||||
<div className="text-right">{getPromotionAmount(original, t)}</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
@@ -115,14 +146,18 @@ export const usePromotionTableColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Status",
|
||||
Header: t("discount-table-status", "Status"),
|
||||
accessor: "ends_at",
|
||||
Cell: ({ row: { original } }) => (
|
||||
<div>{getPromotionStatusDot(original)}</div>
|
||||
<div>{getPromotionStatusDot(original, t)}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: () => <div className="text-right">Redemptions</div>,
|
||||
Header: () => (
|
||||
<div className="text-right">
|
||||
{t("discount-table-redemptions", "Redemptions")}
|
||||
</div>
|
||||
),
|
||||
accessor: "usage_count",
|
||||
Cell: ({ row: { original } }) => {
|
||||
return (
|
||||
|
||||
+27
-10
@@ -1,3 +1,4 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useAdminDeleteDiscount, useAdminUpdateDiscount } from "medusa-react"
|
||||
import useImperativeDialog from "../../../hooks/use-imperative-dialog"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
@@ -11,6 +12,7 @@ import useCopyPromotion from "./use-copy-promotion"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
|
||||
const usePromotionActions = (promotion) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const notification = useNotification()
|
||||
const dialog = useImperativeDialog()
|
||||
@@ -22,8 +24,11 @@ const usePromotionActions = (promotion) => {
|
||||
|
||||
const handleDelete = async () => {
|
||||
const shouldDelete = await dialog({
|
||||
heading: "Delete Discount",
|
||||
text: "Are you sure you want to delete this Discount?",
|
||||
heading: t("discount-table-delete-discount", "Delete Discount"),
|
||||
text: t(
|
||||
"discount-table-confirm-delete",
|
||||
"Are you sure you want to delete this Discount?"
|
||||
),
|
||||
})
|
||||
|
||||
if (shouldDelete) {
|
||||
@@ -39,7 +44,9 @@ const usePromotionActions = (promotion) => {
|
||||
onClick: () => navigate(`/a/discounts/${promotion.id}`),
|
||||
},
|
||||
{
|
||||
label: promotion.is_disabled ? "Publish" : "Unpublish",
|
||||
label: promotion.is_disabled
|
||||
? t("discount-table-publish", "Publish")
|
||||
: t("discount-table-unpublish", "Unpublish"),
|
||||
icon: promotion.is_disabled ? (
|
||||
<PublishIcon size={20} />
|
||||
) : (
|
||||
@@ -53,26 +60,36 @@ const usePromotionActions = (promotion) => {
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
`Successfully ${
|
||||
promotion.is_disabled ? "published" : "unpublished"
|
||||
} discount`,
|
||||
t("discount-table-success", "Success"),
|
||||
promotion.is_disabled
|
||||
? t(
|
||||
"discount-table-successfully-published-discount",
|
||||
"Successfully published discount"
|
||||
)
|
||||
: t(
|
||||
"discount-table-successfully-unpublished-discount",
|
||||
"Successfully unpublished discount"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
onError: (err) =>
|
||||
notification("Error", getErrorMessage(err), "error"),
|
||||
notification(
|
||||
t("discount-table-error", "Error"),
|
||||
getErrorMessage(err),
|
||||
"error"
|
||||
),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Duplicate",
|
||||
label: t("discount-table-duplicate", "Duplicate"),
|
||||
icon: <DuplicateIcon size={20} />,
|
||||
onClick: () => copyPromotion(promotion),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: t("discount-table-delete", "Delete"),
|
||||
icon: <TrashIcon size={20} />,
|
||||
variant: "danger",
|
||||
onClick: handleDelete,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useAdminDraftOrders } from "medusa-react"
|
||||
import { Fragment, useEffect, useState } from "react"
|
||||
import { useLocation } from "react-router-dom"
|
||||
import { usePagination, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import Table from "../../molecules/table"
|
||||
import TableContainer from "../../organisms/table-container"
|
||||
import useDraftOrderTableColumns from "./use-draft-order-column"
|
||||
@@ -11,6 +12,7 @@ const DEFAULT_PAGE_SIZE = 15
|
||||
|
||||
const DraftOrderTable = () => {
|
||||
const location = useLocation()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
reset,
|
||||
@@ -107,7 +109,7 @@ const DraftOrderTable = () => {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + rows.length,
|
||||
title: "Draft Orders",
|
||||
title: t("draft-order-table-draft-orders", "Draft Orders"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
|
||||
+19
-7
@@ -1,24 +1,36 @@
|
||||
import moment from "moment"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { getColor } from "../../../utils/color"
|
||||
import StatusDot from "../../fundamentals/status-indicator"
|
||||
import CustomerAvatarItem from "../../molecules/customer-avatar-item"
|
||||
import Table from "../../molecules/table"
|
||||
|
||||
const useDraftOrderTableColumns = () => {
|
||||
const { t } = useTranslation()
|
||||
const decideStatus = (status) => {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
return <StatusDot variant="success" title={"Completed"} />
|
||||
return (
|
||||
<StatusDot
|
||||
variant="success"
|
||||
title={t("draft-order-table-completed", "Completed")}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <StatusDot variant="primary" title={"Open"} />
|
||||
return (
|
||||
<StatusDot
|
||||
variant="primary"
|
||||
title={t("draft-order-table-open", "Open")}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: "Draft",
|
||||
Header: t("draft-order-table-draft", "Draft"),
|
||||
accessor: "display_id",
|
||||
Cell: ({ cell: { value, getCellProps } }) => (
|
||||
<Table.Cell
|
||||
@@ -28,7 +40,7 @@ const useDraftOrderTableColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Order",
|
||||
Header: t("draft-order-table-order", "Order"),
|
||||
accessor: "order",
|
||||
Cell: ({ cell: { value, getCellProps } }) => {
|
||||
return (
|
||||
@@ -39,7 +51,7 @@ const useDraftOrderTableColumns = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Date added",
|
||||
Header: t("draft-order-table-date-added", "Date added"),
|
||||
accessor: "created_at",
|
||||
Cell: ({ cell: { value, getCellProps } }) => (
|
||||
<Table.Cell {...getCellProps()}>
|
||||
@@ -48,7 +60,7 @@ const useDraftOrderTableColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Customer",
|
||||
Header: t("draft-order-table-customer", "Customer"),
|
||||
accessor: "cart",
|
||||
Cell: ({ row, cell: { value, getCellProps } }) => (
|
||||
<Table.Cell {...getCellProps()}>
|
||||
@@ -64,7 +76,7 @@ const useDraftOrderTableColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Status",
|
||||
Header: t("draft-order-table-status", "Status"),
|
||||
accessor: "status",
|
||||
Cell: ({ cell: { value, getCellProps } }) => (
|
||||
<Table.Cell {...getCellProps()} className="pr-2">
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import clsx from "clsx"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import FilterDropdownContainer from "../../../components/molecules/filter-dropdown/container"
|
||||
import FilterDropdownItem from "../../../components/molecules/filter-dropdown/item"
|
||||
import SaveFilterItem from "../../../components/molecules/filter-dropdown/save-field"
|
||||
import TabFilter from "../../../components/molecules/filter-tab"
|
||||
import PlusIcon from "../../fundamentals/icons/plus-icon"
|
||||
import { TFunction } from "i18next"
|
||||
|
||||
const statusFilters = [
|
||||
"completed",
|
||||
@@ -34,12 +36,12 @@ const fulfillmentFilters = [
|
||||
"canceled",
|
||||
]
|
||||
|
||||
const dateFilters = [
|
||||
"is in the last",
|
||||
"is older than",
|
||||
"is after",
|
||||
"is before",
|
||||
"is equal to",
|
||||
const dateFilters = (t: TFunction) => [
|
||||
t("gift-card-filter-dropdown-is-in-the-last", "is in the last"),
|
||||
t("gift-card-filter-dropdown-is-older-than", "is older than"),
|
||||
t("gift-card-filter-dropdown-is-after", "is after"),
|
||||
t("gift-card-filter-dropdown-is-before", "is before"),
|
||||
t("gift-card-filter-dropdown-is-equal-to", "is equal to"),
|
||||
]
|
||||
|
||||
const OrderFilters = ({
|
||||
@@ -52,6 +54,7 @@ const OrderFilters = ({
|
||||
submitFilters,
|
||||
clearFilters,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [tempState, setTempState] = useState(filters)
|
||||
const [name, setName] = useState("")
|
||||
|
||||
@@ -114,7 +117,7 @@ const OrderFilters = ({
|
||||
)}
|
||||
>
|
||||
<div className="rounded-rounded bg-grey-5 border-grey-20 inter-small-semibold flex h-6 items-center border px-2">
|
||||
Filters
|
||||
{t("gift-card-filter-dropdown-filters", "Filters")}
|
||||
<div className="text-grey-40 ml-1 flex items-center rounded">
|
||||
<span className="text-violet-60 inter-small-semibold">
|
||||
{numberOfFilters ? numberOfFilters : "0"}
|
||||
@@ -128,29 +131,35 @@ const OrderFilters = ({
|
||||
}
|
||||
>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Status"
|
||||
filterTitle={t("gift-card-filter-dropdown-status", "Status")}
|
||||
options={statusFilters}
|
||||
filters={tempState.status.filter}
|
||||
open={tempState.status.open}
|
||||
setFilter={(val) => setSingleFilter("status", val)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Payment Status"
|
||||
filterTitle={t(
|
||||
"gift-card-filter-dropdown-payment-status",
|
||||
"Payment Status"
|
||||
)}
|
||||
options={paymentFilters}
|
||||
filters={tempState.payment.filter}
|
||||
open={tempState.payment.open}
|
||||
setFilter={(val) => setSingleFilter("payment", val)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Fulfillment Status"
|
||||
filterTitle={t(
|
||||
"gift-card-filter-dropdown-fulfillment-status",
|
||||
"Fulfillment Status"
|
||||
)}
|
||||
options={fulfillmentFilters}
|
||||
filters={tempState.fulfillment.filter}
|
||||
open={tempState.fulfillment.open}
|
||||
setFilter={(val) => setSingleFilter("fulfillment", val)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Date"
|
||||
options={dateFilters}
|
||||
filterTitle={t("gift-card-filter-dropdown-date", "Date")}
|
||||
options={dateFilters(t)}
|
||||
filters={tempState.date.filter}
|
||||
open={tempState.date.open}
|
||||
setFilter={(val) => setSingleFilter("date", val)}
|
||||
|
||||
@@ -5,6 +5,7 @@ import qs from "qs"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useLocation } from "react-router-dom"
|
||||
import { usePagination, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import Spinner from "../../atoms/spinner"
|
||||
import Table from "../../molecules/table"
|
||||
import TableContainer from "../../organisms/table-container"
|
||||
@@ -17,6 +18,7 @@ const defaultQueryProps = {}
|
||||
|
||||
const GiftCardTable = () => {
|
||||
const location = useLocation()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
reset,
|
||||
@@ -130,7 +132,7 @@ const GiftCardTable = () => {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + rows.length,
|
||||
title: "Gift cards",
|
||||
title: t("gift-card-table-gift-cards", "Gift cards"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
|
||||
+17
-7
@@ -1,15 +1,17 @@
|
||||
import moment from "moment"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { formatAmountWithSymbol } from "../../../utils/prices"
|
||||
import StatusIndicator from "../../fundamentals/status-indicator"
|
||||
import IconTooltip from "../../molecules/icon-tooltip"
|
||||
import Table from "../../molecules/table"
|
||||
|
||||
const useGiftCardTableColums = () => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: <div className="pl-2">Code</div>,
|
||||
Header: <div className="pl-2">{t("gift-card-table-code", "Code")}</div>,
|
||||
accessor: "code",
|
||||
Cell: ({ cell: { value }, index }) => (
|
||||
<Table.Cell
|
||||
@@ -21,7 +23,7 @@ const useGiftCardTableColums = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Order",
|
||||
Header: t("gift-card-table-order", "Order"),
|
||||
accessor: "order",
|
||||
Cell: ({ cell: { value }, index }) => (
|
||||
<Table.Cell
|
||||
@@ -37,7 +39,7 @@ const useGiftCardTableColums = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Original Amount",
|
||||
Header: t("gift-card-table-original-amount", "Original Amount"),
|
||||
accessor: "value",
|
||||
Cell: ({ row, cell: { value }, index }) => (
|
||||
<Table.Cell key={index}>
|
||||
@@ -56,7 +58,7 @@ const useGiftCardTableColums = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Balance",
|
||||
Header: t("gift-card-table-balance", "Balance"),
|
||||
accessor: "balance",
|
||||
Cell: ({ row, cell: { value }, index }) => (
|
||||
<Table.Cell key={index}>
|
||||
@@ -69,11 +71,19 @@ const useGiftCardTableColums = () => {
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>N / A</span>
|
||||
<IconTooltip content={"Region has been deleted"} />
|
||||
<IconTooltip
|
||||
content={t(
|
||||
"gift-card-table-region-has-been-deleted",
|
||||
"Region has been deleted"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<StatusIndicator title="None" variant="danger" />
|
||||
<StatusIndicator
|
||||
title={t("gift-card-table-none", "None")}
|
||||
variant="danger"
|
||||
/>
|
||||
)}
|
||||
</Table.Cell>
|
||||
),
|
||||
@@ -81,7 +91,7 @@ const useGiftCardTableColums = () => {
|
||||
{
|
||||
Header: () => (
|
||||
<div className="rounded-rounded flex w-full justify-end pr-2">
|
||||
Created
|
||||
{t("gift-card-table-created", "Created")}
|
||||
</div>
|
||||
),
|
||||
accessor: "created_at",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMemo } from "react"
|
||||
import { Controller } from "react-hook-form"
|
||||
import { Column, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { FormImage } from "../../../types/shared"
|
||||
import { NestedForm } from "../../../utils/nested-form"
|
||||
import Button from "../../fundamentals/button"
|
||||
@@ -18,6 +19,7 @@ type ImageTableProps = {
|
||||
}
|
||||
|
||||
const ImageTable = ({ data, form, onDelete }: ImageTableProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { control, register, path } = form
|
||||
|
||||
const columns = useMemo<
|
||||
@@ -46,7 +48,7 @@ const ImageTable = ({ data, form, onDelete }: ImageTableProps) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: () => <span>File name</span>,
|
||||
Header: () => <span>{t("image-table-file-name", "File name")}</span>,
|
||||
accessor: "nativeFile",
|
||||
Cell: ({ cell }) => {
|
||||
return (
|
||||
@@ -72,8 +74,13 @@ const ImageTable = ({ data, form, onDelete }: ImageTableProps) => {
|
||||
{
|
||||
Header: () => (
|
||||
<div className="flex items-center justify-center gap-x-[6px]">
|
||||
<span>Thumbnail</span>
|
||||
<IconTooltip content="Select which image you want to use as the thumbnail for this product" />
|
||||
<span>{t("image-table-thumbnail", "Thumbnail")}</span>
|
||||
<IconTooltip
|
||||
content={t(
|
||||
"image-table-select-thumbnail-image-for-product",
|
||||
"Select which image you want to use as the thumbnail for this product"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
id: "thumbnail",
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
useAdminVariant,
|
||||
} from "medusa-react"
|
||||
import { useLocation, useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import Button from "../../fundamentals/button"
|
||||
import ImagePlaceholder from "../../fundamentals/image-placeholder"
|
||||
@@ -84,6 +85,7 @@ const InventoryTable: React.FC<InventoryTableProps> = () => {
|
||||
const { store } = useAdminStore()
|
||||
|
||||
const location = useLocation()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { stock_locations, isLoading: locationsLoading } =
|
||||
useAdminStockLocations()
|
||||
@@ -217,7 +219,7 @@ const InventoryTable: React.FC<InventoryTableProps> = () => {
|
||||
count: count || 0,
|
||||
offset: offs,
|
||||
pageSize: offs + rows.length,
|
||||
title: "Inventory Items",
|
||||
title: t("inventory-table-inventory-items", "Inventory Items"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -300,6 +302,7 @@ const InventoryRow = ({
|
||||
const inventory = row.original
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
state: isShowingAdjustAvailabilityModal,
|
||||
@@ -312,7 +315,10 @@ const InventoryRow = ({
|
||||
|
||||
const actions = [
|
||||
{
|
||||
label: "Adjust Availability",
|
||||
label: t(
|
||||
"inventory-table-actions-adjust-availability",
|
||||
"Adjust Availability"
|
||||
),
|
||||
onClick: showAdjustAvailabilityModal,
|
||||
},
|
||||
]
|
||||
@@ -320,7 +326,7 @@ const InventoryRow = ({
|
||||
if (productId) {
|
||||
return [
|
||||
{
|
||||
label: "View Product",
|
||||
label: t("inventory-table-view-product", "View Product"),
|
||||
onClick: () => navigate(`/a/products/${productId}`),
|
||||
},
|
||||
...actions,
|
||||
@@ -370,6 +376,7 @@ const AdjustAvailabilityModal = ({
|
||||
const inventoryVariantId = inventory.variants?.[0]?.id
|
||||
const locationLevel = inventory.location_levels?.[0]
|
||||
|
||||
const { t } = useTranslation()
|
||||
const { variant, isLoading } = useAdminVariant(inventoryVariantId || "")
|
||||
const {
|
||||
mutate: updateLocationLevelForInventoryItem,
|
||||
@@ -396,8 +403,11 @@ const AdjustAvailabilityModal = ({
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
"Inventory item updated successfully",
|
||||
t("inventory-table-success", "Success"),
|
||||
t(
|
||||
"inventory-table-inventory-item-updated-successfully",
|
||||
"Inventory item updated successfully"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
handleClose()
|
||||
@@ -412,7 +422,9 @@ const AdjustAvailabilityModal = ({
|
||||
<Modal handleClose={handleClose}>
|
||||
<Modal.Body>
|
||||
<Modal.Header handleClose={handleClose}>
|
||||
<h1 className="inter-large-semibold">Adjust availability</h1>
|
||||
<h1 className="inter-large-semibold">
|
||||
{t("inventory-table-adjust-availability", "Adjust availability")}
|
||||
</h1>
|
||||
</Modal.Header>
|
||||
<Modal.Content>
|
||||
{isLoading ? (
|
||||
@@ -468,7 +480,7 @@ const AdjustAvailabilityModal = ({
|
||||
className="border"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
{t("inventory-table-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
@@ -477,7 +489,7 @@ const AdjustAvailabilityModal = ({
|
||||
loading={isSubmitting}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
Save and close
|
||||
{t("inventory-table-save-and-close", "Save and close")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
||||
+7
-5
@@ -6,16 +6,18 @@ import { InventoryLevelDTO } from "@medusajs/types"
|
||||
import Tooltip from "../../atoms/tooltip"
|
||||
import { useMemo } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
const useInventoryTableColumn = ({
|
||||
location_id,
|
||||
}: {
|
||||
location_id: string
|
||||
}): [Column<DecoratedInventoryItemDTO>[]] => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: "Item",
|
||||
Header: t("inventory-table-item", "Item"),
|
||||
accessor: "title",
|
||||
Cell: ({ row: { original } }) => {
|
||||
return (
|
||||
@@ -36,18 +38,18 @@ const useInventoryTableColumn = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Variant",
|
||||
Header: t("inventory-table-variant", "Variant"),
|
||||
Cell: ({ row: { original } }) => {
|
||||
return <div>{original?.variants[0]?.title || "-"}</div>
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "SKU",
|
||||
Header: t("inventory-table-sku", "SKU"),
|
||||
accessor: "sku",
|
||||
Cell: ({ cell: { value } }) => value,
|
||||
},
|
||||
{
|
||||
Header: "Reserved",
|
||||
Header: t("inventory-table-reserved", "Reserved"),
|
||||
accessor: "reserved_quantity",
|
||||
Cell: ({ row: { original } }) => {
|
||||
const navigate = useNavigate()
|
||||
@@ -85,7 +87,7 @@ const useInventoryTableColumn = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "In stock",
|
||||
Header: t("inventory-table-in-stock", "In stock"),
|
||||
accessor: "stocked_quantity",
|
||||
Cell: ({ row: { original } }) => {
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import clsx from "clsx"
|
||||
import { useAdminRegions, useAdminSalesChannels } from "medusa-react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import FilterDropdownContainer from "../../../components/molecules/filter-dropdown/container"
|
||||
import FilterDropdownItem from "../../../components/molecules/filter-dropdown/item"
|
||||
import SaveFilterItem from "../../../components/molecules/filter-dropdown/save-field"
|
||||
import TabFilter from "../../../components/molecules/filter-tab"
|
||||
import PlusIcon from "../../fundamentals/icons/plus-icon"
|
||||
import FeatureToggle from "../../fundamentals/feature-toggle"
|
||||
import { useFeatureFlag } from "../../../providers/feature-flag-provider"
|
||||
|
||||
const REGION_PAGE_SIZE = 10
|
||||
@@ -58,6 +58,7 @@ const OrderFilters = ({
|
||||
submitFilters,
|
||||
clearFilters,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [tempState, setTempState] = useState(filters)
|
||||
const [name, setName] = useState("")
|
||||
|
||||
@@ -154,7 +155,7 @@ const OrderFilters = ({
|
||||
)}
|
||||
>
|
||||
<div className="rounded-rounded bg-grey-5 border-grey-20 inter-small-semibold flex h-6 items-center border px-2">
|
||||
Filters
|
||||
{t("order-filter-dropdown-filters", "Filters")}
|
||||
<div className="text-grey-40 ml-1 flex items-center rounded">
|
||||
<span className="text-violet-60 inter-small-semibold">
|
||||
{numberOfFilters ? numberOfFilters : "0"}
|
||||
@@ -168,28 +169,34 @@ const OrderFilters = ({
|
||||
}
|
||||
>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Status"
|
||||
filterTitle={t("order-filter-dropdown-status", "Status")}
|
||||
options={statusFilters}
|
||||
filters={tempState.status.filter}
|
||||
open={tempState.status.open}
|
||||
setFilter={(val) => setSingleFilter("status", val)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Payment Status"
|
||||
filterTitle={t(
|
||||
"order-filter-dropdown-payment-status",
|
||||
"Payment Status"
|
||||
)}
|
||||
options={paymentFilters}
|
||||
filters={tempState.payment.filter}
|
||||
open={tempState.payment.open}
|
||||
setFilter={(val) => setSingleFilter("payment", val)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Fulfillment Status"
|
||||
filterTitle={t(
|
||||
"order-filter-dropdown-fulfillment-status",
|
||||
"Fulfillment Status"
|
||||
)}
|
||||
options={fulfillmentFilters}
|
||||
filters={tempState.fulfillment.filter}
|
||||
open={tempState.fulfillment.open}
|
||||
setFilter={(val) => setSingleFilter("fulfillment", val)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Regions"
|
||||
filterTitle={t("order-filter-dropdown-regions", "Regions")}
|
||||
options={
|
||||
regions?.map((region) => ({
|
||||
value: region.id,
|
||||
@@ -209,7 +216,10 @@ const OrderFilters = ({
|
||||
/>
|
||||
{isSalesChannelsEnabled && (
|
||||
<FilterDropdownItem
|
||||
filterTitle="Sales Channel"
|
||||
filterTitle={t(
|
||||
"order-filter-dropdown-sales-channel",
|
||||
"Sales Channel"
|
||||
)}
|
||||
options={
|
||||
sales_channels?.map((salesChannel) => ({
|
||||
value: salesChannel.id,
|
||||
@@ -223,7 +233,7 @@ const OrderFilters = ({
|
||||
/>
|
||||
)}
|
||||
<FilterDropdownItem
|
||||
filterTitle="Date"
|
||||
filterTitle={t("order-filter-dropdown-date", "Date")}
|
||||
options={dateFilters}
|
||||
filters={tempState.date.filter}
|
||||
open={tempState.date.open}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import moment from "moment"
|
||||
import { useMemo } from "react"
|
||||
import ReactCountryFlag from "react-country-flag"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { getColor } from "../../../utils/color"
|
||||
import { isoAlpha2Countries } from "../../../utils/countries"
|
||||
import { formatAmountWithSymbol } from "../../../utils/prices"
|
||||
@@ -9,32 +10,52 @@ import StatusDot from "../../fundamentals/status-indicator"
|
||||
import CustomerAvatarItem from "../../molecules/customer-avatar-item"
|
||||
|
||||
const useOrderTableColums = () => {
|
||||
const { t } = useTranslation()
|
||||
const decideStatus = (status) => {
|
||||
switch (status) {
|
||||
case "captured":
|
||||
return <StatusDot variant="success" title={"Paid"} />
|
||||
return (
|
||||
<StatusDot variant="success" title={t("order-table-paid", "Paid")} />
|
||||
)
|
||||
case "awaiting":
|
||||
return <StatusDot variant="default" title={"Awaiting"} />
|
||||
return (
|
||||
<StatusDot
|
||||
variant="default"
|
||||
title={t("order-table-awaiting", "Awaiting")}
|
||||
/>
|
||||
)
|
||||
case "requires_action":
|
||||
return <StatusDot variant="danger" title={"Requires action"} />
|
||||
return (
|
||||
<StatusDot
|
||||
variant="danger"
|
||||
title={t("order-table-requires-action", "Requires action")}
|
||||
/>
|
||||
)
|
||||
case "canceled":
|
||||
return <StatusDot variant="warning" title={"Canceled"} />
|
||||
return (
|
||||
<StatusDot
|
||||
variant="warning"
|
||||
title={t("order-table-canceled", "Canceled")}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <StatusDot variant="primary" title={"N/A"} />
|
||||
return (
|
||||
<StatusDot variant="primary" title={t("order-table-n-a", "N/A")} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: <div className="pl-2">Order</div>,
|
||||
Header: <div className="pl-2">{t("order-table-order", "Order")}</div>,
|
||||
accessor: "display_id",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<p className="text-grey-90 group-hover:text-violet-60 min-w-[100px] pl-2">{`#${value}`}</p>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Date added",
|
||||
Header: t("order-table-date-added", "Date added"),
|
||||
accessor: "created_at",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<div>
|
||||
@@ -45,7 +66,7 @@ const useOrderTableColums = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Customer",
|
||||
Header: t("order-table-customer", "Customer"),
|
||||
accessor: "customer",
|
||||
Cell: ({ row, cell: { value } }) => (
|
||||
<div>
|
||||
@@ -64,22 +85,24 @@ const useOrderTableColums = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Fulfillment",
|
||||
Header: t("order-table-fulfillment", "Fulfillment"),
|
||||
accessor: "fulfillment_status",
|
||||
Cell: ({ cell: { value } }) => value,
|
||||
},
|
||||
{
|
||||
Header: "Payment status",
|
||||
Header: t("order-table-payment-status", "Payment status"),
|
||||
accessor: "payment_status",
|
||||
Cell: ({ cell: { value } }) => decideStatus(value),
|
||||
},
|
||||
{
|
||||
Header: "Sales Channel",
|
||||
Header: t("order-table-sales-channel", "Sales Channel"),
|
||||
accessor: "sales_channel",
|
||||
Cell: ({ cell: { value } }) => value?.name ?? "N/A",
|
||||
},
|
||||
{
|
||||
Header: () => <div className="text-right">Total</div>,
|
||||
Header: () => (
|
||||
<div className="text-right">{t("order-table-total", "Total")}</div>
|
||||
),
|
||||
accessor: "total",
|
||||
Cell: ({ row, cell: { value } }) => (
|
||||
<div className="text-right">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { omit } from "lodash"
|
||||
import qs from "qs"
|
||||
import { useMemo, useReducer, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { relativeDateFormatToTimestamp } from "../../../utils/time"
|
||||
|
||||
type OrderDateFilter = null | {
|
||||
@@ -156,6 +157,8 @@ export const useOrderFilters = (
|
||||
existing?: string,
|
||||
defaultFilters: OrderDefaultFilters | null = null
|
||||
) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (existing && existing[0] === "?") {
|
||||
existing = existing.substring(1)
|
||||
}
|
||||
@@ -365,11 +368,11 @@ export const useOrderFilters = (
|
||||
const availableTabs = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: "Complete",
|
||||
label: t("order-table-filters-complete", "Complete"),
|
||||
value: "complete",
|
||||
},
|
||||
{
|
||||
label: "Incomplete",
|
||||
label: t("order-table-filters-incomplete", "Incomplete"),
|
||||
value: "incomplete",
|
||||
},
|
||||
...tabs,
|
||||
@@ -423,7 +426,6 @@ export const useOrderFilters = (
|
||||
const clean = omit(repObj, ["limit", "offset"])
|
||||
const repString = qs.stringify(clean, { skipNulls: true })
|
||||
|
||||
|
||||
const storedString = localStorage.getItem("orders::filters")
|
||||
|
||||
let existing: null | object = null
|
||||
|
||||
+5
-3
@@ -1,5 +1,6 @@
|
||||
import clsx from "clsx"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
||||
import FilterDropdownContainer from "../../../components/molecules/filter-dropdown/container"
|
||||
import FilterDropdownItem from "../../../components/molecules/filter-dropdown/item"
|
||||
@@ -19,6 +20,7 @@ const PriceListsFilter = ({
|
||||
onRemoveTab,
|
||||
onSaveTab,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [tempState, setTempState] = useState(filters)
|
||||
const [name, setName] = useState("")
|
||||
|
||||
@@ -82,7 +84,7 @@ const PriceListsFilter = ({
|
||||
)}
|
||||
>
|
||||
<div className="rounded-rounded bg-grey-5 border-grey-20 inter-small-semibold flex h-6 items-center border px-2">
|
||||
Filters
|
||||
{t("price-list-table-filters", "Filters")}
|
||||
<div className="text-grey-40 ml-1 flex items-center rounded">
|
||||
<span className="text-violet-60 inter-small-semibold">
|
||||
{numberOfFilters ? numberOfFilters : "0"}
|
||||
@@ -96,14 +98,14 @@ const PriceListsFilter = ({
|
||||
}
|
||||
>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Status"
|
||||
filterTitle={t("price-list-table-status", "Status")}
|
||||
options={statusFilters}
|
||||
filters={tempState.status.filter}
|
||||
open={tempState.status.open}
|
||||
setFilter={(v) => setSingleFilter("status", v)}
|
||||
/>
|
||||
<FilterDropdownItem
|
||||
filterTitle="Type"
|
||||
filterTitle={t("price-list-table-type", "Type")}
|
||||
options={typeFilters}
|
||||
filters={tempState.type.filter}
|
||||
open={tempState.type.open}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
UseSortByColumnProps,
|
||||
useTable,
|
||||
} from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useDebounce } from "../../../hooks/use-debounce"
|
||||
import Table, { TableProps } from "../../molecules/table"
|
||||
import TableContainer from "../../organisms/table-container"
|
||||
@@ -121,6 +122,7 @@ export function PriceListTable(props: PriceListTableProps) {
|
||||
autoResetPage: false,
|
||||
}
|
||||
|
||||
const { t } = useTranslation()
|
||||
const table = useTable(tableConfig, useSortBy, usePagination, useRowSelect)
|
||||
|
||||
// ********* HANDLERS *********
|
||||
@@ -164,7 +166,7 @@ export function PriceListTable(props: PriceListTableProps) {
|
||||
count: count!,
|
||||
offset: queryObject.offset,
|
||||
pageSize: queryObject.offset + table.rows.length,
|
||||
title: "Price Lists",
|
||||
title: t("price-list-table-price-lists", "Price Lists"),
|
||||
currentPage: table.state.pageIndex + 1,
|
||||
pageCount: table.pageCount,
|
||||
nextPage: handleNext,
|
||||
|
||||
+15
-2
@@ -1,5 +1,6 @@
|
||||
import { useAdminCreatePriceList } from "medusa-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
import { getErrorMessage } from "../../../utils/error-messages"
|
||||
|
||||
@@ -7,6 +8,7 @@ const useCopyPriceList = () => {
|
||||
const navigate = useNavigate()
|
||||
const notification = useNotification()
|
||||
const createPriceList = useAdminCreatePriceList()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleCopyPriceList = async (priceList) => {
|
||||
const copy: any = {
|
||||
@@ -44,9 +46,20 @@ const useCopyPriceList = () => {
|
||||
try {
|
||||
const data = await createPriceList.mutateAsync(copy)
|
||||
navigate(`/a/pricing/${data.price_list.id}`)
|
||||
notification("Success", "Successfully copied price list", "success")
|
||||
notification(
|
||||
t("price-list-table-success", "Success"),
|
||||
t(
|
||||
"price-list-table-successfully-copied-price-list",
|
||||
"Successfully copied price list"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
} catch (err) {
|
||||
notification("Error", getErrorMessage(err), "error")
|
||||
notification(
|
||||
t("price-list-table-error", "Error"),
|
||||
getErrorMessage(err),
|
||||
"error"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-11
@@ -1,4 +1,5 @@
|
||||
import { useAdminDeletePriceList, useAdminUpdatePriceList } from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useImperativeDialog from "../../../hooks/use-imperative-dialog"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
import { getErrorMessage } from "../../../utils/error-messages"
|
||||
@@ -9,6 +10,7 @@ import { isActive } from "./utils"
|
||||
import PublishIcon from "../../fundamentals/icons/publish-icon"
|
||||
|
||||
const usePriceListActions = (priceList) => {
|
||||
const { t } = useTranslation()
|
||||
const dialog = useImperativeDialog()
|
||||
const notification = useNotification()
|
||||
const updatePrice = useAdminUpdatePriceList(priceList?.id)
|
||||
@@ -16,19 +18,30 @@ const usePriceListActions = (priceList) => {
|
||||
|
||||
const onDelete = async () => {
|
||||
const shouldDelete = await dialog({
|
||||
heading: "Delete Price List",
|
||||
text: "Are you sure you want to delete this price list?",
|
||||
heading: t("price-list-table-delete-price-list", "Delete Price List"),
|
||||
text: t(
|
||||
"price-list-table-confirm-delete",
|
||||
"Are you sure you want to delete this price list?"
|
||||
),
|
||||
})
|
||||
if (shouldDelete) {
|
||||
deletePrice.mutate(undefined, {
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
"Successfully deleted the price list",
|
||||
t("price-list-table-success", "Success"),
|
||||
t(
|
||||
"price-list-table-successfully-deleted-the-price-list",
|
||||
"Successfully deleted the price list"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
onError: (err) => notification("Error", getErrorMessage(err), "error"),
|
||||
onError: (err) =>
|
||||
notification(
|
||||
t("price-list-table-error", "Error"),
|
||||
getErrorMessage(err),
|
||||
"error"
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -41,10 +54,16 @@ const usePriceListActions = (priceList) => {
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
`Successfully ${
|
||||
isActive(priceList) ? "unpublished" : "published"
|
||||
} price list`,
|
||||
t("price-list-table-success", "Success"),
|
||||
isActive(priceList)
|
||||
? t(
|
||||
"price-list-table-successfully-unpublished-price-list",
|
||||
"Successfully unpublished price list"
|
||||
)
|
||||
: t(
|
||||
"price-list-table-successfully-published-price-list",
|
||||
"Successfully published price list"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
@@ -54,7 +73,9 @@ const usePriceListActions = (priceList) => {
|
||||
|
||||
const getActions = (): ActionType[] => [
|
||||
{
|
||||
label: isActive(priceList) ? "Unpublish" : "Publish",
|
||||
label: isActive(priceList)
|
||||
? t("price-list-table-unpublish", "Unpublish")
|
||||
: t("price-list-table-publish", "Publish"),
|
||||
onClick: onUpdate,
|
||||
icon: isActive(priceList) ? (
|
||||
<UnpublishIcon size={20} />
|
||||
@@ -63,7 +84,7 @@ const usePriceListActions = (priceList) => {
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: t("price-list-table-delete", "Delete"),
|
||||
onClick: onDelete,
|
||||
icon: <TrashIcon size={20} />,
|
||||
variant: "danger",
|
||||
|
||||
+14
-5
@@ -2,16 +2,18 @@ import { PriceList } from "@medusajs/medusa"
|
||||
import { isArray } from "lodash"
|
||||
import { useMemo } from "react"
|
||||
import { Column } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import Actionables from "../../molecules/actionables"
|
||||
import Table from "../../molecules/table"
|
||||
import usePriceListActions from "./use-price-list-actions"
|
||||
import { formatPriceListGroups, getPriceListStatus } from "./utils"
|
||||
|
||||
export const usePriceListTableColumns = () => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo<Column<PriceList>[]>(
|
||||
() => [
|
||||
{
|
||||
Header: "Name",
|
||||
Header: t("price-list-table-name", "Name"),
|
||||
accessor: "name",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<Table.Cell>
|
||||
@@ -20,19 +22,19 @@ export const usePriceListTableColumns = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Description",
|
||||
Header: t("price-list-table-description", "Description"),
|
||||
accessor: "description",
|
||||
Cell: ({ cell: { value } }) => <Table.Cell>{value}</Table.Cell>,
|
||||
},
|
||||
{
|
||||
Header: "Status",
|
||||
Header: t("price-list-table-status", "Status"),
|
||||
accessor: "status",
|
||||
Cell: ({ row: { original } }) => (
|
||||
<Table.Cell>{getPriceListStatus(original)}</Table.Cell>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Groups",
|
||||
Header: t("price-list-table-groups", "Groups"),
|
||||
accessor: "customer_groups",
|
||||
Cell: ({ cell: { value } }) => {
|
||||
const groups: string[] = isArray(value)
|
||||
@@ -42,7 +44,14 @@ export const usePriceListTableColumns = () => {
|
||||
return (
|
||||
<Table.Cell>
|
||||
{group}
|
||||
{other && <span className="text-grey-40"> + {other} more</span>}
|
||||
{other && (
|
||||
<span className="text-grey-40">
|
||||
{" "}
|
||||
{t("price-list-table-other-more", "+ {{other}} more", {
|
||||
other,
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</Table.Cell>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MoneyAmount, ProductVariant } from "@medusajs/medusa"
|
||||
import React from "react"
|
||||
import { Control, Controller, useForm, useWatch } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import Checkbox, { CheckboxProps } from "../../atoms/checkbox"
|
||||
import Button from "../../fundamentals/button"
|
||||
import Modal from "../../molecules/modal"
|
||||
@@ -35,6 +36,7 @@ const PriceOverrides = ({
|
||||
defaultVariant,
|
||||
isEdit = false,
|
||||
}: PriceOverridesType) => {
|
||||
const { t } = useTranslation()
|
||||
const [mode, setMode] = React.useState(MODES.SELECTED_ONLY)
|
||||
const {
|
||||
handleSubmit,
|
||||
@@ -92,11 +94,17 @@ const PriceOverrides = ({
|
||||
>
|
||||
<RadioGroup.SimpleItem
|
||||
value={MODES.SELECTED_ONLY}
|
||||
label="Apply overrides on selected variants"
|
||||
label={t(
|
||||
"price-overrides-apply-overrides-on-selected-variants",
|
||||
"Apply overrides on selected variants"
|
||||
)}
|
||||
/>
|
||||
<RadioGroup.SimpleItem
|
||||
value={MODES.APPLY_ALL}
|
||||
label="Apply on all variants"
|
||||
label={t(
|
||||
"price-overrides-apply-on-all-variants",
|
||||
"Apply on all variants"
|
||||
)}
|
||||
/>
|
||||
</RadioGroup.Root>
|
||||
)}
|
||||
@@ -120,7 +128,9 @@ const PriceOverrides = ({
|
||||
</div>
|
||||
)}
|
||||
<div className="pt-8">
|
||||
<h6 className="inter-base-semibold">Prices</h6>
|
||||
<h6 className="inter-base-semibold">
|
||||
{t("price-overrides-prices", "Prices")}
|
||||
</h6>
|
||||
<div className="pt-4">
|
||||
{prices.map((price, idx) => (
|
||||
<Controller
|
||||
@@ -154,7 +164,7 @@ const PriceOverrides = ({
|
||||
size="large"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
{t("price-overrides-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
size="large"
|
||||
@@ -163,7 +173,7 @@ const PriceOverrides = ({
|
||||
onClick={onClick}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Save and close
|
||||
{t("price-overrides-save-and-close", "Save and close")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useToggleState from "../../../hooks/use-toggle-state"
|
||||
import { currencies } from "../../../utils/currencies"
|
||||
import Button from "../../fundamentals/button"
|
||||
@@ -6,6 +7,7 @@ import EyeOffIcon from "../../fundamentals/icons/eye-off-icon"
|
||||
import MedusaPriceInput from "../../organisms/medusa-price-input"
|
||||
|
||||
const PriceAmount = ({ value, onChange }) => {
|
||||
const { t } = useTranslation()
|
||||
const { state: showRegions, toggle } = useToggleState()
|
||||
|
||||
const currencyName = currencies[value.currency_code?.toUpperCase()]?.name
|
||||
@@ -28,7 +30,7 @@ const PriceAmount = ({ value, onChange }) => {
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{showRegions ? <EyeOffIcon size={20} /> : <EyeIcon size={20} />}
|
||||
<span>Show regions</span>
|
||||
<span>{t("price-overrides-show-regions", "Show regions")}</span>
|
||||
</div>
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
@@ -4,6 +4,7 @@ import qs from "qs"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { useLocation } from "react-router-dom"
|
||||
import { usePagination, useTable } from "react-table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import ProductsFilter from "../../../domain/products/filter-dropdown"
|
||||
import { useAnalytics } from "../../../providers/analytics-provider"
|
||||
import { useFeatureFlag } from "../../../providers/feature-flag-provider"
|
||||
@@ -26,6 +27,7 @@ const defaultQueryProps = {
|
||||
|
||||
const ProductTable = () => {
|
||||
const location = useLocation()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { isFeatureEnabled } = useFeatureFlag()
|
||||
const { trackNumberOfProducts } = useAnalytics()
|
||||
@@ -185,7 +187,7 @@ const ProductTable = () => {
|
||||
count: count!,
|
||||
offset: offs,
|
||||
pageSize: offs + rows.length,
|
||||
title: "Products",
|
||||
title: t("product-table-products", "Products"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AdminPostProductsReq, Product } from "@medusajs/medusa"
|
||||
import { omit } from "lodash"
|
||||
import { useAdminCreateProduct } from "medusa-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
import { ProductStatus } from "../../../types/shared"
|
||||
import { getErrorMessage } from "../../../utils/error-messages"
|
||||
@@ -37,6 +38,7 @@ const useCopyProduct = () => {
|
||||
const navigate = useNavigate()
|
||||
const notification = useNotification()
|
||||
const { mutate } = useAdminCreateProduct()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleCopyProduct = (product: Product) => {
|
||||
const {
|
||||
@@ -103,7 +105,7 @@ const useCopyProduct = () => {
|
||||
"product",
|
||||
"product_id",
|
||||
"variant_rank",
|
||||
"purchasable"
|
||||
"purchasable",
|
||||
])
|
||||
|
||||
const variantBase = Object.entries(rest).reduce((acc, [key, value]) => {
|
||||
@@ -181,10 +183,21 @@ const useCopyProduct = () => {
|
||||
mutate(base as AdminPostProductsReq, {
|
||||
onSuccess: ({ product: copiedProduct }) => {
|
||||
navigate(`/a/products/${copiedProduct.id}`)
|
||||
notification("Success", "Created a new product", "success")
|
||||
notification(
|
||||
t("product-table-copy-success", "Success"),
|
||||
t(
|
||||
"product-table-copy-created-a-new-product",
|
||||
"Created a new product"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
onError: (error) => {
|
||||
notification("Error", getErrorMessage(error), "error")
|
||||
notification(
|
||||
t("product-table-copy-error", "Error"),
|
||||
getErrorMessage(error),
|
||||
"error"
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+33
-12
@@ -1,6 +1,7 @@
|
||||
import { Product } from "@medusajs/medusa"
|
||||
import { useAdminDeleteProduct, useAdminUpdateProduct } from "medusa-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useImperativeDialog from "../../../hooks/use-imperative-dialog"
|
||||
import useNotification from "../../../hooks/use-notification"
|
||||
import { getErrorMessage } from "../../../utils/error-messages"
|
||||
@@ -13,6 +14,7 @@ import { ActionType } from "../../molecules/actionables"
|
||||
import useCopyProduct from "./use-copy-product"
|
||||
|
||||
const useProductActions = (product: Product) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const notification = useNotification()
|
||||
const dialog = useImperativeDialog()
|
||||
@@ -22,8 +24,11 @@ const useProductActions = (product: Product) => {
|
||||
|
||||
const handleDelete = async () => {
|
||||
const shouldDelete = await dialog({
|
||||
heading: "Delete Product",
|
||||
text: "Are you sure you want to delete this product?",
|
||||
heading: t("product-table-delete-product", "Delete Product"),
|
||||
text: t(
|
||||
"product-table-confirm-delete",
|
||||
"Are you sure you want to delete this product?"
|
||||
),
|
||||
})
|
||||
|
||||
if (shouldDelete) {
|
||||
@@ -33,14 +38,20 @@ const useProductActions = (product: Product) => {
|
||||
|
||||
const getActions = (): ActionType[] => [
|
||||
{
|
||||
label: "Edit",
|
||||
label: t("product-table-edit", "Edit"),
|
||||
onClick: () => navigate(`/a/products/${product.id}`),
|
||||
icon: <EditIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: product.status === "published" ? "Unpublish" : "Publish",
|
||||
label:
|
||||
product.status === "published"
|
||||
? t("product-table-unpublish", "Unpublish")
|
||||
: t("product-table-publish", "Publish"),
|
||||
onClick: () => {
|
||||
const newStatus = product.status === "published" ? "draft" : "published"
|
||||
const newStatus =
|
||||
product.status === "published"
|
||||
? t("product-table-draft", "draft")
|
||||
: t("product-table-published", "published")
|
||||
updateProduct.mutate(
|
||||
{
|
||||
status: newStatus,
|
||||
@@ -48,15 +59,25 @@ const useProductActions = (product: Product) => {
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
`Successfully ${
|
||||
product.status === "published" ? "unpublished" : "published"
|
||||
} product`,
|
||||
t("product-table-success", "Success"),
|
||||
product.status === "published"
|
||||
? t(
|
||||
"product-table-successfully-unpublished-product",
|
||||
"Successfully unpublished product"
|
||||
)
|
||||
: t(
|
||||
"product-table-successfully-published-product",
|
||||
"Successfully published product"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
onError: (err) =>
|
||||
notification("Error", getErrorMessage(err), "error"),
|
||||
notification(
|
||||
t("product-table-error", "Error"),
|
||||
getErrorMessage(err),
|
||||
"error"
|
||||
),
|
||||
}
|
||||
)
|
||||
},
|
||||
@@ -68,12 +89,12 @@ const useProductActions = (product: Product) => {
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Duplicate",
|
||||
label: t("product-table-duplicate", "Duplicate"),
|
||||
onClick: () => copyProduct(product),
|
||||
icon: <DuplicateIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: t("product-table-delete", "Delete"),
|
||||
variant: "danger",
|
||||
onClick: handleDelete,
|
||||
icon: <TrashIcon size={20} />,
|
||||
|
||||
+37
-11
@@ -1,6 +1,7 @@
|
||||
import clsx from "clsx"
|
||||
import { useAdminStore } from "medusa-react"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { defaultChannelsSorter } from "../../../utils/sales-channel-compare-operator"
|
||||
import DelimitedList from "../../molecules/delimited-list"
|
||||
import ListIcon from "../../fundamentals/icons/list-icon"
|
||||
@@ -9,16 +10,38 @@ import ImagePlaceholder from "../../fundamentals/image-placeholder"
|
||||
import StatusIndicator from "../../fundamentals/status-indicator"
|
||||
|
||||
const useProductTableColumn = ({ setTileView, setListView, showList }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const getProductStatus = (status) => {
|
||||
switch (status) {
|
||||
case "proposed":
|
||||
return <StatusIndicator title={"Proposed"} variant={"warning"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
title={t("product-table-proposed", "Proposed")}
|
||||
variant={"warning"}
|
||||
/>
|
||||
)
|
||||
case "published":
|
||||
return <StatusIndicator title={"Published"} variant={"success"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
title={t("product-table-published-title", "Published")}
|
||||
variant={"success"}
|
||||
/>
|
||||
)
|
||||
case "rejected":
|
||||
return <StatusIndicator title={"Rejected"} variant={"danger"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
title={t("product-table-rejected", "Rejected")}
|
||||
variant={"danger"}
|
||||
/>
|
||||
)
|
||||
case "draft":
|
||||
return <StatusIndicator title={"Draft"} variant={"default"} />
|
||||
return (
|
||||
<StatusIndicator
|
||||
title={t("product-table-draft-title", "Draft")}
|
||||
variant={"default"}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <StatusIndicator title={status} variant={"default"} />
|
||||
}
|
||||
@@ -37,7 +60,7 @@ const useProductTableColumn = ({ setTileView, setListView, showList }) => {
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: "Name",
|
||||
Header: t("product-table-name", "Name"),
|
||||
accessor: "title",
|
||||
Cell: ({ row: { original } }) => {
|
||||
return (
|
||||
@@ -58,30 +81,33 @@ const useProductTableColumn = ({ setTileView, setListView, showList }) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Collection",
|
||||
Header: t("product-table-collection", "Collection"),
|
||||
accessor: "collection", // accessor is the "key" in the data
|
||||
Cell: ({ cell: { value } }) => {
|
||||
return <div>{value?.title || "-"}</div>
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Status",
|
||||
Header: t("product-table-status", "Status"),
|
||||
accessor: "status",
|
||||
Cell: ({ cell: { value } }) => getProductStatus(value),
|
||||
},
|
||||
{
|
||||
Header: "Availability",
|
||||
Header: t("product-table-availability", "Availability"),
|
||||
accessor: "sales_channels",
|
||||
Cell: ({ cell: { value } }) => getProductSalesChannels(value),
|
||||
},
|
||||
{
|
||||
Header: "Inventory",
|
||||
Header: t("product-table-inventory", "Inventory"),
|
||||
accessor: "variants",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<div>
|
||||
{value.reduce((acc, next) => acc + next.inventory_quantity, 0)}
|
||||
{" in stock for "}
|
||||
{value.length} variant(s)
|
||||
{t(
|
||||
"product-table-inventory-in-stock-count",
|
||||
" in stock for {{count}} variant(s)",
|
||||
{ count: value.length }
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
+37
-12
@@ -1,6 +1,7 @@
|
||||
import Button from "../../../../fundamentals/button"
|
||||
import { Controller } from "react-hook-form"
|
||||
import { DecoratedInventoryItemDTO } from "@medusajs/medusa"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import InputField from "../../../../molecules/input"
|
||||
import ItemSearch from "../../../../molecules/item-search"
|
||||
import LocationDropdown from "../../../../molecules/location-dropdown"
|
||||
@@ -19,6 +20,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const ReservationForm: React.FC<Props> = ({ form }) => {
|
||||
const { t } = useTranslation()
|
||||
const { register, path, watch, control, setValue } = form
|
||||
|
||||
const locationId = watch(path("location"))
|
||||
@@ -36,8 +38,15 @@ const ReservationForm: React.FC<Props> = ({ form }) => {
|
||||
<div className="flex w-full flex-col gap-6">
|
||||
<div className="grid w-full grid-cols-2 items-center">
|
||||
<div>
|
||||
<p className="inter-base-semibold mb-1">Location</p>
|
||||
<p className="text-grey-50">Choose where you wish to reserve from.</p>
|
||||
<p className="inter-base-semibold mb-1">
|
||||
{t("reservation-form-location", "Location")}
|
||||
</p>
|
||||
<p className="text-grey-50">
|
||||
{t(
|
||||
"reservation-form-choose-where-you-wish-to-reserve-from",
|
||||
"Choose where you wish to reserve from."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -57,9 +66,14 @@ const ReservationForm: React.FC<Props> = ({ form }) => {
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-2 items-center">
|
||||
<div>
|
||||
<p className="inter-base-semibold mb-1">Item to reserve</p>
|
||||
<p className="inter-base-semibold mb-1">
|
||||
{t("reservation-form-item-to-reserve", "Item to reserve")}
|
||||
</p>
|
||||
<p className="text-grey-50">
|
||||
Select the item that you wish to reserve.
|
||||
{t(
|
||||
"reservation-form-select-the-item-that-you-wish-to-reserve",
|
||||
"Select the item that you wish to reserve."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Controller
|
||||
@@ -87,20 +101,24 @@ const ReservationForm: React.FC<Props> = ({ form }) => {
|
||||
[&>*:nth-child(even)]:pr-4 [&>*:nth-child(even)]:text-right
|
||||
[&>*:nth-child(-n+2)]:border-t`}
|
||||
>
|
||||
<div className="rounded-tl-rounded">Item</div>
|
||||
<div className="rounded-tl-rounded">
|
||||
{t("reservation-form-item", "Item")}
|
||||
</div>
|
||||
<div className="rounded-tr-rounded">
|
||||
{selectedItem!.title ?? "N/A"}
|
||||
</div>
|
||||
<div>SKU</div>
|
||||
<div>{selectedItem.sku ?? "N/A"}</div>
|
||||
<div>In stock</div>
|
||||
<div>{t("reservation-form-in-stock", "In stock")}</div>
|
||||
<div>{locationLevel?.stocked_quantity}</div>
|
||||
<div>Available</div>
|
||||
<div>{t("reservation-form-available", "Available")}</div>
|
||||
<div>
|
||||
{locationLevel?.stocked_quantity -
|
||||
locationLevel?.reserved_quantity}
|
||||
</div>
|
||||
<div className="rounded-bl-rounded">Reserve</div>
|
||||
<div className="rounded-bl-rounded">
|
||||
{t("reservation-form-reserve", "Reserve")}
|
||||
</div>
|
||||
<div className="bg-grey-0 rounded-br-rounded text-grey-80 flex items-center">
|
||||
<input
|
||||
className="remove-number-spinner inter-base-regular w-full shrink border-none bg-transparent text-right font-normal outline-none outline-0"
|
||||
@@ -124,7 +142,7 @@ const ReservationForm: React.FC<Props> = ({ form }) => {
|
||||
type="button"
|
||||
onClick={() => setValue(path("item"), undefined)}
|
||||
>
|
||||
Remove item
|
||||
{t("reservation-form-remove-item", "Remove item")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,12 +150,19 @@ const ReservationForm: React.FC<Props> = ({ form }) => {
|
||||
</div>
|
||||
<div className="border-grey border-grey-20 grid w-full grid-cols-2 items-center border-t py-6">
|
||||
<div>
|
||||
<p className="inter-base-semibold mb-1">Description</p>
|
||||
<p className="text-grey-50">What type of reservation is this?</p>
|
||||
<p className="inter-base-semibold mb-1">
|
||||
{t("reservation-form-description", "Description")}
|
||||
</p>
|
||||
<p className="text-grey-50">
|
||||
{t(
|
||||
"reservation-form-what-type-of-reservation-is-this",
|
||||
"What type of reservation is this?"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<InputField
|
||||
{...register(path("description"))}
|
||||
placeholder="Description"
|
||||
placeholder={t("reservation-form-description", "Description")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useAdminStockLocations,
|
||||
useAdminStore,
|
||||
} from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import BuildingsIcon from "../../fundamentals/icons/buildings-icon"
|
||||
import Button from "../../fundamentals/button"
|
||||
@@ -137,6 +138,7 @@ const LocationDropdown = ({
|
||||
}
|
||||
|
||||
const ReservationsTable: React.FC<ReservationsTableProps> = () => {
|
||||
const { t } = useTranslation()
|
||||
const { store } = useAdminStore()
|
||||
const {
|
||||
state: createReservationState,
|
||||
@@ -277,7 +279,7 @@ const ReservationsTable: React.FC<ReservationsTableProps> = () => {
|
||||
count: count || 0,
|
||||
offset: offs,
|
||||
pageSize: offs + rows.length,
|
||||
title: "Reservations",
|
||||
title: t("reservations-table-reservations", "Reservations"),
|
||||
currentPage: pageIndex + 1,
|
||||
pageCount: pageCount,
|
||||
nextPage: handleNext,
|
||||
@@ -367,6 +369,7 @@ const ReservationRow = ({
|
||||
} & TableRowProps) => {
|
||||
const inventory = row.original
|
||||
|
||||
const { t } = useTranslation()
|
||||
const { mutate: deleteReservation } = useAdminDeleteReservation(inventory.id)
|
||||
|
||||
const [showEditReservation, setShowEditReservation] =
|
||||
@@ -376,12 +379,12 @@ const ReservationRow = ({
|
||||
const getRowActionables = () => {
|
||||
const actions = [
|
||||
{
|
||||
label: "Edit",
|
||||
label: t("reservations-table-edit", "Edit"),
|
||||
onClick: () => setShowEditReservation(row.original),
|
||||
icon: <EditIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: t("reservations-table-delete", "Delete"),
|
||||
variant: "danger",
|
||||
icon: <TrashIcon size={20} />,
|
||||
onClick: () => setShowDeleteReservation(true),
|
||||
@@ -416,9 +419,18 @@ const ReservationRow = ({
|
||||
)}
|
||||
{showDeleteReservation && (
|
||||
<DeletePrompt
|
||||
text={"Are you sure you want to remove this reservation?"}
|
||||
heading={"Remove reservation"}
|
||||
successText={"Reservation has been removed"}
|
||||
text={t(
|
||||
"reservations-table-confirm-delete",
|
||||
"Are you sure you want to remove this reservation?"
|
||||
)}
|
||||
heading={t(
|
||||
"reservations-table-remove-reservation",
|
||||
"Remove reservation"
|
||||
)}
|
||||
successText={t(
|
||||
"reservations-table-reservation-has-been-removed",
|
||||
"Reservation has been removed"
|
||||
)}
|
||||
onDelete={async () => await deleteReservation(undefined)}
|
||||
handleClose={() => setShowDeleteReservation(false)}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import MetadataForm, {
|
||||
MetadataFormType,
|
||||
getSubmittableMetadata,
|
||||
@@ -28,6 +29,7 @@ const NewReservation = ({
|
||||
onClose: () => void
|
||||
locationId?: string
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { mutateAsync: createReservation } = useAdminCreateReservation()
|
||||
const form = useForm<NewReservationFormType>({
|
||||
defaultValues: {
|
||||
@@ -51,11 +53,18 @@ const NewReservation = ({
|
||||
|
||||
createReservation(payload, {
|
||||
onSuccess: () => {
|
||||
notification("Success", "Successfully created reservation", "success")
|
||||
notification(
|
||||
t("new-success", "Success"),
|
||||
t(
|
||||
"new-successfully-created-reservation",
|
||||
"Successfully created reservation"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
onClose()
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
notification("Error", getErrorMessage(err), "error")
|
||||
notification(t("new-error", "Error"), getErrorMessage(err), "error")
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -80,10 +89,10 @@ const NewReservation = ({
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
{t("new-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button size="small" variant="primary" type="submit">
|
||||
Save reservation
|
||||
{t("new-save-reservation", "Save reservation")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,13 +100,15 @@ const NewReservation = ({
|
||||
<FocusModal.Main className="no-scrollbar flex w-full justify-center">
|
||||
<div className="medium:w-7/12 large:w-6/12 small:w-4/5 my-16 max-w-[700px]">
|
||||
<h1 className="mb-base text-grey-90 text-xlarge font-semibold">
|
||||
Reserve Item
|
||||
{t("new-reserve-item", "Reserve Item")}
|
||||
</h1>
|
||||
<div className="mt-xlarge gap-y-xlarge flex w-full pb-0.5">
|
||||
<ReservationForm form={nestedForm(form, "general")} />
|
||||
</div>
|
||||
<div className="border-grey border-grey-20 w-full items-center border-t pt-6">
|
||||
<p className="inter-base-semibold mb-2">Metadata</p>
|
||||
<p className="inter-base-semibold mb-2">
|
||||
{t("new-metadata", "Metadata")}
|
||||
</p>
|
||||
<MetadataForm form={nestedForm(form, "metadata")} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+10
-4
@@ -1,7 +1,9 @@
|
||||
import moment from "moment"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
const useReservationsTableColumns = () => {
|
||||
const { t } = useTranslation()
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -10,22 +12,26 @@ const useReservationsTableColumns = () => {
|
||||
Cell: ({ cell: { value } }) => value,
|
||||
},
|
||||
{
|
||||
Header: "Order ID",
|
||||
Header: t("reservations-table-order-id", "Order ID"),
|
||||
accessor: "line_item.order.display_id",
|
||||
Cell: ({ cell: { value } }) => value ?? "-",
|
||||
},
|
||||
{
|
||||
Header: "Description",
|
||||
Header: t("reservations-table-description", "Description"),
|
||||
accessor: "description",
|
||||
Cell: ({ cell: { value } }) => value,
|
||||
},
|
||||
{
|
||||
Header: "Created",
|
||||
Header: t("reservations-table-created", "Created"),
|
||||
accessor: "created_at",
|
||||
Cell: ({ cell: { value } }) => moment(value).format("MMM Do YYYY"),
|
||||
},
|
||||
{
|
||||
Header: () => <div className="pr-2 text-right">Quantity</div>,
|
||||
Header: () => (
|
||||
<div className="pr-2 text-right">
|
||||
{t("reservations-table-quantity", "Quantity")}
|
||||
</div>
|
||||
),
|
||||
accessor: "quantity",
|
||||
Cell: ({ cell: { value } }) => (
|
||||
<div className="w-full pr-2 text-right">{value}</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useAdminProducts,
|
||||
} from "medusa-react"
|
||||
import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useDebounce } from "../../../hooks/use-debounce"
|
||||
import Spinner from "../../atoms/spinner"
|
||||
import Input from "../../atoms/text-input"
|
||||
@@ -24,6 +25,7 @@ const getTotal = (...lists) =>
|
||||
lists.reduce((total, list = []) => total + list.length, 0)
|
||||
|
||||
const SearchModal = ({ handleClose }) => {
|
||||
const { t } = useTranslation()
|
||||
const [q, setQ] = React.useState("")
|
||||
const query = useDebounce(q, 500)
|
||||
const onChange = (e) => setQ(e.target.value)
|
||||
@@ -90,13 +92,16 @@ const SearchModal = ({ handleClose }) => {
|
||||
className="flex-1"
|
||||
onChange={onChange}
|
||||
value={q}
|
||||
placeholder="Start typing to search..."
|
||||
placeholder={t(
|
||||
"search-modal-start-typing-to-search",
|
||||
"Start typing to search..."
|
||||
)}
|
||||
{...getInputProps()}
|
||||
/>
|
||||
<Tooltip
|
||||
className="bg-grey-0"
|
||||
onClick={handleClear}
|
||||
content="Clear search"
|
||||
content={t("search-modal-clear-search", "Clear search")}
|
||||
>
|
||||
<CrossIcon className="text-grey-50 flex" />
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import ArrowDownIcon from "../../fundamentals/icons/arrow-down-icon"
|
||||
import ArrowUpIcon from "../../fundamentals/icons/arrow-up-icon"
|
||||
import DownLeftIcon from "../../fundamentals/icons/down-left"
|
||||
import PointerIcon from "../../fundamentals/icons/pointer-icon"
|
||||
|
||||
const KeyboardShortcuts = ({ ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<p {...props}>
|
||||
<span className="bg-grey-10 rounded p-1">
|
||||
<PointerIcon color="#9CA3AF" size="16px" />
|
||||
</span>
|
||||
or
|
||||
{t("search-modal-or", "or")}
|
||||
<span className="bg-grey-10 rounded p-1">
|
||||
<ArrowUpIcon color="#9CA3AF" size="16px" />
|
||||
</span>
|
||||
<span className="bg-grey-10 -ml-1 rounded p-1">
|
||||
<ArrowDownIcon color="#9CA3AF" size="16px" />
|
||||
</span>
|
||||
to navigate,
|
||||
{t("search-modal-to-navigate", "to navigate")},
|
||||
<span className="bg-grey-10 rounded p-1">
|
||||
<DownLeftIcon color="#9CA3AF" size="16px" />
|
||||
</span>
|
||||
to select, and
|
||||
{t("search-modal-to-select-and", "to select, and")}
|
||||
<span className="bg-grey-10 leading-small font-small rounded px-1.5 py-0.5 font-semibold">
|
||||
<OSCommandIcon />
|
||||
</span>
|
||||
@@ -28,7 +30,7 @@ const KeyboardShortcuts = ({ ...props }) => {
|
||||
<span className="bg-grey-10 leading-small font-small rounded px-1.5 py-0.5 font-semibold">
|
||||
K
|
||||
</span>
|
||||
to search anytime
|
||||
{t("search-modal-to-search-anytime", "to search anytime")}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import PageDescription from "../atoms/page-description"
|
||||
import Spacer from "../atoms/spacer"
|
||||
|
||||
const SettingsOverview: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div>
|
||||
<PageDescription
|
||||
title={"Settings"}
|
||||
subtitle={"Manage the settings for your Medusa store"}
|
||||
title={t("templates-settings", "Settings")}
|
||||
subtitle={t(
|
||||
"templates-manage-the-settings-for-your-medusa-store",
|
||||
"Manage the settings for your Medusa store"
|
||||
)}
|
||||
/>
|
||||
<div className="medium:grid-cols-2 gap-x-base gap-y-xsmall grid auto-cols-fr grid-cols-1">
|
||||
{children}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from "medusa-react"
|
||||
import moment from "moment"
|
||||
import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import {
|
||||
DisplayTotalAmount,
|
||||
FulfillmentStatusComponent,
|
||||
@@ -28,6 +29,7 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
order,
|
||||
onDismiss,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [customersQuery, setCustomersQuery] = React.useState<string>("")
|
||||
const debouncedCustomersQuery = useDebounce(customersQuery, 400)
|
||||
const { customers } = useAdminCustomers({
|
||||
@@ -55,7 +57,14 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
}
|
||||
|
||||
if (customer.id === order.customer_id) {
|
||||
notification("Info", "Customer is already the owner of the order", "info")
|
||||
notification(
|
||||
t("transfer-orders-modal-info", "Info"),
|
||||
t(
|
||||
"transfer-orders-modal-customer-is-already-the-owner-of-the-order",
|
||||
"Customer is already the owner of the order"
|
||||
),
|
||||
"info"
|
||||
)
|
||||
onDismiss()
|
||||
return
|
||||
}
|
||||
@@ -65,16 +74,22 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification(
|
||||
"Success",
|
||||
"Successfully transferred order to different customer",
|
||||
t("transfer-orders-modal-success", "Success"),
|
||||
t(
|
||||
"transfer-orders-modal-successfully-transferred-order-to-different-customer",
|
||||
"Successfully transferred order to different customer"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
onDismiss()
|
||||
},
|
||||
onError: () => {
|
||||
notification(
|
||||
"Error",
|
||||
"Could not transfer order to different customer",
|
||||
t("transfer-orders-modal-error", "Error"),
|
||||
t(
|
||||
"transfer-orders-modal-could-not-transfer-order-to-different-customer",
|
||||
"Could not transfer order to different customer"
|
||||
),
|
||||
"error"
|
||||
)
|
||||
},
|
||||
@@ -124,12 +139,16 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
<Modal handleClose={onDismiss}>
|
||||
<Modal.Body>
|
||||
<Modal.Header handleClose={onDismiss}>
|
||||
<h2 className="inter-xlarge-semibold">Transfer order</h2>
|
||||
<h2 className="inter-xlarge-semibold">
|
||||
{t("transfer-orders-modal-transfer-order", "Transfer order")}
|
||||
</h2>
|
||||
</Modal.Header>
|
||||
<Modal.Content>
|
||||
<div className="space-y-xlarge flex flex-col">
|
||||
<div className="space-y-xsmall">
|
||||
<h3 className="inter-base-semibold">Order</h3>
|
||||
<h3 className="inter-base-semibold">
|
||||
{t("transfer-orders-modal-order", "Order")}
|
||||
</h3>
|
||||
<div className="border-grey-20 rounded-rounded py-xsmall flex items-center justify-between border px-2.5">
|
||||
<Badge variant="default">
|
||||
<span className="text-grey-60">{`#${order.display_id}`}</span>
|
||||
@@ -147,9 +166,14 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-2">
|
||||
<div className="flex flex-col">
|
||||
<span className="inter-base-semibold">Current Owner</span>
|
||||
<span className="inter-base-semibold">
|
||||
{t("transfer-orders-modal-current-owner", "Current Owner")}
|
||||
</span>
|
||||
<span className="inter-base-regular">
|
||||
The customer currently related to this order
|
||||
{t(
|
||||
"transfer-orders-modal-the-customer-currently-related-to-this-order",
|
||||
"The customer currently related to this order"
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
@@ -174,9 +198,14 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-2">
|
||||
<div className="flex flex-col">
|
||||
<span className="inter-base-semibold">New Owner</span>
|
||||
<span className="inter-base-semibold">
|
||||
{t("transfer-orders-modal-new-owner", "New Owner")}
|
||||
</span>
|
||||
<span className="inter-base-regular">
|
||||
The customer to transfer this order to
|
||||
{t(
|
||||
"transfer-orders-modal-the-customer-to-transfer-this-order-to",
|
||||
"The customer to transfer this order to"
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
@@ -206,7 +235,7 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
className="border-grey-20 border"
|
||||
variant="ghost"
|
||||
>
|
||||
Cancel
|
||||
{t("transfer-orders-modal-cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
@@ -221,7 +250,7 @@ const TransferOrdersModal: React.FC<TransferOrdersModalProps> = ({
|
||||
}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
Confirm
|
||||
{t("transfer-orders-modal-confirm", "Confirm")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import SidebarTeamMember from "../molecules/sidebar-team-member"
|
||||
import Table from "../molecules/table"
|
||||
import DeletePrompt from "../organisms/delete-prompt"
|
||||
import EditUser from "../organisms/edit-user-modal"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
type UserListElement = {
|
||||
entity: any
|
||||
@@ -42,6 +43,7 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
const [selectedInvite, setSelectedInvite] = useState<Invite | null>(null)
|
||||
const notification = useNotification()
|
||||
const { store, isLoading } = useAdminStore()
|
||||
const { t } = useTranslation()
|
||||
|
||||
useEffect(() => {
|
||||
setElements([
|
||||
@@ -75,12 +77,12 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
color={"inherit"}
|
||||
actions={[
|
||||
{
|
||||
label: "Edit User",
|
||||
label: t("templates-edit-user", "Edit User"),
|
||||
onClick: () => setSelectedUser(user),
|
||||
icon: <EditIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Remove User",
|
||||
label: t("templates-remove-user", "Remove User"),
|
||||
variant: "danger",
|
||||
onClick: () => {
|
||||
setDeleteUser(true)
|
||||
@@ -119,14 +121,17 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
key={`invite-${index}`}
|
||||
actions={[
|
||||
{
|
||||
label: "Resend Invitation",
|
||||
label: t("templates-resend-invitation", "Resend Invitation"),
|
||||
onClick: () => {
|
||||
Medusa.invites
|
||||
.resend(invite.id)
|
||||
.then(() => {
|
||||
notification(
|
||||
"Success",
|
||||
"Invitiation link has been resent",
|
||||
t("templates-success", "Success"),
|
||||
t(
|
||||
"templates-invitiation-link-has-been-resent",
|
||||
"Invitiation link has been resent"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
})
|
||||
@@ -135,20 +140,23 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
icon: <RefreshIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Copy invite link",
|
||||
label: t("templates-copy-invite-link", "Copy invite link"),
|
||||
disabled: isLoading,
|
||||
onClick: () => {
|
||||
copy(inviteLink.replace("{invite_token}", invite.token))
|
||||
notification(
|
||||
"Success",
|
||||
"Invite link copied to clipboard",
|
||||
t("templates-success", "Success"),
|
||||
t(
|
||||
"templates-invite-link-copied-to-clipboard",
|
||||
"Invite link copied to clipboard"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
},
|
||||
icon: <ClipboardCopyIcon size={20} />,
|
||||
},
|
||||
{
|
||||
label: "Remove Invitation",
|
||||
label: t("templates-remove-invitation", "Remove Invitation"),
|
||||
variant: "danger",
|
||||
onClick: () => {
|
||||
setSelectedInvite(invite)
|
||||
@@ -166,9 +174,15 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
<Table.Cell></Table.Cell>
|
||||
<Table.Cell>
|
||||
{new Date(invite?.expires_at) < new Date() ? (
|
||||
<StatusIndicator title={"Expired"} variant={"danger"} />
|
||||
<StatusIndicator
|
||||
title={t("templates-expired", "Expired")}
|
||||
variant={"danger"}
|
||||
/>
|
||||
) : (
|
||||
<StatusIndicator title={"Pending"} variant={"success"} />
|
||||
<StatusIndicator
|
||||
title={t("templates-pending", "Pending")}
|
||||
variant={"success"}
|
||||
/>
|
||||
)}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -180,12 +194,12 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
title: "Team permissions",
|
||||
options: [
|
||||
{
|
||||
title: "All",
|
||||
title: t("templates-all", "All"),
|
||||
count: elements.length,
|
||||
onClick: () => setShownElements(elements),
|
||||
},
|
||||
{
|
||||
title: "Member",
|
||||
title: t("templates-member", "Member"),
|
||||
count: elements.filter(
|
||||
(e) => e.entityType === "user" && e.entity.role === "member"
|
||||
).length,
|
||||
@@ -197,7 +211,7 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Admin",
|
||||
title: t("templates-admin", "Admin"),
|
||||
count: elements.filter(
|
||||
(e) => e.entityType === "user" && e.entity.role === "admin"
|
||||
).length,
|
||||
@@ -209,7 +223,7 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "No team permissions",
|
||||
title: t("templates-no-team-permissions", "No team permissions"),
|
||||
count: elements.filter((e) => e.entityType === "invite").length,
|
||||
onClick: () =>
|
||||
setShownElements(elements.filter((e) => e.entityType === "invite")),
|
||||
@@ -217,21 +231,21 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
title: t("templates-status", "Status"),
|
||||
options: [
|
||||
{
|
||||
title: "All",
|
||||
title: t("templates-all", "All"),
|
||||
count: elements.length,
|
||||
onClick: () => setShownElements(elements),
|
||||
},
|
||||
{
|
||||
title: "Active",
|
||||
title: t("templates-active", "Active"),
|
||||
count: elements.filter((e) => e.entityType === "user").length,
|
||||
onClick: () =>
|
||||
setShownElements(elements.filter((e) => e.entityType === "user")),
|
||||
},
|
||||
{
|
||||
title: "Pending",
|
||||
title: t("templates-pending", "Pending"),
|
||||
count: elements.filter(
|
||||
(e) =>
|
||||
e.entityType === "invite" &&
|
||||
@@ -247,7 +261,7 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Expired",
|
||||
title: t("templates-expired", "Expired"),
|
||||
count: elements.filter(
|
||||
(e) =>
|
||||
e.entityType === "invite" &&
|
||||
@@ -287,9 +301,15 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
>
|
||||
<Table.Head>
|
||||
<Table.HeadRow>
|
||||
<Table.HeadCell className="w-72">Name</Table.HeadCell>
|
||||
<Table.HeadCell className="w-80">Email</Table.HeadCell>
|
||||
<Table.HeadCell className="w-72">Team permissions</Table.HeadCell>
|
||||
<Table.HeadCell className="w-72">
|
||||
{t("templates-name", "Name")}
|
||||
</Table.HeadCell>
|
||||
<Table.HeadCell className="w-80">
|
||||
{t("templates-email", "Email")}
|
||||
</Table.HeadCell>
|
||||
<Table.HeadCell className="w-72">
|
||||
{t("templates-team-permissions", "Team permissions")}
|
||||
</Table.HeadCell>
|
||||
<Table.HeadCell>Status</Table.HeadCell>
|
||||
</Table.HeadRow>
|
||||
</Table.Head>
|
||||
@@ -298,11 +318,18 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
{selectedUser &&
|
||||
(deleteUser ? (
|
||||
<DeletePrompt
|
||||
text={"Are you sure you want to remove this user?"}
|
||||
heading={"Remove user"}
|
||||
text={t(
|
||||
"templates-confirm-remove",
|
||||
"Are you sure you want to remove this user?"
|
||||
)}
|
||||
heading={t("templates-remove-user-heading", "Remove user")}
|
||||
onDelete={() =>
|
||||
Medusa.users.delete(selectedUser.id).then(() => {
|
||||
notification("Success", "User has been removed", "success")
|
||||
notification(
|
||||
t("templates-success", "Success"),
|
||||
t("templates-user-has-been-removed", "User has been removed"),
|
||||
"success"
|
||||
)
|
||||
triggerRefetch()
|
||||
})
|
||||
}
|
||||
@@ -317,11 +344,21 @@ const UserTable: React.FC<UserTableProps> = ({
|
||||
))}
|
||||
{selectedInvite && (
|
||||
<DeletePrompt
|
||||
text={"Are you sure you want to remove this invite?"}
|
||||
heading={"Remove invite"}
|
||||
text={t(
|
||||
"templates-confirm-remove-invite",
|
||||
"Are you sure you want to remove this invite?"
|
||||
)}
|
||||
heading={t("templates-remove-invite", "Remove invite")}
|
||||
onDelete={() =>
|
||||
Medusa.invites.delete(selectedInvite.id).then(() => {
|
||||
notification("Success", "Invitiation has been removed", "success")
|
||||
notification(
|
||||
t("templates-success", "Success"),
|
||||
t(
|
||||
"templates-invitiation-has-been-removed",
|
||||
"Invitiation has been removed"
|
||||
),
|
||||
"success"
|
||||
)
|
||||
triggerRefetch()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user