fix(medusa, admin-ui, medusa-react): Gift Card update fixes and admin UI cleanup (#3676)
* fix gc domain issues * add changeset * update changeset * more minor fixes, remove breadcrumb * more cleanup * address feedback
This commit is contained in:
1 parent
a5ad6c0542
commit
788ddc0f43
51 files changed
+997
-709
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"medusa-react": patch
|
||||||
|
"@medusajs/admin-ui": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(admin-ui, medusa-react, medusa): Minor fixes to GC domain in admin UI. Also fixes GC update payload type in medusa-react and medusa.
|
||||||
@@ -60,6 +60,7 @@ const DatePicker: React.FC<DateTimePickerProps> = ({
|
|||||||
"shadow-input border-violet-60": isOpen,
|
"shadow-input border-violet-60": isOpen,
|
||||||
"border-grey-20": !isOpen,
|
"border-grey-20": !isOpen,
|
||||||
})}
|
})}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<InputContainer className="shadown-none border-0 focus-within:shadow-none">
|
<InputContainer className="shadown-none border-0 focus-within:shadow-none">
|
||||||
<div className="text-grey-50 flex w-full justify-between pr-0.5">
|
<div className="text-grey-50 flex w-full justify-between pr-0.5">
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ const TimePicker: React.FC<DateTimePickerProps> = ({
|
|||||||
"shadow-input border-violet-60": isOpen,
|
"shadow-input border-violet-60": isOpen,
|
||||||
"border-grey-20": !isOpen,
|
"border-grey-20": !isOpen,
|
||||||
})}
|
})}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<InputContainer className="shadown-none border-0 focus-within:shadow-none">
|
<InputContainer className="shadown-none border-0 focus-within:shadow-none">
|
||||||
<div className="text-grey-50 flex w-full justify-between pr-0.5">
|
<div className="text-grey-50 flex w-full justify-between pr-0.5">
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ const NumberScroller: React.FC<NumberScrollerProps> = ({
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<button onClick={() => onSelect(n)} className="h-full w-full py-2">
|
<button
|
||||||
|
onClick={() => onSelect(n)}
|
||||||
|
className="h-full w-full py-2"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
{n.toLocaleString("en-US", { minimumIntegerDigits: 2 })}
|
{n.toLocaleString("en-US", { minimumIntegerDigits: 2 })}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Spacer that ensures a consistent bottom margin on all pages
|
||||||
|
* Temporary fix to BodyCard and main layout not having a consistent bottom margin
|
||||||
|
*/
|
||||||
|
const Spacer = () => {
|
||||||
|
return <div className="h-xlarge w-full" />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Spacer
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Controller } from "react-hook-form"
|
||||||
|
import FormValidator from "../../../../utils/form-validator"
|
||||||
|
import { NestedForm } from "../../../../utils/nested-form"
|
||||||
|
import { formatAmountWithSymbol } from "../../../../utils/prices"
|
||||||
|
import PriceFormInput from "../../general/prices-form/price-form-input"
|
||||||
|
|
||||||
|
export type GiftCardBalanceFormType = {
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GiftCardBalanceFormProps = {
|
||||||
|
form: NestedForm<GiftCardBalanceFormType>
|
||||||
|
currencyCode: string
|
||||||
|
originalAmount?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const GiftCardBalanceForm = ({
|
||||||
|
form,
|
||||||
|
currencyCode,
|
||||||
|
originalAmount,
|
||||||
|
}: GiftCardBalanceFormProps) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
path,
|
||||||
|
formState: { errors },
|
||||||
|
} = form
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
name={path("amount")}
|
||||||
|
rules={{
|
||||||
|
required: FormValidator.required("Balance"),
|
||||||
|
min: {
|
||||||
|
value: 0,
|
||||||
|
message: "Balance must be greater than 0",
|
||||||
|
},
|
||||||
|
max: originalAmount
|
||||||
|
? {
|
||||||
|
value: originalAmount,
|
||||||
|
message: `The updated balance cannot exceed the original value of ${formatAmountWithSymbol(
|
||||||
|
{
|
||||||
|
amount: originalAmount,
|
||||||
|
currency: currencyCode,
|
||||||
|
}
|
||||||
|
)}`,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
}}
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange, name } }) => {
|
||||||
|
return (
|
||||||
|
<PriceFormInput
|
||||||
|
label="Amount"
|
||||||
|
currencyCode={currencyCode}
|
||||||
|
onChange={onChange}
|
||||||
|
amount={value}
|
||||||
|
name={name}
|
||||||
|
errors={errors}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GiftCardBalanceForm
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { Controller } from "react-hook-form"
|
||||||
|
import { NestedForm } from "../../../../utils/nested-form"
|
||||||
|
import DatePicker from "../../../atoms/date-picker/date-picker"
|
||||||
|
import TimePicker from "../../../atoms/date-picker/time-picker"
|
||||||
|
import SwitchableItem from "../../../molecules/switchable-item"
|
||||||
|
|
||||||
|
export type GiftCardEndsAtFormType = {
|
||||||
|
ends_at: Date | null
|
||||||
|
}
|
||||||
|
|
||||||
|
type GiftCardEndsAtFormProps = {
|
||||||
|
form: NestedForm<GiftCardEndsAtFormType>
|
||||||
|
}
|
||||||
|
|
||||||
|
const GiftCardEndsAtForm = ({ form }: GiftCardEndsAtFormProps) => {
|
||||||
|
const { control, path } = form
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
name={path("ends_at")}
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange } }) => {
|
||||||
|
return (
|
||||||
|
<SwitchableItem
|
||||||
|
open={!!value}
|
||||||
|
onSwitch={() => {
|
||||||
|
if (value) {
|
||||||
|
onChange(null)
|
||||||
|
} else {
|
||||||
|
onChange(
|
||||||
|
new Date(new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
title="Gift Card has an expiry date?"
|
||||||
|
description="Schedule the Gift Card to deactivate in the future."
|
||||||
|
>
|
||||||
|
<div className="gap-x-xsmall flex items-center">
|
||||||
|
<DatePicker
|
||||||
|
date={value!}
|
||||||
|
label="Expiry date"
|
||||||
|
onSubmitDate={onChange}
|
||||||
|
/>
|
||||||
|
<TimePicker
|
||||||
|
label="Expiry time"
|
||||||
|
date={value!}
|
||||||
|
onSubmitDate={onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SwitchableItem>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GiftCardEndsAtForm
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import FormValidator from "../../../../utils/form-validator"
|
||||||
|
import { NestedForm } from "../../../../utils/nested-form"
|
||||||
|
import InputField from "../../../molecules/input"
|
||||||
|
import TextArea from "../../../molecules/textarea"
|
||||||
|
|
||||||
|
export type GiftCardReceiverFormType = {
|
||||||
|
email: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type GiftCardReceiverFormProps = {
|
||||||
|
form: NestedForm<GiftCardReceiverFormType>
|
||||||
|
}
|
||||||
|
|
||||||
|
const GiftCardReceiverForm = ({ form }: GiftCardReceiverFormProps) => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
path,
|
||||||
|
formState: { errors },
|
||||||
|
} = form
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="gap-y-large flex flex-col">
|
||||||
|
<InputField
|
||||||
|
label="Email"
|
||||||
|
{...register(path("email"), {
|
||||||
|
required: FormValidator.required("Email"),
|
||||||
|
pattern: FormValidator.email("Email"),
|
||||||
|
})}
|
||||||
|
required
|
||||||
|
errors={errors}
|
||||||
|
/>
|
||||||
|
<TextArea
|
||||||
|
label="Personal Message"
|
||||||
|
placeholder="Write a personal message here"
|
||||||
|
rows={7}
|
||||||
|
{...register(path("message"))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GiftCardReceiverForm
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { useAdminRegions } from "medusa-react"
|
||||||
|
import { useEffect, useMemo } from "react"
|
||||||
|
import { Controller, useWatch } from "react-hook-form"
|
||||||
|
import { Option } from "../../../../types/shared"
|
||||||
|
import FormValidator from "../../../../utils/form-validator"
|
||||||
|
import { NestedForm } from "../../../../utils/nested-form"
|
||||||
|
import { NextSelect } from "../../../molecules/select/next-select"
|
||||||
|
|
||||||
|
type RegionOption = Option & {
|
||||||
|
currency_code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GiftCardRegionFormType = {
|
||||||
|
region_id: RegionOption
|
||||||
|
}
|
||||||
|
|
||||||
|
type GiftCardRegionFormProps = {
|
||||||
|
form: NestedForm<GiftCardRegionFormType>
|
||||||
|
}
|
||||||
|
|
||||||
|
const GiftCardRegionForm = ({ form }: GiftCardRegionFormProps) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
path,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
} = form
|
||||||
|
|
||||||
|
const { regions } = useAdminRegions({
|
||||||
|
limit: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
const regionOptions: RegionOption[] = useMemo(() => {
|
||||||
|
return (
|
||||||
|
regions?.map((r) => ({
|
||||||
|
label: r.name,
|
||||||
|
value: r.id,
|
||||||
|
currency_code: r.currency_code,
|
||||||
|
})) || []
|
||||||
|
)
|
||||||
|
}, [regions])
|
||||||
|
|
||||||
|
const subscriber = useWatch({
|
||||||
|
control,
|
||||||
|
name: path("region_id"),
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!subscriber) {
|
||||||
|
setValue(path("region_id"), regionOptions[0], {
|
||||||
|
shouldDirty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [subscriber, regionOptions, setValue, path])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
name={path("region_id")}
|
||||||
|
rules={{
|
||||||
|
required: FormValidator.required("Region"),
|
||||||
|
}}
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<NextSelect
|
||||||
|
label="Region"
|
||||||
|
required
|
||||||
|
{...field}
|
||||||
|
errors={errors}
|
||||||
|
options={regionOptions}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GiftCardRegionForm
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
import React from "react"
|
import React, { PropsWithChildren } from "react"
|
||||||
import Actionables, { ActionType } from "../../molecules/actionables"
|
import Actionables, { ActionType } from "../../molecules/actionables"
|
||||||
|
|
||||||
type BannerCardProps = {
|
type BannerCardProps = PropsWithChildren<{
|
||||||
actions?: ActionType[]
|
actions?: ActionType[]
|
||||||
title: string
|
title: string
|
||||||
thumbnail: string | null
|
thumbnail?: string | null
|
||||||
} & React.RefAttributes<HTMLDivElement>
|
}> &
|
||||||
|
React.RefAttributes<HTMLDivElement>
|
||||||
|
|
||||||
type BannerCardDescriptionProps = {
|
type BannerCardDescriptionProps = PropsWithChildren<{
|
||||||
cta?: {
|
cta?: {
|
||||||
label: string
|
label: string
|
||||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
|
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||||
}
|
}
|
||||||
}
|
}>
|
||||||
|
|
||||||
const BannerCard: React.FC<BannerCardProps> & {
|
const BannerCard: React.FC<BannerCardProps> & {
|
||||||
Description: React.FC<BannerCardDescriptionProps>
|
Description: React.FC<BannerCardDescriptionProps>
|
||||||
Footer: React.FC
|
Footer: React.FC<PropsWithChildren>
|
||||||
} = ({ title, thumbnail, actions, children }) => {
|
} = ({ title, thumbnail, actions, children }) => {
|
||||||
return (
|
return (
|
||||||
<div className="rounded-rounded bg-grey-0 border-grey-20 p-base medium:p-xlarge w-full border">
|
<div className="rounded-rounded bg-grey-0 border-grey-20 p-base medium:p-xlarge w-full border">
|
||||||
@@ -63,7 +64,7 @@ const Description: React.FC<BannerCardDescriptionProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Footer: React.FC = ({ children }) => {
|
const Footer = ({ children }: PropsWithChildren) => {
|
||||||
return <div className="mt-base">{children}</div>
|
return <div className="mt-base">{children}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import clsx from "clsx"
|
|
||||||
import React from "react"
|
|
||||||
import { useNavigate } from "react-router-dom"
|
|
||||||
import ChevronRightIcon from "../../fundamentals/icons/chevron-right-icon"
|
|
||||||
|
|
||||||
type BreadcrumbProps = React.HtmlHTMLAttributes<HTMLDivElement> & {
|
|
||||||
previousRoute?: string
|
|
||||||
previousBreadcrumb?: string
|
|
||||||
currentPage: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const Breadcrumb: React.FC<BreadcrumbProps> = ({
|
|
||||||
previousRoute = "/a/settings",
|
|
||||||
previousBreadcrumb = "Settings",
|
|
||||||
currentPage,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const navigate = useNavigate()
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
"inter-small-semibold text-grey-50 mb-4 flex w-full items-center",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="text-violet-60 cursor-pointer"
|
|
||||||
onClick={() => navigate(previousRoute)}
|
|
||||||
>
|
|
||||||
{previousBreadcrumb}
|
|
||||||
</span>
|
|
||||||
<span className="mx-0.5">
|
|
||||||
<ChevronRightIcon size={16} />
|
|
||||||
</span>
|
|
||||||
<span>{currentPage}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Breadcrumb
|
|
||||||
@@ -64,7 +64,7 @@ const BodyCard: React.FC<BodyCardProps> = ({
|
|||||||
"border-grey-20 border-b border-solid": setBorders,
|
"border-grey-20 border-b border-solid": setBorders,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
{customHeader ? (
|
{customHeader ? (
|
||||||
<div>{customHeader}</div>
|
<div>{customHeader}</div>
|
||||||
@@ -112,8 +112,8 @@ const BodyCard: React.FC<BodyCardProps> = ({
|
|||||||
<Button
|
<Button
|
||||||
key={i}
|
key={i}
|
||||||
onClick={event.onClick}
|
onClick={event.onClick}
|
||||||
className="first:ml-xsmall min-w-[130px] justify-center"
|
className="first:ml-xsmall justify-center"
|
||||||
variant={i === 0 ? "primary" : "ghost"}
|
variant={i === 0 ? "primary" : "secondary"}
|
||||||
size={"small"}
|
size={"small"}
|
||||||
type={event.type}
|
type={event.type}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
|
|
||||||
|
import useNotification from "../../hooks/use-notification"
|
||||||
|
import { getErrorMessage } from "../../utils/error-messages"
|
||||||
import Button from "../fundamentals/button"
|
import Button from "../fundamentals/button"
|
||||||
import Modal from "../molecules/modal"
|
import Modal from "../molecules/modal"
|
||||||
import { getErrorMessage } from "../../utils/error-messages"
|
|
||||||
import useNotification from "../../hooks/use-notification"
|
|
||||||
|
|
||||||
type DeletePromptProps = {
|
type DeletePromptProps = {
|
||||||
heading?: string
|
heading?: string
|
||||||
@@ -54,10 +54,10 @@ const DeletePrompt: React.FC<DeletePromptProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<div className="flex h-8 w-full justify-end">
|
<div className="gap-x-xsmall flex h-8 w-full justify-end">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="secondary"
|
||||||
className="text-small min-w-24 mr-2 justify-center"
|
className="justify-center"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
>
|
>
|
||||||
@@ -66,7 +66,7 @@ const DeletePrompt: React.FC<DeletePromptProps> = ({
|
|||||||
<Button
|
<Button
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
size="small"
|
size="small"
|
||||||
className="text-small w-24 justify-center"
|
className="justify-center"
|
||||||
variant="nuclear"
|
variant="nuclear"
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useMemo } from "react"
|
import React, { useMemo } from "react"
|
||||||
import { currencies } from "../../../utils/currencies"
|
|
||||||
import { normalizeAmount } from "../../../utils/prices"
|
import { normalizeAmount } from "../../../utils/prices"
|
||||||
import EditIcon from "../../fundamentals/icons/edit-icon"
|
import EditIcon from "../../fundamentals/icons/edit-icon"
|
||||||
import TrashIcon from "../../fundamentals/icons/trash-icon"
|
import TrashIcon from "../../fundamentals/icons/trash-icon"
|
||||||
@@ -75,7 +74,7 @@ const GiftCardBanner: React.FC<GiftCardBannerProps> = ({
|
|||||||
)} ${defaultCurrency.toUpperCase()}`
|
)} ${defaultCurrency.toUpperCase()}`
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
}, [variants, defaultCurrency, currencies])
|
}, [variants, defaultCurrency])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BannerCard title={title} thumbnail={thumbnail} actions={actions}>
|
<BannerCard title={title} thumbnail={thumbnail} actions={actions}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { getErrorMessage } from "../../../utils/error-messages"
|
|||||||
import Button from "../../fundamentals/button"
|
import Button from "../../fundamentals/button"
|
||||||
import InputField from "../../molecules/input"
|
import InputField from "../../molecules/input"
|
||||||
import Modal from "../../molecules/modal"
|
import Modal from "../../molecules/modal"
|
||||||
import Select from "../../molecules/select"
|
import { NextSelect } from "../../molecules/select/next-select"
|
||||||
|
|
||||||
type InviteModalProps = {
|
type InviteModalProps = {
|
||||||
handleClose: () => void
|
handleClose: () => void
|
||||||
@@ -68,10 +68,13 @@ const InviteModal: React.FC<InviteModalProps> = ({ handleClose }) => {
|
|||||||
name="role"
|
name="role"
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue={{ label: "Member", value: "member" }}
|
defaultValue={{ label: "Member", value: "member" }}
|
||||||
render={({ field: { value, onChange } }) => {
|
render={({ field: { value, onChange, onBlur, ref } }) => {
|
||||||
return (
|
return (
|
||||||
<Select
|
<NextSelect
|
||||||
label="Role"
|
label="Role"
|
||||||
|
placeholder="Select role"
|
||||||
|
onBlur={onBlur}
|
||||||
|
ref={ref}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
options={roleOptions}
|
options={roleOptions}
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ function RawJSON(props: RawJSONProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BodyCard className={"mb-4 h-auto min-h-0 w-full"} title={title}>
|
<BodyCard className={"h-auto min-h-0 w-full"} title={title}>
|
||||||
<div className="flex flex-grow items-center">
|
<div className="flex flex-grow items-center">
|
||||||
<JSONView data={data} />
|
<JSONView data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import PageDescription from "../atoms/page-description"
|
import PageDescription from "../atoms/page-description"
|
||||||
|
import Spacer from "../atoms/spacer"
|
||||||
|
|
||||||
const SettingsOverview: React.FC<React.PropsWithChildren> = ({ children }) => {
|
const SettingsOverview: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
@@ -11,6 +12,7 @@ const SettingsOverview: React.FC<React.PropsWithChildren> = ({ children }) => {
|
|||||||
<div className="medium:grid-cols-2 gap-x-base gap-y-xsmall grid auto-cols-fr grid-cols-1">
|
<div className="medium:grid-cols-2 gap-x-base gap-y-xsmall grid auto-cols-fr grid-cols-1">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const TwoSplitPane: React.FC<TwoSplitPaneProps> = ({
|
|||||||
{Children.map(children, (child, i) => {
|
{Children.map(children, (child, i) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx("h-full w-full", {
|
className={clsx("w-full", {
|
||||||
"col-span-2": threeCols && i === 1,
|
"col-span-2": threeCols && i === 1,
|
||||||
})}
|
})}
|
||||||
key={i}
|
key={i}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ const CollectionDetails = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="!pb-xlarge flex flex-col">
|
<div className="flex flex-col">
|
||||||
<BackButton
|
<BackButton
|
||||||
className="mb-xsmall"
|
className="mb-xsmall"
|
||||||
path="/a/products?view=collections"
|
path="/a/products?view=collections"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const CustomerDetail = () => {
|
|||||||
path="/a/customers"
|
path="/a/customers"
|
||||||
className="mb-xsmall"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
<div className="gap-y-small flex flex-col">
|
<div className="gap-y-xsmall flex flex-col">
|
||||||
<Section>
|
<Section>
|
||||||
<div className="flex w-full items-start justify-between">
|
<div className="flex w-full items-start justify-between">
|
||||||
<div className="gap-x-base flex w-full items-center">
|
<div className="gap-x-base flex w-full items-center">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Route, Routes } from "react-router-dom"
|
import { Route, Routes } from "react-router-dom"
|
||||||
|
import Spacer from "../../components/atoms/spacer"
|
||||||
import BodyCard from "../../components/organisms/body-card"
|
import BodyCard from "../../components/organisms/body-card"
|
||||||
import CustomerTable from "../../components/templates/customer-table"
|
import CustomerTable from "../../components/templates/customer-table"
|
||||||
import Details from "./details"
|
import Details from "./details"
|
||||||
@@ -7,15 +8,14 @@ import CustomersPageTableHeader from "./header"
|
|||||||
|
|
||||||
const CustomerIndex = () => {
|
const CustomerIndex = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full grow flex-col">
|
<div>
|
||||||
<div className="flex w-full grow flex-col">
|
<BodyCard
|
||||||
<BodyCard
|
customHeader={<CustomersPageTableHeader activeView="customers" />}
|
||||||
customHeader={<CustomersPageTableHeader activeView="customers" />}
|
className="h-fit"
|
||||||
className="h-fit"
|
>
|
||||||
>
|
<CustomerTable />
|
||||||
<CustomerTable />
|
</BodyCard>
|
||||||
</BodyCard>
|
<Spacer />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,54 +22,56 @@ const DiscountDetailsConditions: React.FC<DiscountDetailsConditionsProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ConditionsProvider discount={discount}>
|
<ConditionsProvider discount={discount}>
|
||||||
<BodyCard
|
<div>
|
||||||
title="Conditions"
|
<BodyCard
|
||||||
className="min-h-[200px]"
|
title="Conditions"
|
||||||
forceDropdown
|
className="min-h-[200px]"
|
||||||
actionables={[
|
forceDropdown
|
||||||
{
|
actionables={[
|
||||||
label: "Add condition",
|
{
|
||||||
icon: <PlusIcon size={16} />,
|
label: "Add condition",
|
||||||
onClick: () => setShow(true),
|
icon: <PlusIcon size={16} />,
|
||||||
},
|
onClick: () => setShow(true),
|
||||||
]}
|
},
|
||||||
>
|
]}
|
||||||
{conditions.length ? (
|
>
|
||||||
<div
|
{conditions.length ? (
|
||||||
style={{
|
<div
|
||||||
gridTemplateRows: `repeat(${Math.ceil(
|
style={{
|
||||||
conditions?.length / 2
|
gridTemplateRows: `repeat(${Math.ceil(
|
||||||
)}, minmax(0, 1fr))`,
|
conditions?.length / 2
|
||||||
}}
|
)}, minmax(0, 1fr))`,
|
||||||
className="gap-y-base gap-x-xlarge grid grid-flow-col grid-cols-2"
|
}}
|
||||||
>
|
className="gap-y-base gap-x-xlarge grid grid-flow-col grid-cols-2"
|
||||||
{conditions.map((condition, i) => (
|
>
|
||||||
<NumberedItem
|
{conditions.map((condition, i) => (
|
||||||
key={i}
|
<NumberedItem
|
||||||
title={condition.title}
|
key={i}
|
||||||
index={i + 1}
|
title={condition.title}
|
||||||
description={condition.description}
|
index={i + 1}
|
||||||
actions={condition.actions}
|
description={condition.description}
|
||||||
/>
|
actions={condition.actions}
|
||||||
))}
|
/>
|
||||||
</div>
|
))}
|
||||||
) : (
|
</div>
|
||||||
<div className="gap-y-small flex flex-1 flex-col items-center justify-center">
|
) : (
|
||||||
<span className="inter-base-regular text-grey-50">
|
<div className="gap-y-small flex flex-1 flex-col items-center justify-center">
|
||||||
This discount has no conditions
|
<span className="inter-base-regular text-grey-50">
|
||||||
</span>
|
This discount has no conditions
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</BodyCard>
|
||||||
|
<AddCondition show={show} onClose={() => setShow(false)} />
|
||||||
|
{selectedCondition && (
|
||||||
|
<EditConditionsModal
|
||||||
|
open={!!selectedCondition}
|
||||||
|
condition={selectedCondition}
|
||||||
|
discount={discount}
|
||||||
|
onClose={() => deSelectCondition()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</BodyCard>
|
</div>
|
||||||
<AddCondition show={show} onClose={() => setShow(false)} />
|
|
||||||
{selectedCondition && (
|
|
||||||
<EditConditionsModal
|
|
||||||
open={!!selectedCondition}
|
|
||||||
condition={selectedCondition}
|
|
||||||
discount={discount}
|
|
||||||
onClose={() => deSelectCondition()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ConditionsProvider>
|
</ConditionsProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useAdminDeleteDiscount, useAdminDiscount } from "medusa-react"
|
import { useAdminDeleteDiscount, useAdminDiscount } from "medusa-react"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
import Spinner from "../../../components/atoms/spinner"
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import DeletePrompt from "../../../components/organisms/delete-prompt"
|
import DeletePrompt from "../../../components/organisms/delete-prompt"
|
||||||
import RawJSON from "../../../components/organisms/raw-json"
|
import RawJSON from "../../../components/organisms/raw-json"
|
||||||
import useNotification from "../../../hooks/use-notification"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
@@ -50,22 +50,22 @@ const Edit = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage="Add Discount"
|
label="Back to Discounts"
|
||||||
previousBreadcrumb="Discount"
|
path="/a/discounts"
|
||||||
previousRoute="/a/discounts"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
{isLoading || !discount ? (
|
{isLoading || !discount ? (
|
||||||
<div className="flex h-full items-center justify-center">
|
<div className="flex h-full items-center justify-center">
|
||||||
<Spinner variant="secondary" />
|
<Spinner variant="secondary" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-y-4">
|
<div className="gap-y-xsmall flex flex-col">
|
||||||
<DiscountFormProvider>
|
<DiscountFormProvider>
|
||||||
<General discount={discount} />
|
<General discount={discount} />
|
||||||
<Configurations discount={discount} />
|
<Configurations discount={discount} />
|
||||||
<DiscountDetailsConditions discount={discount} />
|
<DiscountDetailsConditions discount={discount} />
|
||||||
<RawJSON data={discount} title="Raw discount" rootName="discount" />
|
<RawJSON data={discount} title="Raw discount" />
|
||||||
</DiscountFormProvider>
|
</DiscountFormProvider>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { Route, Routes } from "react-router-dom"
|
import { Route, Routes } from "react-router-dom"
|
||||||
import Fade from "../../components/atoms/fade-wrapper"
|
import Fade from "../../components/atoms/fade-wrapper"
|
||||||
|
import Spacer from "../../components/atoms/spacer"
|
||||||
import PlusIcon from "../../components/fundamentals/icons/plus-icon"
|
import PlusIcon from "../../components/fundamentals/icons/plus-icon"
|
||||||
import BodyCard from "../../components/organisms/body-card"
|
import BodyCard from "../../components/organisms/body-card"
|
||||||
import TableViewHeader from "../../components/organisms/custom-table-header"
|
import TableViewHeader from "../../components/organisms/custom-table-header"
|
||||||
@@ -31,6 +32,7 @@ const DiscountIndex = () => {
|
|||||||
>
|
>
|
||||||
<DiscountTable />
|
<DiscountTable />
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
<DiscountFormProvider>
|
<DiscountFormProvider>
|
||||||
<Fade isVisible={isOpen} isFullScreen={true}>
|
<Fade isVisible={isOpen} isFullScreen={true}>
|
||||||
|
|||||||
@@ -1,161 +1,136 @@
|
|||||||
import { useAdminCreateGiftCard, useAdminRegions } from "medusa-react"
|
import { useAdminCreateGiftCard } from "medusa-react"
|
||||||
import React, { useEffect, useState } from "react"
|
import React, { useEffect } from "react"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm, useWatch } from "react-hook-form"
|
||||||
|
import GiftCardBalanceForm, {
|
||||||
|
GiftCardBalanceFormType,
|
||||||
|
} from "../../components/forms/gift-card/gift-card-balance-form"
|
||||||
|
import GiftCardEndsAtForm, {
|
||||||
|
GiftCardEndsAtFormType,
|
||||||
|
} from "../../components/forms/gift-card/gift-card-ends-at-form"
|
||||||
|
import GiftCardReceiverForm, {
|
||||||
|
GiftCardReceiverFormType,
|
||||||
|
} from "../../components/forms/gift-card/gift-card-receiver-form"
|
||||||
|
import GiftCardRegionForm, {
|
||||||
|
GiftCardRegionFormType,
|
||||||
|
} from "../../components/forms/gift-card/gift-card-region-form"
|
||||||
import Button from "../../components/fundamentals/button"
|
import Button from "../../components/fundamentals/button"
|
||||||
import InputField from "../../components/molecules/input"
|
|
||||||
import Modal from "../../components/molecules/modal"
|
import Modal from "../../components/molecules/modal"
|
||||||
import Select from "../../components/molecules/select"
|
|
||||||
import TextArea from "../../components/molecules/textarea"
|
|
||||||
import CurrencyInput from "../../components/organisms/currency-input"
|
|
||||||
import useNotification from "../../hooks/use-notification"
|
import useNotification from "../../hooks/use-notification"
|
||||||
import { getErrorMessage } from "../../utils/error-messages"
|
import { getErrorMessage } from "../../utils/error-messages"
|
||||||
import { focusByName } from "../../utils/focus-by-name"
|
import { nestedForm } from "../../utils/nested-form"
|
||||||
import { validateEmail } from "../../utils/validate-email"
|
|
||||||
|
|
||||||
type CustomGiftcardProps = {
|
type CustomGiftcardProps = {
|
||||||
onDismiss: () => void
|
onClose: () => void
|
||||||
|
open: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomGiftcard: React.FC<CustomGiftcardProps> = ({ onDismiss }) => {
|
type CustomGiftCardFormType = {
|
||||||
const { isLoading, regions } = useAdminRegions()
|
region: GiftCardRegionFormType
|
||||||
const [selectedRegion, setSelectedRegion] = useState<any>(null)
|
ends_at: GiftCardEndsAtFormType
|
||||||
const [giftCardAmount, setGiftCardAmount] = useState(0)
|
balance: GiftCardBalanceFormType
|
||||||
|
receiver: GiftCardReceiverFormType
|
||||||
|
}
|
||||||
|
|
||||||
const { register, handleSubmit } = useForm()
|
const CustomGiftcard: React.FC<CustomGiftcardProps> = ({ onClose, open }) => {
|
||||||
|
const form = useForm<CustomGiftCardFormType>()
|
||||||
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
control,
|
||||||
|
formState: { isDirty },
|
||||||
|
} = form
|
||||||
|
|
||||||
|
const currencySubscriber = useWatch({
|
||||||
|
control,
|
||||||
|
name: "region.region_id.currency_code",
|
||||||
|
defaultValue: "usd",
|
||||||
|
})
|
||||||
|
|
||||||
const notification = useNotification()
|
const notification = useNotification()
|
||||||
|
|
||||||
const { mutate, isLoading: isSubmitting } = useAdminCreateGiftCard()
|
const { mutate, isLoading: isSubmitting } = useAdminCreateGiftCard()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading) {
|
if (open) {
|
||||||
setSelectedRegion({
|
reset()
|
||||||
value: regions[0],
|
|
||||||
label: regions[0].name,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [isLoading])
|
}, [open, reset])
|
||||||
|
|
||||||
const onSubmit = (data) => {
|
const onSubmit = handleSubmit((data) => {
|
||||||
if (!giftCardAmount) {
|
mutate(
|
||||||
notification("Error", "Please enter an amount", "error")
|
{
|
||||||
focusByName("amount")
|
region_id: data.region.region_id.value,
|
||||||
return
|
value: data.balance.amount,
|
||||||
}
|
ends_at: data.ends_at.ends_at || undefined,
|
||||||
|
metadata: {
|
||||||
if (!validateEmail(data.metadata.email)) {
|
email: data.receiver.email,
|
||||||
notification("Error", "Invalid email address", "error")
|
personal_message: data.receiver.message,
|
||||||
focusByName("metadata.email")
|
},
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const update = {
|
|
||||||
region_id: selectedRegion.value.id,
|
|
||||||
value: Math.round(
|
|
||||||
giftCardAmount / (1 + selectedRegion.value.tax_rate / 100)
|
|
||||||
),
|
|
||||||
...data,
|
|
||||||
}
|
|
||||||
|
|
||||||
mutate(update, {
|
|
||||||
onSuccess: () => {
|
|
||||||
notification("Success", "Created Custom Gift Card", "success")
|
|
||||||
onDismiss()
|
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
{
|
||||||
notification("Error", getErrorMessage(error), "error")
|
onSuccess: () => {
|
||||||
onDismiss()
|
notification(
|
||||||
},
|
"Created gift card",
|
||||||
})
|
"Custom gift card was created successfully",
|
||||||
}
|
"success"
|
||||||
|
)
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
notification("Error", getErrorMessage(error), "error")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal handleClose={onDismiss}>
|
<Modal open={open} handleClose={onClose}>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<Modal.Header handleClose={onDismiss}>
|
<Modal.Header handleClose={onClose}>
|
||||||
<h2 className="inter-xlarge-semibold">Custom Gift Card</h2>
|
<h2 className="inter-xlarge-semibold">Custom Gift Card</h2>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Content>
|
<form onSubmit={onSubmit}>
|
||||||
<div className="flex flex-col">
|
<Modal.Content>
|
||||||
<span className="inter-base-semibold">Value</span>
|
<div className="gap-y-xlarge flex flex-col">
|
||||||
<div className="gap-x-2xsmall mt-4 flex">
|
<div>
|
||||||
<div className="w-[267px]">
|
<h2 className="inter-base-semibold mb-base">Details</h2>
|
||||||
<Select
|
<div className="gap-x-xsmall grid grid-cols-2">
|
||||||
label={"Region"}
|
<GiftCardRegionForm form={nestedForm(form, "region")} />
|
||||||
value={selectedRegion}
|
<GiftCardBalanceForm
|
||||||
onChange={(value) => setSelectedRegion(value)}
|
form={nestedForm(form, "balance")}
|
||||||
options={
|
currencyCode={currencySubscriber}
|
||||||
regions?.map((r) => ({
|
|
||||||
value: r,
|
|
||||||
label: r.name,
|
|
||||||
})) || []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-[415px]">
|
|
||||||
<CurrencyInput.Root
|
|
||||||
size="medium"
|
|
||||||
currencyCodes={
|
|
||||||
isLoading ? undefined : regions?.map((r) => r.currency_code)
|
|
||||||
}
|
|
||||||
readOnly
|
|
||||||
currentCurrency={selectedRegion?.value?.currency_code}
|
|
||||||
>
|
|
||||||
<CurrencyInput.Amount
|
|
||||||
label={"Amount"}
|
|
||||||
amount={giftCardAmount}
|
|
||||||
onChange={(value) => {
|
|
||||||
setGiftCardAmount(value || 0)
|
|
||||||
}}
|
|
||||||
name="amount"
|
|
||||||
required={true}
|
|
||||||
/>
|
/>
|
||||||
</CurrencyInput.Root>
|
</div>
|
||||||
|
</div>
|
||||||
|
<GiftCardEndsAtForm form={nestedForm(form, "ends_at")} />
|
||||||
|
<div>
|
||||||
|
<h2 className="inter-base-semibold mb-base">Receiver</h2>
|
||||||
|
<GiftCardReceiverForm form={nestedForm(form, "receiver")} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal.Content>
|
||||||
<div className="mt-8">
|
<Modal.Footer>
|
||||||
<span className="inter-base-semibold">Receiver</span>
|
<div className="gap-x-xsmall flex w-full justify-end">
|
||||||
<div className="gap-y-xsmall mt-4 grid grid-cols-1">
|
<Button
|
||||||
<InputField
|
variant="secondary"
|
||||||
label={"Email"}
|
onClick={onClose}
|
||||||
required
|
size="small"
|
||||||
{...register("metadata.email", { required: true })}
|
type="button"
|
||||||
placeholder="lebron@james.com"
|
>
|
||||||
type="email"
|
Cancel
|
||||||
/>
|
</Button>
|
||||||
<TextArea
|
<Button
|
||||||
label={"Personal Message"}
|
variant="primary"
|
||||||
rows={7}
|
type="submit"
|
||||||
placeholder="Something nice to someone special"
|
size="small"
|
||||||
{...register("metadata.personal_message")}
|
disabled={isSubmitting || !isDirty}
|
||||||
/>
|
loading={isSubmitting}
|
||||||
|
>
|
||||||
|
Create and send
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal.Footer>
|
||||||
</Modal.Content>
|
</form>
|
||||||
<Modal.Footer>
|
|
||||||
<div className="gap-x-xsmall flex w-full justify-end">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={onDismiss}
|
|
||||||
size="small"
|
|
||||||
className="w-[112px]"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
type="submit"
|
|
||||||
onClick={handleSubmit(onSubmit)}
|
|
||||||
size="small"
|
|
||||||
className="w-[112px]"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
loading={isSubmitting}
|
|
||||||
>
|
|
||||||
Create & Send
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,111 +1,137 @@
|
|||||||
import { AdminPostGiftCardsGiftCardReq, Region } from "@medusajs/medusa"
|
import { GiftCard } from "@medusajs/medusa"
|
||||||
import { useMemo } from "react"
|
import { useAdminUpdateGiftCard } from "medusa-react"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { useEffect } from "react"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import GiftCardEndsAtForm, {
|
||||||
|
GiftCardEndsAtFormType,
|
||||||
|
} from "../../../components/forms/gift-card/gift-card-ends-at-form"
|
||||||
|
import GiftCardRegionForm, {
|
||||||
|
GiftCardRegionFormType,
|
||||||
|
} from "../../../components/forms/gift-card/gift-card-region-form"
|
||||||
import Button from "../../../components/fundamentals/button"
|
import Button from "../../../components/fundamentals/button"
|
||||||
import Modal from "../../../components/molecules/modal"
|
import Modal from "../../../components/molecules/modal"
|
||||||
import Select from "../../../components/molecules/select"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
import { Option } from "../../../types/shared"
|
import { getErrorMessage } from "../../../utils/error-messages"
|
||||||
|
import { nestedForm } from "../../../utils/nested-form"
|
||||||
|
|
||||||
type EditGiftCardModalProps = {
|
type EditGiftCardModalProps = {
|
||||||
handleClose: () => void
|
onClose: () => void
|
||||||
handleSave: (update: AdminPostGiftCardsGiftCardReq) => void
|
open: boolean
|
||||||
updating: boolean
|
giftCard: GiftCard
|
||||||
regions: Region[] | undefined
|
|
||||||
region: Region
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EditGiftCardModalFormData = {
|
type EditGiftCardFormType = {
|
||||||
region: Option
|
region: GiftCardRegionFormType
|
||||||
|
ends_at: GiftCardEndsAtFormType
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditGiftCardModal = ({
|
const EditGiftCardModal = ({
|
||||||
handleClose,
|
open,
|
||||||
handleSave,
|
onClose,
|
||||||
updating,
|
giftCard,
|
||||||
regions,
|
|
||||||
region,
|
|
||||||
}: EditGiftCardModalProps) => {
|
}: EditGiftCardModalProps) => {
|
||||||
const { control, handleSubmit } = useForm<EditGiftCardModalFormData>({
|
const form = useForm<EditGiftCardFormType>({
|
||||||
defaultValues: {
|
defaultValues: getDefaultValues(giftCard),
|
||||||
region: {
|
})
|
||||||
value: region.id,
|
const {
|
||||||
label: region.name,
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
formState: { isDirty },
|
||||||
|
} = form
|
||||||
|
|
||||||
|
const { mutate, isLoading } = useAdminUpdateGiftCard(giftCard.id)
|
||||||
|
|
||||||
|
const notification = useNotification()
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((data) => {
|
||||||
|
mutate(
|
||||||
|
{
|
||||||
|
region_id: data.region.region_id.value,
|
||||||
|
ends_at: data.ends_at.ends_at,
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
notification(
|
||||||
|
"Updated Gift card",
|
||||||
|
"Gift card was succesfully updated",
|
||||||
|
"success"
|
||||||
|
)
|
||||||
|
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
notification(
|
||||||
|
"Failed to update Gift card",
|
||||||
|
getErrorMessage(err),
|
||||||
|
"error"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = (data: EditGiftCardModalFormData) => {
|
useEffect(() => {
|
||||||
handleSave({ region_id: data.region.value })
|
if (open) {
|
||||||
}
|
reset(getDefaultValues(giftCard))
|
||||||
|
}
|
||||||
const regionOptions: Option[] = useMemo(() => {
|
}, [open, reset, giftCard])
|
||||||
return (
|
|
||||||
regions?.map((r) => ({
|
|
||||||
label: r.name,
|
|
||||||
value: r.id,
|
|
||||||
})) || []
|
|
||||||
)
|
|
||||||
}, [regions])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal handleClose={handleClose} isLargeModal={true}>
|
<Modal open={open} handleClose={onClose}>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<Modal.Body>
|
||||||
<Modal.Body isLargeModal={true}>
|
<Modal.Header handleClose={onClose}>
|
||||||
<Modal.Header handleClose={handleClose}>
|
<h1 className="inter-xlarge-semibold">Edit Gift Card</h1>
|
||||||
<span className="inter-xlarge-semibold">
|
</Modal.Header>
|
||||||
Edit Gift Card Details
|
<form onSubmit={onSubmit}>
|
||||||
</span>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
{/* TODO: Missing backend support for updating code
|
<div className="gap-y-xlarge flex flex-col">
|
||||||
<InputField
|
<div>
|
||||||
label="Code"
|
<h2 className="inter-base-semibold mb-base">Details</h2>
|
||||||
name="code"
|
<GiftCardRegionForm form={nestedForm(form, "region")} />
|
||||||
value={code}
|
</div>
|
||||||
onChange={({ currentTarget }) => setCode(currentTarget.value)}
|
<GiftCardEndsAtForm form={nestedForm(form, "ends_at")} />
|
||||||
className="mb-4"
|
</div>
|
||||||
/> */}
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="region"
|
|
||||||
render={({ field: { value, onChange } }) => {
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
label="Region"
|
|
||||||
options={regionOptions}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<div className="flex w-full justify-end">
|
<div className="gap-x-xsmall flex w-full justify-end">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={handleClose}
|
onClick={onClose}
|
||||||
className="mr-2"
|
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
loading={updating}
|
|
||||||
disabled={updating}
|
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="min-w-[100px]"
|
|
||||||
size="small"
|
size="small"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
disabled={isLoading || !isDirty}
|
||||||
|
loading={isLoading}
|
||||||
>
|
>
|
||||||
Save
|
Save and close
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal.Footer>
|
</Modal.Footer>
|
||||||
</Modal.Body>
|
</form>
|
||||||
</form>
|
</Modal.Body>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDefaultValues = (giftCard: GiftCard): EditGiftCardFormType => {
|
||||||
|
return {
|
||||||
|
region: {
|
||||||
|
region_id: {
|
||||||
|
label: giftCard.region.name,
|
||||||
|
value: giftCard.region.id,
|
||||||
|
currency_code: giftCard.region.currency_code,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ends_at: {
|
||||||
|
ends_at: giftCard.ends_at,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default EditGiftCardModal
|
export default EditGiftCardModal
|
||||||
@@ -1,23 +1,15 @@
|
|||||||
import { AdminPostGiftCardsGiftCardReq } from "@medusajs/medusa"
|
import { useAdminGiftCard, useAdminUpdateGiftCard } from "medusa-react"
|
||||||
import {
|
|
||||||
useAdminGiftCard,
|
|
||||||
useAdminRegions,
|
|
||||||
useAdminUpdateGiftCard,
|
|
||||||
} from "medusa-react"
|
|
||||||
import moment from "moment"
|
import moment from "moment"
|
||||||
import { useState } from "react"
|
|
||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
import Spinner from "../../../components/atoms/spinner"
|
||||||
import Badge from "../../../components/fundamentals/badge"
|
|
||||||
import DollarSignIcon from "../../../components/fundamentals/icons/dollar-sign-icon"
|
import DollarSignIcon from "../../../components/fundamentals/icons/dollar-sign-icon"
|
||||||
import EditIcon from "../../../components/fundamentals/icons/edit-icon"
|
import EditIcon from "../../../components/fundamentals/icons/edit-icon"
|
||||||
import PublishIcon from "../../../components/fundamentals/icons/publish-icon"
|
|
||||||
import UnpublishIcon from "../../../components/fundamentals/icons/unpublish-icon"
|
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import StatusSelector from "../../../components/molecules/status-selector"
|
import StatusSelector from "../../../components/molecules/status-selector"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import RawJSON from "../../../components/organisms/raw-json"
|
import RawJSON from "../../../components/organisms/raw-json"
|
||||||
import useNotification from "../../../hooks/use-notification"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
|
import useToggleState from "../../../hooks/use-toggle-state"
|
||||||
import { getErrorMessage } from "../../../utils/error-messages"
|
import { getErrorMessage } from "../../../utils/error-messages"
|
||||||
import { formatAmountWithSymbol } from "../../../utils/prices"
|
import { formatAmountWithSymbol } from "../../../utils/prices"
|
||||||
import EditGiftCardModal from "./edit-gift-card-modal"
|
import EditGiftCardModal from "./edit-gift-card-modal"
|
||||||
@@ -29,45 +21,46 @@ const GiftCardDetails = () => {
|
|||||||
const { gift_card: giftCard, isLoading } = useAdminGiftCard(id!, {
|
const { gift_card: giftCard, isLoading } = useAdminGiftCard(id!, {
|
||||||
enabled: !!id,
|
enabled: !!id,
|
||||||
})
|
})
|
||||||
const { regions } = useAdminRegions()
|
|
||||||
|
|
||||||
const updateGiftCard = useAdminUpdateGiftCard(giftCard?.id!)
|
const updateGiftCard = useAdminUpdateGiftCard(giftCard?.id!)
|
||||||
|
|
||||||
const notification = useNotification()
|
const notification = useNotification()
|
||||||
|
|
||||||
const [showUpdateBalance, setShowUpdateBalance] = useState(false)
|
const {
|
||||||
const [showEdit, setShowEdit] = useState(false)
|
state: editState,
|
||||||
|
open: openEdit,
|
||||||
|
close: closeEdit,
|
||||||
|
} = useToggleState()
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: balanceState,
|
||||||
|
open: openBalance,
|
||||||
|
close: closeBalance,
|
||||||
|
} = useToggleState()
|
||||||
|
|
||||||
const actions = [
|
const actions = [
|
||||||
{
|
{
|
||||||
label: "Edit",
|
label: "Edit details",
|
||||||
onClick: () => setShowEdit(true),
|
onClick: openEdit,
|
||||||
icon: <EditIcon size={20} />,
|
icon: <EditIcon size={20} />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: `${giftCard?.is_disabled ? "Activate" : "Disable"}`,
|
|
||||||
onClick: () => handleUpdate({ is_disabled: !giftCard?.is_disabled }),
|
|
||||||
icon: giftCard?.is_disabled ? (
|
|
||||||
<PublishIcon size={20} />
|
|
||||||
) : (
|
|
||||||
<UnpublishIcon size={20} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: "Update balance",
|
label: "Update balance",
|
||||||
onClick: () => setShowUpdateBalance(true),
|
onClick: openBalance,
|
||||||
icon: <DollarSignIcon size={20} />,
|
icon: <DollarSignIcon size={20} />,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const handleUpdate = (data: AdminPostGiftCardsGiftCardReq) => {
|
const updateStatus = (data: { is_disabled?: boolean }) => {
|
||||||
updateGiftCard.mutate(
|
updateGiftCard.mutate(
|
||||||
{ ...data },
|
{ is_disabled: data.is_disabled },
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
notification("Success", "Succesfully updated Gift Card", "success")
|
notification(
|
||||||
setShowEdit(false)
|
"Updated status",
|
||||||
setShowUpdateBalance(false)
|
"Succesfully updated the status of the Gift Card",
|
||||||
|
"success"
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onError: (err) => notification("Error", getErrorMessage(err), "error"),
|
onError: (err) => notification("Error", getErrorMessage(err), "error"),
|
||||||
}
|
}
|
||||||
@@ -76,10 +69,10 @@ const GiftCardDetails = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage={"Gift Card Details"}
|
label="Back to Gift Cards"
|
||||||
previousBreadcrumb={"Gift Cards"}
|
path="/a/gift-cards"
|
||||||
previousRoute="/a/gift-cards"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
{isLoading || !giftCard ? (
|
{isLoading || !giftCard ? (
|
||||||
<div className="bg-grey-0 border-grey-20 rounded-rounded py-xlarge flex w-full items-center justify-center border">
|
<div className="bg-grey-0 border-grey-20 rounded-rounded py-xlarge flex w-full items-center justify-center border">
|
||||||
@@ -87,83 +80,89 @@ const GiftCardDetails = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<BodyCard
|
<div className="gap-y-xsmall flex flex-col">
|
||||||
className={"h-auto min-h-0 w-full"}
|
<BodyCard
|
||||||
title={`${giftCard?.code}`}
|
className={"h-auto min-h-0 w-full"}
|
||||||
subtitle={`Gift Card id: ${giftCard?.id}`}
|
title={`${giftCard?.code}`}
|
||||||
status={
|
status={
|
||||||
<StatusSelector
|
<StatusSelector
|
||||||
isDraft={!!giftCard?.is_disabled}
|
isDraft={!!giftCard?.is_disabled}
|
||||||
activeState={"Active"}
|
activeState={"Active"}
|
||||||
draftState={"Disable"}
|
draftState={"Disable"}
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
handleUpdate({ is_disabled: !giftCard?.is_disabled })
|
updateStatus({ is_disabled: !giftCard.is_disabled })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
actionables={actions}
|
actionables={actions}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="mt-6 flex space-x-6 divide-x">
|
<div className="flex space-x-6 divide-x">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="inter-smaller-regular text-grey-50 mb-1">
|
<div className="inter-smaller-regular text-grey-50 mb-1">
|
||||||
Original amount
|
Original amount
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{formatAmountWithSymbol({
|
||||||
|
amount: giftCard.value,
|
||||||
|
currency: giftCard.region.currency_code,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex flex-col pl-6">
|
||||||
{formatAmountWithSymbol({
|
<div className="inter-smaller-regular text-grey-50 mb-1">
|
||||||
amount: giftCard?.value,
|
Balance
|
||||||
currency: giftCard?.region.currency_code,
|
</div>
|
||||||
})}
|
<div>
|
||||||
|
{formatAmountWithSymbol({
|
||||||
|
amount: giftCard.balance,
|
||||||
|
currency: giftCard.region.currency_code,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex flex-col pl-6">
|
||||||
<div className="flex flex-col pl-6">
|
<div className="inter-smaller-regular text-grey-50 mb-1">
|
||||||
<div className="inter-smaller-regular text-grey-50 mb-1">
|
Region
|
||||||
Balance
|
</div>
|
||||||
|
<div>{giftCard.region.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{giftCard.ends_at && (
|
||||||
{formatAmountWithSymbol({
|
<div className="flex flex-col pl-6">
|
||||||
amount: giftCard?.balance,
|
<div className="inter-smaller-regular text-grey-50 mb-1">
|
||||||
currency: giftCard?.region.currency_code,
|
Expires on
|
||||||
})}
|
</div>
|
||||||
</div>
|
<div>
|
||||||
</div>
|
{moment(giftCard.ends_at).format("DD MMM YYYY")}
|
||||||
<div className="flex flex-col pl-6">
|
</div>
|
||||||
<div className="inter-smaller-regular text-grey-50 mb-1">
|
</div>
|
||||||
Created
|
)}
|
||||||
</div>
|
<div className="flex flex-col pl-6">
|
||||||
<div>
|
<div className="inter-smaller-regular text-grey-50 mb-1">
|
||||||
{moment(giftCard?.created_at).format("DD MMM YYYY")}
|
Created
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{moment(giftCard.created_at).format("DD MMM YYYY")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end">
|
</BodyCard>
|
||||||
<Badge variant="default">{giftCard?.region?.name}</Badge>
|
<RawJSON data={giftCard} title="Raw gift card" />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BodyCard>
|
|
||||||
<div className="mt-large">
|
|
||||||
<RawJSON data={giftCard} title="Raw gift card" rootName="product" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<UpdateBalanceModal
|
||||||
|
giftCard={giftCard}
|
||||||
|
onClose={closeBalance}
|
||||||
|
open={balanceState}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditGiftCardModal
|
||||||
|
onClose={closeEdit}
|
||||||
|
open={editState}
|
||||||
|
giftCard={giftCard}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{showUpdateBalance && giftCard && (
|
|
||||||
<UpdateBalanceModal
|
|
||||||
giftCard={giftCard}
|
|
||||||
currencyCode={giftCard?.region.currency_code}
|
|
||||||
handleClose={() => setShowUpdateBalance(false)}
|
|
||||||
handleSave={handleUpdate}
|
|
||||||
updating={updateGiftCard.isLoading}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{showEdit && giftCard && (
|
|
||||||
<EditGiftCardModal
|
|
||||||
handleClose={() => setShowEdit(false)}
|
|
||||||
handleSave={handleUpdate}
|
|
||||||
regions={regions}
|
|
||||||
region={giftCard.region}
|
|
||||||
updating={updateGiftCard.isLoading}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,109 +1,109 @@
|
|||||||
import { AdminPostGiftCardsGiftCardReq, GiftCard } from "@medusajs/medusa"
|
import { GiftCard } from "@medusajs/medusa"
|
||||||
import clsx from "clsx"
|
import { useAdminUpdateGiftCard } from "medusa-react"
|
||||||
import { Controller, useForm, useWatch } from "react-hook-form"
|
import { useEffect } from "react"
|
||||||
import Tooltip from "../../../components/atoms/tooltip"
|
import { useForm } from "react-hook-form"
|
||||||
|
import GiftCardBalanceForm, {
|
||||||
|
GiftCardBalanceFormType,
|
||||||
|
} from "../../../components/forms/gift-card/gift-card-balance-form"
|
||||||
import Button from "../../../components/fundamentals/button"
|
import Button from "../../../components/fundamentals/button"
|
||||||
import Modal from "../../../components/molecules/modal"
|
import Modal from "../../../components/molecules/modal"
|
||||||
import CurrencyInput from "../../../components/organisms/currency-input"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
|
import { getErrorMessage } from "../../../utils/error-messages"
|
||||||
|
import { nestedForm } from "../../../utils/nested-form"
|
||||||
|
|
||||||
type UpdateBalanceModalProps = {
|
type UpdateBalanceModalProps = {
|
||||||
handleClose: () => void
|
open: boolean
|
||||||
handleSave: (update: AdminPostGiftCardsGiftCardReq) => void
|
onClose: () => void
|
||||||
currencyCode: string
|
|
||||||
giftCard: GiftCard
|
giftCard: GiftCard
|
||||||
updating: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateBalanceModalFormData = {
|
type UpdateBalanceModalFormData = {
|
||||||
balance: number
|
balance: GiftCardBalanceFormType
|
||||||
}
|
}
|
||||||
|
|
||||||
const UpdateBalanceModal = ({
|
const UpdateBalanceModal = ({
|
||||||
handleClose,
|
open,
|
||||||
handleSave,
|
onClose,
|
||||||
currencyCode,
|
|
||||||
giftCard,
|
giftCard,
|
||||||
updating,
|
|
||||||
}: UpdateBalanceModalProps) => {
|
}: UpdateBalanceModalProps) => {
|
||||||
const { control, handleSubmit } = useForm<UpdateBalanceModalFormData>({
|
const form = useForm<UpdateBalanceModalFormData>({
|
||||||
defaultValues: {
|
defaultValues: getDefaultValues(giftCard),
|
||||||
balance: giftCard.balance,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const balance = useWatch({
|
const {
|
||||||
control,
|
handleSubmit,
|
||||||
name: "balance",
|
reset,
|
||||||
|
formState: { isDirty },
|
||||||
|
} = form
|
||||||
|
|
||||||
|
const { mutate, isLoading } = useAdminUpdateGiftCard(giftCard.id)
|
||||||
|
|
||||||
|
const notification = useNotification()
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((data) => {
|
||||||
|
mutate(
|
||||||
|
{
|
||||||
|
balance: data.balance.amount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
notification(
|
||||||
|
"Balance updated",
|
||||||
|
"Gift card balance was updated",
|
||||||
|
"success"
|
||||||
|
)
|
||||||
|
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
notification(
|
||||||
|
"Failed to update balance",
|
||||||
|
getErrorMessage(err),
|
||||||
|
"error"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
reset(getDefaultValues(giftCard))
|
||||||
|
}
|
||||||
|
}, [open, reset, giftCard])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal handleClose={handleClose}>
|
<Modal open={open} handleClose={onClose}>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<form onSubmit={handleSubmit(handleSave)}>
|
<Modal.Header handleClose={onClose}>
|
||||||
<Modal.Header handleClose={handleClose}>
|
<h1 className="inter-xlarge-semibold">Update Balance</h1>
|
||||||
<span className="inter-xlarge-semibold">Update Balance</span>
|
</Modal.Header>
|
||||||
<span
|
<form onSubmit={onSubmit}>
|
||||||
className={clsx(
|
|
||||||
"inter-small-regular mt-2xsmall transition-display text-rose-50 delay-75",
|
|
||||||
{
|
|
||||||
hidden: !(balance > giftCard.value),
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Balance can't be updated to a value that is greater than the
|
|
||||||
original amount
|
|
||||||
</span>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
<CurrencyInput.Root
|
<GiftCardBalanceForm
|
||||||
readOnly
|
form={nestedForm(form, "balance")}
|
||||||
currentCurrency={currencyCode}
|
currencyCode={giftCard.region.currency_code}
|
||||||
size="small"
|
originalAmount={giftCard.value}
|
||||||
>
|
/>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="balance"
|
|
||||||
rules={{
|
|
||||||
required: true,
|
|
||||||
}}
|
|
||||||
render={({ field: { value, onChange } }) => {
|
|
||||||
return (
|
|
||||||
<CurrencyInput.Amount
|
|
||||||
amount={value}
|
|
||||||
label="Price"
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CurrencyInput.Root>
|
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<div className="flex w-full justify-end">
|
<div className="gap-x-xsmall flex w-full justify-end">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={handleClose}
|
onClick={onClose}
|
||||||
className="mr-2"
|
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
loading={updating}
|
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="min-w-[100px]"
|
|
||||||
size="small"
|
size="small"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={balance > giftCard.value || updating}
|
loading={isLoading}
|
||||||
|
disabled={isLoading || !isDirty}
|
||||||
>
|
>
|
||||||
{balance > giftCard.value ? (
|
Save and close
|
||||||
<Tooltip content="Balance is above original value">
|
|
||||||
Save
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
"Save"
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal.Footer>
|
</Modal.Footer>
|
||||||
@@ -112,4 +112,13 @@ const UpdateBalanceModal = ({
|
|||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDefaultValues = (giftCard: GiftCard): UpdateBalanceModalFormData => {
|
||||||
|
return {
|
||||||
|
balance: {
|
||||||
|
amount: giftCard.balance,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default UpdateBalanceModal
|
export default UpdateBalanceModal
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Product } from "@medusajs/medusa"
|
||||||
import {
|
import {
|
||||||
useAdminDeleteProduct,
|
useAdminDeleteProduct,
|
||||||
useAdminProducts,
|
useAdminProducts,
|
||||||
@@ -7,6 +8,7 @@ import {
|
|||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
import { useNavigate } from "react-router-dom"
|
import { useNavigate } from "react-router-dom"
|
||||||
import PageDescription from "../../components/atoms/page-description"
|
import PageDescription from "../../components/atoms/page-description"
|
||||||
|
import Spacer from "../../components/atoms/spacer"
|
||||||
import Spinner from "../../components/atoms/spinner"
|
import Spinner from "../../components/atoms/spinner"
|
||||||
import PlusIcon from "../../components/fundamentals/icons/plus-icon"
|
import PlusIcon from "../../components/fundamentals/icons/plus-icon"
|
||||||
import BannerCard from "../../components/molecules/banner-card"
|
import BannerCard from "../../components/molecules/banner-card"
|
||||||
@@ -15,6 +17,7 @@ import DeletePrompt from "../../components/organisms/delete-prompt"
|
|||||||
import GiftCardBanner from "../../components/organisms/gift-card-banner"
|
import GiftCardBanner from "../../components/organisms/gift-card-banner"
|
||||||
import GiftCardTable from "../../components/templates/gift-card-table"
|
import GiftCardTable from "../../components/templates/gift-card-table"
|
||||||
import useNotification from "../../hooks/use-notification"
|
import useNotification from "../../hooks/use-notification"
|
||||||
|
import useToggleState from "../../hooks/use-toggle-state"
|
||||||
import { ProductStatus } from "../../types/shared"
|
import { ProductStatus } from "../../types/shared"
|
||||||
import { getErrorMessage } from "../../utils/error-messages"
|
import { getErrorMessage } from "../../utils/error-messages"
|
||||||
import CustomGiftcard from "./custom-giftcard"
|
import CustomGiftcard from "./custom-giftcard"
|
||||||
@@ -26,8 +29,6 @@ const Overview = () => {
|
|||||||
})
|
})
|
||||||
const { store } = useAdminStore()
|
const { store } = useAdminStore()
|
||||||
const [showCreate, setShowCreate] = useState(false)
|
const [showCreate, setShowCreate] = useState(false)
|
||||||
const [showCreateCustom, setShowCreateCustom] = useState(false)
|
|
||||||
const [showDelete, setShowDelete] = useState(false)
|
|
||||||
|
|
||||||
const giftCard = products?.[0]
|
const giftCard = products?.[0]
|
||||||
|
|
||||||
@@ -36,6 +37,20 @@ const Overview = () => {
|
|||||||
const updateGiftCard = useAdminUpdateProduct(giftCard?.id!)
|
const updateGiftCard = useAdminUpdateProduct(giftCard?.id!)
|
||||||
const deleteGiftCard = useAdminDeleteProduct(giftCard?.id!)
|
const deleteGiftCard = useAdminDeleteProduct(giftCard?.id!)
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: stateCustom,
|
||||||
|
close: closeCustom,
|
||||||
|
open: openCustom,
|
||||||
|
} = useToggleState()
|
||||||
|
|
||||||
|
const { state: stateNew, close: closeNew, open: openNew } = useToggleState()
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: stateDelete,
|
||||||
|
close: closeDelete,
|
||||||
|
open: openDelete,
|
||||||
|
} = useToggleState()
|
||||||
|
|
||||||
const onUpdate = () => {
|
const onUpdate = () => {
|
||||||
let status: ProductStatus = ProductStatus.PUBLISHED
|
let status: ProductStatus = ProductStatus.PUBLISHED
|
||||||
if (giftCard?.status === "published") {
|
if (giftCard?.status === "published") {
|
||||||
@@ -43,7 +58,6 @@ const Overview = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateGiftCard.mutate(
|
updateGiftCard.mutate(
|
||||||
// @ts-ignore
|
|
||||||
{ status },
|
{ status },
|
||||||
{
|
{
|
||||||
onSuccess: () =>
|
onSuccess: () =>
|
||||||
@@ -64,7 +78,7 @@ const Overview = () => {
|
|||||||
const actionables = [
|
const actionables = [
|
||||||
{
|
{
|
||||||
label: "Custom Gift Card",
|
label: "Custom Gift Card",
|
||||||
onClick: () => setShowCreateCustom(true),
|
onClick: openCustom,
|
||||||
icon: <PlusIcon size={20} />,
|
icon: <PlusIcon size={20} />,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -74,69 +88,70 @@ const Overview = () => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...giftCard, defaultCurrency: store.default_currency_code }
|
return {
|
||||||
|
...(giftCard as Product),
|
||||||
|
defaultCurrency: store.default_currency_code,
|
||||||
|
}
|
||||||
}, [giftCard, store])
|
}, [giftCard, store])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="pb-xlarge flex h-full grow flex-col">
|
<div className="flex flex-col">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
title="Gift Cards"
|
title="Gift Cards"
|
||||||
subtitle="Manage the Gift Cards of your Medusa store"
|
subtitle="Manage the Gift Cards of your Medusa store"
|
||||||
/>
|
/>
|
||||||
{!isLoading ? (
|
{!isLoading ? (
|
||||||
<>
|
<div className="gap-y-xsmall flex flex-col">
|
||||||
<div className="mb-base">
|
{giftCardWithCurrency ? (
|
||||||
{giftCardWithCurrency ? (
|
<GiftCardBanner
|
||||||
<GiftCardBanner
|
{...giftCardWithCurrency}
|
||||||
{...giftCardWithCurrency}
|
onDelete={openDelete}
|
||||||
onDelete={() => setShowDelete(true)}
|
onEdit={() => navigate("/a/gift-cards/manage")}
|
||||||
onEdit={() => navigate("/a/gift-cards/manage")}
|
onUnpublish={onUpdate}
|
||||||
onUnpublish={onUpdate}
|
/>
|
||||||
/>
|
) : (
|
||||||
) : (
|
<BannerCard title="Are you ready to sell your first Gift Card?">
|
||||||
<BannerCard title="Are you ready to sell your first Gift Card?">
|
<BannerCard.Description
|
||||||
<BannerCard.Description
|
cta={{
|
||||||
cta={{
|
label: "Create Gift Card",
|
||||||
label: "Create Gift Card",
|
onClick: () => setShowCreate(true),
|
||||||
onClick: () => setShowCreate(true),
|
}}
|
||||||
}}
|
>
|
||||||
>
|
No Gift Card has been added yet.
|
||||||
No Gift Card has been added yet.
|
</BannerCard.Description>
|
||||||
</BannerCard.Description>
|
</BannerCard>
|
||||||
</BannerCard>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
<BodyCard
|
||||||
<div className="flex w-full grow flex-col">
|
title="History"
|
||||||
<BodyCard
|
subtitle="See the history of purchased Gift Cards"
|
||||||
title="History"
|
actionables={actionables}
|
||||||
subtitle="See the history of purchased Gift Cards"
|
>
|
||||||
actionables={actionables}
|
<GiftCardTable />
|
||||||
className="h-fit"
|
</BodyCard>
|
||||||
>
|
</div>
|
||||||
<GiftCardTable />
|
|
||||||
</BodyCard>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-rounded border-grey-20 flex h-44 w-full items-center justify-center border">
|
<div className="rounded-rounded border-grey-20 flex h-44 w-full items-center justify-center border">
|
||||||
<Spinner variant="secondary" size="large" />
|
<Spinner variant="secondary" size="large" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{showCreateCustom && (
|
|
||||||
<CustomGiftcard onDismiss={() => setShowCreateCustom(false)} />
|
<CustomGiftcard onClose={closeCustom} open={stateCustom} />
|
||||||
)}
|
|
||||||
{showCreate && <NewGiftCard onClose={() => setShowCreate(!showCreate)} />}
|
{showCreate && <NewGiftCard onClose={() => setShowCreate(!showCreate)} />}
|
||||||
{showDelete && (
|
|
||||||
|
{stateDelete && (
|
||||||
<DeletePrompt
|
<DeletePrompt
|
||||||
handleClose={() => setShowDelete(!showDelete)}
|
handleClose={closeDelete}
|
||||||
onDelete={async () => onDelete()}
|
onDelete={async () => onDelete()}
|
||||||
successText="Successfully deleted Gift Card"
|
successText="Successfully deleted Gift Card"
|
||||||
confirmText="Yes, delete"
|
confirmText="Yes, delete"
|
||||||
heading="Delete Gift Card"
|
heading="Delete Gift Card"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<Spacer />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Spacer from "../../../components/atoms/spacer"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import InventoryTable from "../../../components/templates/inventory-table"
|
import InventoryTable from "../../../components/templates/inventory-table"
|
||||||
import InventoryPageTableHeader from "../header"
|
import InventoryPageTableHeader from "../header"
|
||||||
@@ -12,6 +13,7 @@ const InventoryView = () => {
|
|||||||
>
|
>
|
||||||
<InventoryTable />
|
<InventoryTable />
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import moment from "moment"
|
|||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { useHotkeys } from "react-hotkeys-hook"
|
import { useHotkeys } from "react-hotkeys-hook"
|
||||||
import Avatar from "../../../components/atoms/avatar"
|
import Avatar from "../../../components/atoms/avatar"
|
||||||
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
import Spinner from "../../../components/atoms/spinner"
|
||||||
import Tooltip from "../../../components/atoms/tooltip"
|
import Tooltip from "../../../components/atoms/tooltip"
|
||||||
import Button from "../../../components/fundamentals/button"
|
import Button from "../../../components/fundamentals/button"
|
||||||
@@ -42,7 +43,6 @@ import MailIcon from "../../../components/fundamentals/icons/mail-icon"
|
|||||||
import RefreshIcon from "../../../components/fundamentals/icons/refresh-icon"
|
import RefreshIcon from "../../../components/fundamentals/icons/refresh-icon"
|
||||||
import TruckIcon from "../../../components/fundamentals/icons/truck-icon"
|
import TruckIcon from "../../../components/fundamentals/icons/truck-icon"
|
||||||
import { ActionType } from "../../../components/molecules/actionables"
|
import { ActionType } from "../../../components/molecules/actionables"
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import JSONView from "../../../components/molecules/json-view"
|
import JSONView from "../../../components/molecules/json-view"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import RawJSON from "../../../components/organisms/raw-json"
|
import RawJSON from "../../../components/organisms/raw-json"
|
||||||
@@ -285,10 +285,10 @@ const OrderDetails = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<OrderEditProvider orderId={id!}>
|
<OrderEditProvider orderId={id!}>
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage={"Order Details"}
|
path="/a/orders"
|
||||||
previousBreadcrumb={"Orders"}
|
label="Back to Orders"
|
||||||
previousRoute="/a/orders"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
{isLoading || !order ? (
|
{isLoading || !order ? (
|
||||||
<BodyCard className="pt-2xlarge flex w-full items-center justify-center">
|
<BodyCard className="pt-2xlarge flex w-full items-center justify-center">
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import moment from "moment"
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useNavigate, useParams } from "react-router-dom"
|
import { useNavigate, useParams } from "react-router-dom"
|
||||||
import Avatar from "../../../components/atoms/avatar"
|
import Avatar from "../../../components/atoms/avatar"
|
||||||
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import CopyToClipboard from "../../../components/atoms/copy-to-clipboard"
|
import CopyToClipboard from "../../../components/atoms/copy-to-clipboard"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
import Spinner from "../../../components/atoms/spinner"
|
||||||
import Badge from "../../../components/fundamentals/badge"
|
import Badge from "../../../components/fundamentals/badge"
|
||||||
@@ -19,7 +20,6 @@ import DollarSignIcon from "../../../components/fundamentals/icons/dollar-sign-i
|
|||||||
import TruckIcon from "../../../components/fundamentals/icons/truck-icon"
|
import TruckIcon from "../../../components/fundamentals/icons/truck-icon"
|
||||||
import ImagePlaceholder from "../../../components/fundamentals/image-placeholder"
|
import ImagePlaceholder from "../../../components/fundamentals/image-placeholder"
|
||||||
import StatusDot from "../../../components/fundamentals/status-indicator"
|
import StatusDot from "../../../components/fundamentals/status-indicator"
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import JSONView from "../../../components/molecules/json-view"
|
import JSONView from "../../../components/molecules/json-view"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import ConfirmationPrompt from "../../../components/organisms/confirmation-prompt"
|
import ConfirmationPrompt from "../../../components/organisms/confirmation-prompt"
|
||||||
@@ -125,10 +125,10 @@ const DraftOrderDetails = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage={"Draft Order Details"}
|
path="/a/draft-orders"
|
||||||
previousBreadcrumb={"Draft Orders"}
|
label="Back to Draft Orders"
|
||||||
previousRoute="/a/draft-orders"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
{isLoading || !draft_order ? (
|
{isLoading || !draft_order ? (
|
||||||
<BodyCard className="pt-2xlarge flex w-full items-center justify-center">
|
<BodyCard className="pt-2xlarge flex w-full items-center justify-center">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
import { Route, Routes, useNavigate } from "react-router-dom"
|
import { Route, Routes, useNavigate } from "react-router-dom"
|
||||||
|
import Spacer from "../../../components/atoms/spacer"
|
||||||
|
|
||||||
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
@@ -47,6 +48,7 @@ const DraftOrderIndex = () => {
|
|||||||
>
|
>
|
||||||
<DraftOrderTable />
|
<DraftOrderTable />
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
{showNewOrder && (
|
{showNewOrder && (
|
||||||
<NewOrderFormProvider>
|
<NewOrderFormProvider>
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import { Route, Routes, useNavigate } from "react-router-dom"
|
|
||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
|
import { Route, Routes, useNavigate } from "react-router-dom"
|
||||||
|
|
||||||
import BodyCard from "../../components/organisms/body-card"
|
import { useAdminCreateBatchJob } from "medusa-react"
|
||||||
|
import Spacer from "../../components/atoms/spacer"
|
||||||
import Button from "../../components/fundamentals/button"
|
import Button from "../../components/fundamentals/button"
|
||||||
import Details from "./details"
|
|
||||||
import ExportIcon from "../../components/fundamentals/icons/export-icon"
|
import ExportIcon from "../../components/fundamentals/icons/export-icon"
|
||||||
|
import BodyCard from "../../components/organisms/body-card"
|
||||||
|
import TableViewHeader from "../../components/organisms/custom-table-header"
|
||||||
import ExportModal from "../../components/organisms/export-modal"
|
import ExportModal from "../../components/organisms/export-modal"
|
||||||
import OrderTable from "../../components/templates/order-table"
|
import OrderTable from "../../components/templates/order-table"
|
||||||
import TableViewHeader from "../../components/organisms/custom-table-header"
|
|
||||||
import { getErrorMessage } from "../../utils/error-messages"
|
|
||||||
import { transformFiltersAsExportContext } from "./utils"
|
|
||||||
import { useAdminCreateBatchJob } from "medusa-react"
|
|
||||||
import useNotification from "../../hooks/use-notification"
|
import useNotification from "../../hooks/use-notification"
|
||||||
import { usePolling } from "../../providers/polling-provider"
|
|
||||||
import useToggleState from "../../hooks/use-toggle-state"
|
import useToggleState from "../../hooks/use-toggle-state"
|
||||||
|
import { usePolling } from "../../providers/polling-provider"
|
||||||
|
import { getErrorMessage } from "../../utils/error-messages"
|
||||||
|
import Details from "./details"
|
||||||
|
import { transformFiltersAsExportContext } from "./utils"
|
||||||
|
|
||||||
const VIEWS = ["orders", "drafts"]
|
const VIEWS = ["orders", "drafts"]
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ const OrderIndex = () => {
|
|||||||
const actions = useMemo(() => {
|
const actions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
<Button
|
<Button
|
||||||
|
key="export"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => openExportModal()}
|
onClick={() => openExportModal()}
|
||||||
@@ -90,6 +92,7 @@ const OrderIndex = () => {
|
|||||||
>
|
>
|
||||||
<OrderTable setContextFilters={setContextFilters} />
|
<OrderTable setContextFilters={setContextFilters} />
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{exportModalOpen && (
|
{exportModalOpen && (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useAdminPriceList } from "medusa-react"
|
import { useAdminPriceList } from "medusa-react"
|
||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import RawJSON from "../../../components/organisms/raw-json"
|
import RawJSON from "../../../components/organisms/raw-json"
|
||||||
import { mapPriceListToFormValues } from "../pricing-form/form/mappers"
|
import { mapPriceListToFormValues } from "../pricing-form/form/mappers"
|
||||||
import { PriceListFormProvider } from "../pricing-form/form/pricing-form-context"
|
import { PriceListFormProvider } from "../pricing-form/form/pricing-form-context"
|
||||||
@@ -13,25 +13,21 @@ const PricingDetails = () => {
|
|||||||
const { price_list, isLoading } = useAdminPriceList(id!)
|
const { price_list, isLoading } = useAdminPriceList(id!)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-xlarge">
|
<div className="pb-large">
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage="Edit price list"
|
label="Back to Pricing"
|
||||||
previousBreadcrumb="Pricing"
|
path="/a/pricing"
|
||||||
previousRoute="/a/pricing"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isLoading && price_list ? (
|
{!isLoading && price_list ? (
|
||||||
<PriceListFormProvider priceList={mapPriceListToFormValues(price_list)}>
|
<PriceListFormProvider priceList={mapPriceListToFormValues(price_list)}>
|
||||||
<Header priceList={price_list} />
|
<div className="gap-y-xsmall flex flex-col">
|
||||||
<div className="mt-4 w-full">
|
<Header priceList={price_list} />
|
||||||
|
|
||||||
<PricesDetails id={price_list?.id} />
|
<PricesDetails id={price_list?.id} />
|
||||||
</div>
|
|
||||||
<div className="mt-xlarge">
|
<RawJSON data={price_list} title="Raw price list" />
|
||||||
<RawJSON
|
|
||||||
data={price_list}
|
|
||||||
title="Raw price list"
|
|
||||||
rootName="price_list"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</PriceListFormProvider>
|
</PriceListFormProvider>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Route, Routes, useNavigate } from "react-router-dom"
|
import { Route, Routes, useNavigate } from "react-router-dom"
|
||||||
|
import Spacer from "../../components/atoms/spacer"
|
||||||
import PlusIcon from "../../components/fundamentals/icons/plus-icon"
|
import PlusIcon from "../../components/fundamentals/icons/plus-icon"
|
||||||
import BodyCard from "../../components/organisms/body-card"
|
import BodyCard from "../../components/organisms/body-card"
|
||||||
import TableViewHeader from "../../components/organisms/custom-table-header"
|
import TableViewHeader from "../../components/organisms/custom-table-header"
|
||||||
@@ -27,6 +28,7 @@ const PricingIndex = () => {
|
|||||||
>
|
>
|
||||||
<PricingTable />
|
<PricingTable />
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createContext, useState } from "react"
|
|||||||
import { ProductCategory } from "@medusajs/medusa"
|
import { ProductCategory } from "@medusajs/medusa"
|
||||||
import { useAdminProductCategories } from "medusa-react"
|
import { useAdminProductCategories } from "medusa-react"
|
||||||
|
|
||||||
|
import Spacer from "../../../components/atoms/spacer"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import useToggleState from "../../../hooks/use-toggle-state"
|
import useToggleState from "../../../hooks/use-toggle-state"
|
||||||
import ProductCategoriesList from "../components/product-categories-list"
|
import ProductCategoriesList from "../components/product-categories-list"
|
||||||
@@ -99,6 +100,7 @@ function ProductCategoryPage() {
|
|||||||
<ProductCategoriesList categories={categories!} />
|
<ProductCategoriesList categories={categories!} />
|
||||||
)}
|
)}
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
{isCreateModalVisible && (
|
{isCreateModalVisible && (
|
||||||
<CreateProductCategory
|
<CreateProductCategory
|
||||||
parentCategory={activeCategory}
|
parentCategory={activeCategory}
|
||||||
|
|||||||
@@ -1,20 +1,30 @@
|
|||||||
import { AdminPostProductsReq, ProductVariant } from "@medusajs/medusa"
|
import { AdminPostProductsReq, ProductVariant } from "@medusajs/medusa"
|
||||||
import { useAdminCreateProduct, useMedusa } from "medusa-react"
|
import { useAdminCreateProduct, useMedusa } from "medusa-react"
|
||||||
import { useForm, useWatch } from "react-hook-form"
|
import { useForm, useWatch } from "react-hook-form"
|
||||||
import CustomsForm, { CustomsFormType } from "../../../components/forms/product/customs-form"
|
import CustomsForm, {
|
||||||
|
CustomsFormType,
|
||||||
|
} from "../../../components/forms/product/customs-form"
|
||||||
import DimensionsForm, {
|
import DimensionsForm, {
|
||||||
DimensionsFormType
|
DimensionsFormType,
|
||||||
} from "../../../components/forms/product/dimensions-form"
|
} from "../../../components/forms/product/dimensions-form"
|
||||||
import DiscountableForm, {
|
import DiscountableForm, {
|
||||||
DiscountableFormType
|
DiscountableFormType,
|
||||||
} from "../../../components/forms/product/discountable-form"
|
} from "../../../components/forms/product/discountable-form"
|
||||||
import GeneralForm, { GeneralFormType } from "../../../components/forms/product/general-form"
|
import GeneralForm, {
|
||||||
import MediaForm, { MediaFormType } from "../../../components/forms/product/media-form"
|
GeneralFormType,
|
||||||
import OrganizeForm, { OrganizeFormType } from "../../../components/forms/product/organize-form"
|
} from "../../../components/forms/product/general-form"
|
||||||
import ThumbnailForm, { ThumbnailFormType } from "../../../components/forms/product/thumbnail-form"
|
import MediaForm, {
|
||||||
|
MediaFormType,
|
||||||
|
} from "../../../components/forms/product/media-form"
|
||||||
|
import OrganizeForm, {
|
||||||
|
OrganizeFormType,
|
||||||
|
} from "../../../components/forms/product/organize-form"
|
||||||
|
import ThumbnailForm, {
|
||||||
|
ThumbnailFormType,
|
||||||
|
} from "../../../components/forms/product/thumbnail-form"
|
||||||
import { FormImage, ProductStatus } from "../../../types/shared"
|
import { FormImage, ProductStatus } from "../../../types/shared"
|
||||||
import AddSalesChannelsForm, {
|
import AddSalesChannelsForm, {
|
||||||
AddSalesChannelsFormType
|
AddSalesChannelsFormType,
|
||||||
} from "./add-sales-channels"
|
} from "./add-sales-channels"
|
||||||
import AddVariantsForm, { AddVariantsFormType } from "./add-variants"
|
import AddVariantsForm, { AddVariantsFormType } from "./add-variants"
|
||||||
|
|
||||||
@@ -247,8 +257,8 @@ const NewProduct = ({ onClose }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FocusModal.Header>
|
</FocusModal.Header>
|
||||||
<FocusModal.Main className="no-scrollbar flex w-full justify-center">
|
<FocusModal.Main className="no-scrollbar flex w-full justify-center py-16">
|
||||||
<div className="small:w-4/5 medium:w-7/12 large:w-6/12 my-16 max-w-[700px]">
|
<div className="small:w-4/5 medium:w-7/12 large:w-6/12 max-w-[700px]">
|
||||||
<Accordion defaultValue={["general"]} type="multiple">
|
<Accordion defaultValue={["general"]} type="multiple">
|
||||||
<Accordion.Item
|
<Accordion.Item
|
||||||
value={"general"}
|
value={"general"}
|
||||||
@@ -357,14 +367,14 @@ const createPayload = (
|
|||||||
mid_code: data.customs.mid_code || undefined,
|
mid_code: data.customs.mid_code || undefined,
|
||||||
type: data.organize.type
|
type: data.organize.type
|
||||||
? {
|
? {
|
||||||
value: data.organize.type.label,
|
value: data.organize.type.label,
|
||||||
id: data.organize.type.value,
|
id: data.organize.type.value,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
tags: data.organize.tags
|
tags: data.organize.tags
|
||||||
? data.organize.tags.map((t) => ({
|
? data.organize.tags.map((t) => ({
|
||||||
value: t,
|
value: t,
|
||||||
}))
|
}))
|
||||||
: undefined,
|
: undefined,
|
||||||
categories: data.organize.categories?.length
|
categories: data.organize.categories?.length
|
||||||
? data.organize.categories.map((id) => ({ id }))
|
? data.organize.categories.map((id) => ({ id }))
|
||||||
@@ -435,6 +445,7 @@ const createBlank = (): NewProductForm => {
|
|||||||
images: [],
|
images: [],
|
||||||
},
|
},
|
||||||
organize: {
|
organize: {
|
||||||
|
categories: null,
|
||||||
collection: null,
|
collection: null,
|
||||||
tags: null,
|
tags: null,
|
||||||
type: null,
|
type: null,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useAdminCreateBatchJob, useAdminCreateCollection } from "medusa-react"
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useLocation, useNavigate } from "react-router-dom"
|
import { useLocation, useNavigate } from "react-router-dom"
|
||||||
import Fade from "../../../components/atoms/fade-wrapper"
|
import Fade from "../../../components/atoms/fade-wrapper"
|
||||||
|
import Spacer from "../../../components/atoms/spacer"
|
||||||
import Button from "../../../components/fundamentals/button"
|
import Button from "../../../components/fundamentals/button"
|
||||||
import ExportIcon from "../../../components/fundamentals/icons/export-icon"
|
import ExportIcon from "../../../components/fundamentals/icons/export-icon"
|
||||||
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
||||||
@@ -14,10 +15,10 @@ import CollectionsTable from "../../../components/templates/collections-table"
|
|||||||
import ProductTable from "../../../components/templates/product-table"
|
import ProductTable from "../../../components/templates/product-table"
|
||||||
import useNotification from "../../../hooks/use-notification"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
import useToggleState from "../../../hooks/use-toggle-state"
|
import useToggleState from "../../../hooks/use-toggle-state"
|
||||||
|
import { usePolling } from "../../../providers/polling-provider"
|
||||||
import { getErrorMessage } from "../../../utils/error-messages"
|
import { getErrorMessage } from "../../../utils/error-messages"
|
||||||
import ImportProducts from "../batch-job/import"
|
import ImportProducts from "../batch-job/import"
|
||||||
import NewProduct from "../new"
|
import NewProduct from "../new"
|
||||||
import { usePolling } from "../../../providers/polling-provider"
|
|
||||||
|
|
||||||
const VIEWS = ["products", "collections"]
|
const VIEWS = ["products", "collections"]
|
||||||
|
|
||||||
@@ -178,8 +179,10 @@ const Overview = () => {
|
|||||||
>
|
>
|
||||||
<CurrentView />
|
<CurrentView />
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showNewCollection && (
|
{showNewCollection && (
|
||||||
<AddCollectionModal
|
<AddCollectionModal
|
||||||
onClose={() => setShowNewCollection(!showNewCollection)}
|
onClose={() => setShowNewCollection(!showNewCollection)}
|
||||||
|
|||||||
@@ -6,21 +6,22 @@ import {
|
|||||||
useAdminCreatePublishableApiKey,
|
useAdminCreatePublishableApiKey,
|
||||||
} from "medusa-react"
|
} from "medusa-react"
|
||||||
|
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
|
||||||
import FocusModal from "../../../components/molecules/modal/focus-modal"
|
|
||||||
import Fade from "../../../components/atoms/fade-wrapper"
|
import Fade from "../../../components/atoms/fade-wrapper"
|
||||||
import useToggleState from "../../../hooks/use-toggle-state"
|
import Spacer from "../../../components/atoms/spacer"
|
||||||
import Button from "../../../components/fundamentals/button"
|
import Button from "../../../components/fundamentals/button"
|
||||||
|
import ChannelsIcon from "../../../components/fundamentals/icons/channels-icon"
|
||||||
import CrossIcon from "../../../components/fundamentals/icons/cross-icon"
|
import CrossIcon from "../../../components/fundamentals/icons/cross-icon"
|
||||||
import InputField from "../../../components/molecules/input"
|
import InputField from "../../../components/molecules/input"
|
||||||
|
import FocusModal from "../../../components/molecules/modal/focus-modal"
|
||||||
|
import SalesChannelsSummary from "../../../components/molecules/sales-channels-summary"
|
||||||
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import useNotification from "../../../hooks/use-notification"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
import PublishableApiKeysTable from "../tables/publishable-api-keys-table"
|
import useToggleState from "../../../hooks/use-toggle-state"
|
||||||
|
import AddSalesChannelsSideModal from "../modals/add-sales-channels"
|
||||||
import DetailsModal from "../modals/details"
|
import DetailsModal from "../modals/details"
|
||||||
import ManageSalesChannelsSideModal from "../modals/manage-sales-channels"
|
import ManageSalesChannelsSideModal from "../modals/manage-sales-channels"
|
||||||
import ChannelsIcon from "../../../components/fundamentals/icons/channels-icon"
|
import PublishableApiKeysTable from "../tables/publishable-api-keys-table"
|
||||||
import SalesChannelsSummary from "../../../components/molecules/sales-channels-summary"
|
|
||||||
import AddSalesChannelsSideModal from "../modals/add-sales-channels"
|
|
||||||
|
|
||||||
type AddSalesChannelsSectionProps = {
|
type AddSalesChannelsSectionProps = {
|
||||||
setSelectedChannels: (arg: any) => void
|
setSelectedChannels: (arg: any) => void
|
||||||
@@ -229,10 +230,10 @@ function Index() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage="Publishable API Keys"
|
label="Back to settings"
|
||||||
previousBreadcrumb="Settings"
|
path="/a/settings"
|
||||||
previousRoute="/a/settings"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
<BodyCard
|
<BodyCard
|
||||||
title="Publishable API keys"
|
title="Publishable API keys"
|
||||||
@@ -256,6 +257,7 @@ function Index() {
|
|||||||
close={_closeChannelsModal}
|
close={_closeChannelsModal}
|
||||||
/>
|
/>
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { useEffect, useRef, useState, useMemo } from "react"
|
import { useEffect, useMemo, useRef, useState } from "react"
|
||||||
|
|
||||||
import { SalesChannel } from "@medusajs/medusa"
|
import { SalesChannel } from "@medusajs/medusa"
|
||||||
import {
|
import {
|
||||||
@@ -9,27 +9,27 @@ import {
|
|||||||
useAdminUpdateSalesChannel,
|
useAdminUpdateSalesChannel,
|
||||||
} from "medusa-react"
|
} from "medusa-react"
|
||||||
|
|
||||||
import EditSalesChannel from "../form/edit-sales-channel"
|
import { useNavigate, useParams } from "react-router-dom"
|
||||||
import AddSalesChannelModal from "../form/add-sales-channel"
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
|
import Fade from "../../../components/atoms/fade-wrapper"
|
||||||
|
import Spacer from "../../../components/atoms/spacer"
|
||||||
|
import CrossIcon from "../../../components/fundamentals/icons/cross-icon"
|
||||||
|
import EditIcon from "../../../components/fundamentals/icons/edit-icon"
|
||||||
|
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
||||||
|
import SearchIcon from "../../../components/fundamentals/icons/search-icon"
|
||||||
|
import TrashIcon from "../../../components/fundamentals/icons/trash-icon"
|
||||||
import Actionables, {
|
import Actionables, {
|
||||||
ActionType,
|
ActionType,
|
||||||
} from "../../../components/molecules/actionables"
|
} from "../../../components/molecules/actionables"
|
||||||
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
import StatusSelector from "../../../components/molecules/status-selector"
|
||||||
import EditIcon from "../../../components/fundamentals/icons/edit-icon"
|
import useImperativeDialog from "../../../hooks/use-imperative-dialog"
|
||||||
import TrashIcon from "../../../components/fundamentals/icons/trash-icon"
|
import useToggleState from "../../../hooks/use-toggle-state"
|
||||||
import SearchIcon from "../../../components/fundamentals/icons/search-icon"
|
import AddSalesChannelModal from "../form/add-sales-channel"
|
||||||
|
import EditSalesChannel from "../form/edit-sales-channel"
|
||||||
import {
|
import {
|
||||||
SalesChannelProductsSelectModal,
|
SalesChannelProductsSelectModal,
|
||||||
SalesChannelProductsTable,
|
SalesChannelProductsTable,
|
||||||
} from "../tables/product"
|
} from "../tables/product"
|
||||||
import CrossIcon from "../../../components/fundamentals/icons/cross-icon"
|
|
||||||
import StatusSelector from "../../../components/molecules/status-selector"
|
|
||||||
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
|
||||||
import Fade from "../../../components/atoms/fade-wrapper"
|
|
||||||
import Breadcrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import useToggleState from "../../../hooks/use-toggle-state"
|
|
||||||
import { useNavigate, useParams } from "react-router-dom"
|
|
||||||
import useImperativeDialog from "../../../hooks/use-imperative-dialog"
|
|
||||||
|
|
||||||
type ListIndicatorProps = { isActive: boolean }
|
type ListIndicatorProps = { isActive: boolean }
|
||||||
|
|
||||||
@@ -101,10 +101,7 @@ function SalesChannelTile(props: SalesChannelTileProps) {
|
|||||||
<h3 className="text-grey-90 mb-1 font-semibold leading-5">
|
<h3 className="text-grey-90 mb-1 font-semibold leading-5">
|
||||||
{salesChannel.name}
|
{salesChannel.name}
|
||||||
</h3>
|
</h3>
|
||||||
<span
|
<span className="text-small text-grey-50 ">
|
||||||
title={salesChannel.description}
|
|
||||||
className="text-small text-grey-50 "
|
|
||||||
>
|
|
||||||
{salesChannel.description}
|
{salesChannel.description}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -210,7 +207,7 @@ function SalesChannelsList(props: SalesChannelsListProps) {
|
|||||||
} = props
|
} = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-grey-0 border-grey-20 col-span-1 h-[968px] rounded-lg border px-8 py-6">
|
<div className="bg-grey-0 border-grey-20 col-span-1 grow rounded-lg border px-8 py-6">
|
||||||
<SalesChannelsHeader
|
<SalesChannelsHeader
|
||||||
filterText={filterText}
|
filterText={filterText}
|
||||||
setFilterText={setFilterText}
|
setFilterText={setFilterText}
|
||||||
@@ -338,7 +335,7 @@ function SalesChannelDetails(props: SalesChannelDetailsProps) {
|
|||||||
useToggleState(false)
|
useToggleState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-rounded bg-grey-0 border-grey-20 col-span-2 h-[968px] border px-8 py-6">
|
<div className="rounded-rounded bg-grey-0 border-grey-20 col-span-2 col-span-2 h-fit border px-8 py-6">
|
||||||
<SalesChannelDetailsHeader
|
<SalesChannelDetailsHeader
|
||||||
isDefault={isDefault}
|
isDefault={isDefault}
|
||||||
resetDetails={resetDetails}
|
resetDetails={resetDetails}
|
||||||
@@ -449,13 +446,13 @@ function Details() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumb
|
<BackButton
|
||||||
currentPage={"Sales channels"}
|
path="/a/settings"
|
||||||
previousBreadcrumb={"Settings"}
|
label="Back to settings"
|
||||||
previousRoute="/a/settings"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TwoSplitPane threeCols>
|
<div className="gap-x-xsmall grid grid-cols-3">
|
||||||
<SalesChannelsList
|
<SalesChannelsList
|
||||||
filterText={filterText}
|
filterText={filterText}
|
||||||
setFilterText={setFilterText}
|
setFilterText={setFilterText}
|
||||||
@@ -477,7 +474,8 @@ function Details() {
|
|||||||
resetDetails={resetDetails}
|
resetDetails={resetDetails}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TwoSplitPane>
|
</div>
|
||||||
|
<Spacer />
|
||||||
|
|
||||||
<Fade isVisible={showCreateModal} isFullScreen={true}>
|
<Fade isVisible={showCreateModal} isFullScreen={true}>
|
||||||
<AddSalesChannelModal onClose={closeCreateModal} />
|
<AddSalesChannelModal onClose={closeCreateModal} />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Store } from "@medusajs/medusa"
|
|||||||
import { useAdminStore, useAdminUpdateStore } from "medusa-react"
|
import { useAdminStore, useAdminUpdateStore } from "medusa-react"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import BreadCrumb from "../../components/molecules/breadcrumb"
|
import BackButton from "../../components/atoms/back-button"
|
||||||
import Input from "../../components/molecules/input"
|
import Input from "../../components/molecules/input"
|
||||||
import BodyCard from "../../components/organisms/body-card"
|
import BodyCard from "../../components/organisms/body-card"
|
||||||
import useNotification from "../../hooks/use-notification"
|
import useNotification from "../../hooks/use-notification"
|
||||||
@@ -64,10 +64,10 @@ const AccountDetails = () => {
|
|||||||
return (
|
return (
|
||||||
<form className="flex-col py-5">
|
<form className="flex-col py-5">
|
||||||
<div className="max-w-[632px]">
|
<div className="max-w-[632px]">
|
||||||
<BreadCrumb
|
<BackButton
|
||||||
previousRoute="/a/settings/"
|
path="/a/settings/"
|
||||||
previousBreadcrumb="Settings"
|
label="Back to settings"
|
||||||
currentPage="Store Details"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
<BodyCard
|
<BodyCard
|
||||||
events={[
|
events={[
|
||||||
@@ -76,37 +76,41 @@ const AccountDetails = () => {
|
|||||||
type: "button",
|
type: "button",
|
||||||
onClick: handleSubmit(onSubmit),
|
onClick: handleSubmit(onSubmit),
|
||||||
},
|
},
|
||||||
{ label: "Cancel changes", type: "button", onClick: handleCancel },
|
{ label: "Cancel", type: "button", onClick: handleCancel },
|
||||||
]}
|
]}
|
||||||
title="Store Details"
|
title="Store Details"
|
||||||
subtitle="Manage your business details"
|
subtitle="Manage your business details"
|
||||||
>
|
>
|
||||||
<h6 className="mt-large inter-base-semibold">General</h6>
|
<div className="gap-y-xlarge mb-large flex flex-col">
|
||||||
<Input
|
<div>
|
||||||
className="mt-base"
|
<h2 className="inter-base-semibold mb-base">General</h2>
|
||||||
label="Store name"
|
<Input
|
||||||
{...register("name")}
|
label="Store name"
|
||||||
placeholder="Medusa Store"
|
{...register("name")}
|
||||||
/>
|
placeholder="Medusa Store"
|
||||||
<h6 className="mt-2xlarge inter-base-semibold">Advanced settings</h6>
|
/>
|
||||||
<Input
|
</div>
|
||||||
className="mt-base"
|
<div>
|
||||||
label="Swap link template"
|
<h2 className="inter-base-semibold mb-base">Advanced settings</h2>
|
||||||
{...register("swap_link_template")}
|
<Input
|
||||||
placeholder="https://acme.inc/swap={swap_id}"
|
label="Swap link template"
|
||||||
/>
|
{...register("swap_link_template")}
|
||||||
<Input
|
placeholder="https://acme.inc/swap={swap_id}"
|
||||||
className="mt-base"
|
/>
|
||||||
label="Draft order link template"
|
<Input
|
||||||
{...register("payment_link_template")}
|
className="mt-base"
|
||||||
placeholder="https://acme.inc/payment={payment_id}"
|
label="Draft order link template"
|
||||||
/>
|
{...register("payment_link_template")}
|
||||||
<Input
|
placeholder="https://acme.inc/payment={payment_id}"
|
||||||
className="mt-base"
|
/>
|
||||||
label="Invite link template"
|
<Input
|
||||||
{...register("invite_link_template")}
|
className="mt-base"
|
||||||
placeholder="https://acme-admin.inc/invite?token={invite_token}"
|
label="Invite link template"
|
||||||
/>
|
{...register("invite_link_template")}
|
||||||
|
placeholder="https://acme-admin.inc/invite?token={invite_token}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Regions = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="gap-y-xsmall flex h-full flex-col">
|
<div className="gap-y-xsmall flex h-full flex-col">
|
||||||
<BackButton label="Back to Settings" path="/a/settings" />
|
<BackButton label="Back to settings" path="/a/settings" />
|
||||||
<div className="medium:grid-cols-3 gap-xsmall pb-xlarge grid grid-cols-1">
|
<div className="medium:grid-cols-3 gap-xsmall pb-xlarge grid grid-cols-1">
|
||||||
<div className="h-full w-full">
|
<div className="h-full w-full">
|
||||||
<RegionOverview id={regId} />
|
<RegionOverview id={regId} />
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const ReturnReasonDetail = ({ reason }: ReturnReasonDetailsProps) => {
|
|||||||
onClick: handleSubmit(onSave),
|
onClick: handleSubmit(onSave),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Cancel changes",
|
label: "Cancel",
|
||||||
onClick: handleCancel,
|
onClick: handleCancel,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useAdminReturnReasons } from "medusa-react"
|
import { useAdminReturnReasons } from "medusa-react"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
import Spinner from "../../../components/atoms/spinner"
|
||||||
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
||||||
import BreadCrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import RadioGroup from "../../../components/organisms/radio-group"
|
import RadioGroup from "../../../components/organisms/radio-group"
|
||||||
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
||||||
@@ -30,10 +30,10 @@ const ReturnReasons = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BreadCrumb
|
<BackButton
|
||||||
previousRoute="/a/settings"
|
path="/a/settings"
|
||||||
previousBreadcrumb="Settings"
|
label="Back to settings"
|
||||||
currentPage="Return Reasons"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
<TwoSplitPane>
|
<TwoSplitPane>
|
||||||
<BodyCard
|
<BodyCard
|
||||||
@@ -65,6 +65,7 @@ const ReturnReasons = () => {
|
|||||||
>
|
>
|
||||||
{return_reasons?.map((reason) => (
|
{return_reasons?.map((reason) => (
|
||||||
<RadioGroup.Item
|
<RadioGroup.Item
|
||||||
|
key={reason.id}
|
||||||
label={reason.label}
|
label={reason.label}
|
||||||
description={reason.description}
|
description={reason.description}
|
||||||
className="mt-xsmall"
|
className="mt-xsmall"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useAdminRegions } from "medusa-react"
|
import { useAdminRegions } from "medusa-react"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom"
|
import { useNavigate, useSearchParams } from "react-router-dom"
|
||||||
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
import Spinner from "../../../components/atoms/spinner"
|
||||||
import GearIcon from "../../../components/fundamentals/icons/gear-icon"
|
import GearIcon from "../../../components/fundamentals/icons/gear-icon"
|
||||||
import BreadCrumb from "../../../components/molecules/breadcrumb"
|
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import RadioGroup from "../../../components/organisms/radio-group"
|
import RadioGroup from "../../../components/organisms/radio-group"
|
||||||
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
||||||
@@ -26,10 +26,10 @@ const Taxes = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<BreadCrumb
|
<BackButton
|
||||||
previousRoute="/a/settings"
|
path="/a/settings"
|
||||||
previousBreadcrumb="Settings"
|
label="Back to settings"
|
||||||
currentPage="Taxes"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
<TwoSplitPane threeCols>
|
<TwoSplitPane threeCols>
|
||||||
<BodyCard
|
<BodyCard
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useState } from "react"
|
import React, { useEffect, useState } from "react"
|
||||||
import BreadCrumb from "../../../components/molecules/breadcrumb"
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Medusa from "../../../services/api"
|
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import InviteModal from "../../../components/organisms/invite-modal"
|
import InviteModal from "../../../components/organisms/invite-modal"
|
||||||
import PlusIcon from "../../../components/fundamentals/icons/plus-icon"
|
|
||||||
import UserTable from "../../../components/templates/user-table"
|
import UserTable from "../../../components/templates/user-table"
|
||||||
|
import Medusa from "../../../services/api"
|
||||||
|
|
||||||
const Users: React.FC = () => {
|
const Users: React.FC = () => {
|
||||||
const [users, setUsers] = useState([])
|
const [users, setUsers] = useState([])
|
||||||
@@ -46,28 +46,27 @@ const Users: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col">
|
<div className="flex h-full flex-col">
|
||||||
<div className="flex w-full grow flex-col">
|
<div className="flex w-full grow flex-col">
|
||||||
<BreadCrumb
|
<BackButton
|
||||||
previousRoute="/a/settings"
|
path="/a/settings"
|
||||||
previousBreadcrumb="Settings"
|
label="Back to settings"
|
||||||
currentPage="The Team"
|
className="mb-xsmall"
|
||||||
/>
|
/>
|
||||||
<BodyCard
|
<BodyCard
|
||||||
title="The Team"
|
title="The Team"
|
||||||
subtitle="Manage users of your Medusa Store"
|
subtitle="Manage users of your Medusa Store"
|
||||||
actionables={actionables}
|
actionables={actionables}
|
||||||
>
|
>
|
||||||
<div className="flex grow flex-col pt-2">
|
<div className="flex grow flex-col justify-between">
|
||||||
<UserTable
|
<UserTable
|
||||||
users={users}
|
users={users}
|
||||||
invites={invites}
|
invites={invites}
|
||||||
triggerRefetch={triggerRefetch}
|
triggerRefetch={triggerRefetch}
|
||||||
/>
|
/>
|
||||||
|
<p className="inter-small-regular text-grey-50">
|
||||||
|
{users.length} member
|
||||||
|
{users.length === 1 ? "" : "s"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="inter-small-regular text-grey-50">
|
|
||||||
{users.length} member
|
|
||||||
{users.length === 1 ? "" : "s"}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showInviteModal && (
|
{showInviteModal && (
|
||||||
<InviteModal
|
<InviteModal
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ const FormValidator = {
|
|||||||
`${name} must be less than or equal to ${getNormalizedAmount(currency)}.`
|
`${name} must be less than or equal to ${getNormalizedAmount(currency)}.`
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
email: (name: string) => ({
|
||||||
|
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
|
message: `${name} must be a valid email address.`,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
AdminGiftCardsDeleteRes,
|
AdminGiftCardsDeleteRes,
|
||||||
AdminGiftCardsRes,
|
AdminGiftCardsRes,
|
||||||
|
AdminPostGiftCardsGiftCardReq,
|
||||||
AdminPostGiftCardsReq,
|
AdminPostGiftCardsReq,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
import { Response } from "@medusajs/medusa-js"
|
import { Response } from "@medusajs/medusa-js"
|
||||||
@@ -33,14 +34,14 @@ export const useAdminUpdateGiftCard = (
|
|||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
Response<AdminGiftCardsRes>,
|
Response<AdminGiftCardsRes>,
|
||||||
Error,
|
Error,
|
||||||
AdminPostGiftCardsReq
|
AdminPostGiftCardsGiftCardReq
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
const { client } = useMedusa()
|
const { client } = useMedusa()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
(payload: AdminPostGiftCardsReq) =>
|
(payload: AdminPostGiftCardsGiftCardReq) =>
|
||||||
client.admin.giftCards.update(id, payload),
|
client.admin.giftCards.update(id, payload),
|
||||||
buildOptions(
|
buildOptions(
|
||||||
queryClient,
|
queryClient,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"
|
import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"
|
||||||
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
|
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
|
||||||
|
|
||||||
import { GiftCardService } from "../../../../services"
|
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { GiftCardService } from "../../../../services"
|
||||||
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/gift-cards/{id}
|
* @oas [post] /admin/gift-cards/{id}
|
||||||
@@ -123,7 +123,7 @@ export class AdminPostGiftCardsGiftCardReq {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsDate()
|
@IsDate()
|
||||||
@Type(() => Date)
|
@Type(() => Date)
|
||||||
ends_at?: Date
|
ends_at?: Date | null
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export type CreateGiftCardInput = {
|
|||||||
|
|
||||||
export type UpdateGiftCardInput = {
|
export type UpdateGiftCardInput = {
|
||||||
balance?: number
|
balance?: number
|
||||||
ends_at?: Date
|
ends_at?: Date | null
|
||||||
is_disabled?: boolean
|
is_disabled?: boolean
|
||||||
region_id?: string
|
region_id?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
|
|||||||
Reference in new issue
Block a user