fix(dashboard,medusa,fulfillment): Move Shipping Profiles to settings (#7090)
**What** - Moves Shipping Profiles to settings - Adds `q` and filters to shipping profile list endpoint - Adds new details page for profiles
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { Text } from "@medusajs/ui"
|
||||
import { Text, clx } from "@medusajs/ui"
|
||||
import { ReactNode } from "react"
|
||||
|
||||
export type SectionRowProps = {
|
||||
title: string
|
||||
value?: React.ReactNode | string | null
|
||||
actions?: React.ReactNode
|
||||
value?: ReactNode | string | null
|
||||
actions?: ReactNode
|
||||
}
|
||||
|
||||
export const SectionRow = ({ title, value, actions }: SectionRowProps) => {
|
||||
@@ -11,9 +12,12 @@ export const SectionRow = ({ title, value, actions }: SectionRowProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`text-ui-fg-subtle grid ${
|
||||
!!actions ? "grid-cols-[1fr_1fr_28px]" : "grid-cols-2"
|
||||
} items-center px-6 py-4`}
|
||||
className={clx(
|
||||
`text-ui-fg-subtle grid grid-cols-2 items-center px-6 py-4`,
|
||||
{
|
||||
"grid-cols-[1fr_1fr_28px]": !!actions,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{title}
|
||||
|
||||
@@ -1,16 +1,151 @@
|
||||
import { clx } from "@medusajs/ui"
|
||||
import { Container, Heading, Text, clx } from "@medusajs/ui"
|
||||
import { CSSProperties, ComponentPropsWithoutRef } from "react"
|
||||
|
||||
type SkeletonProps = {
|
||||
className?: string
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export const Skeleton = ({ className }: SkeletonProps) => {
|
||||
export const Skeleton = ({ className, style }: SkeletonProps) => {
|
||||
return (
|
||||
<div
|
||||
aria-hidden
|
||||
className={clx(
|
||||
"bg-ui-bg-component h-3 w-3 animate-pulse rounded-[4px]",
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type TextSkeletonProps = {
|
||||
size?: ComponentPropsWithoutRef<typeof Text>["size"]
|
||||
leading?: ComponentPropsWithoutRef<typeof Text>["leading"]
|
||||
characters?: number
|
||||
}
|
||||
|
||||
type HeadingSkeletonProps = {
|
||||
level?: ComponentPropsWithoutRef<typeof Heading>["level"]
|
||||
characters?: number
|
||||
}
|
||||
|
||||
export const HeadingSkeleton = ({
|
||||
level = "h1",
|
||||
characters = 10,
|
||||
}: HeadingSkeletonProps) => {
|
||||
let charWidth = 9
|
||||
|
||||
switch (level) {
|
||||
case "h1":
|
||||
charWidth = 11
|
||||
break
|
||||
case "h2":
|
||||
charWidth = 10
|
||||
break
|
||||
case "h3":
|
||||
charWidth = 9
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<Skeleton
|
||||
className={clx({
|
||||
"h-7": level === "h1",
|
||||
"h-6": level === "h2",
|
||||
"h-5": level === "h3",
|
||||
})}
|
||||
style={{
|
||||
width: `${charWidth * characters}px`,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const TextSkeleton = ({
|
||||
size = "small",
|
||||
leading = "compact",
|
||||
characters = 10,
|
||||
}: TextSkeletonProps) => {
|
||||
let charWidth = 9
|
||||
|
||||
switch (size) {
|
||||
case "xlarge":
|
||||
charWidth = 13
|
||||
break
|
||||
case "large":
|
||||
charWidth = 11
|
||||
break
|
||||
case "base":
|
||||
charWidth = 10
|
||||
break
|
||||
case "small":
|
||||
charWidth = 9
|
||||
break
|
||||
case "xsmall":
|
||||
charWidth = 8
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<Skeleton
|
||||
className={clx({
|
||||
"h-5": size === "xsmall",
|
||||
"h-6": size === "small",
|
||||
"h-7": size === "base",
|
||||
"h-8": size === "xlarge",
|
||||
"!h-5": leading === "compact",
|
||||
})}
|
||||
style={{
|
||||
width: `${charWidth * characters}px`,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const IconButtonSkeleton = () => {
|
||||
return <Skeleton className="h-7 w-7 rounded-md" />
|
||||
}
|
||||
|
||||
type GeneralSectionSkeletonProps = {
|
||||
rowCount?: number
|
||||
}
|
||||
|
||||
export const GeneralSectionSkeleton = ({
|
||||
rowCount,
|
||||
}: GeneralSectionSkeletonProps) => {
|
||||
const rows = Array.from({ length: rowCount ?? 0 }, (_, i) => i)
|
||||
|
||||
return (
|
||||
<Container className="divide-y p-0" aria-hidden>
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<HeadingSkeleton characters={16} />
|
||||
<IconButtonSkeleton />
|
||||
</div>
|
||||
{rows.map((row) => (
|
||||
<div
|
||||
key={row}
|
||||
className="grid grid-cols-2 items-center px-6 py-4"
|
||||
aria-hidden
|
||||
>
|
||||
<TextSkeleton size="small" leading="compact" characters={12} />
|
||||
<TextSkeleton size="small" leading="compact" characters={24} />
|
||||
</div>
|
||||
))}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export const JsonViewSectionSkeleton = () => {
|
||||
return (
|
||||
<Container className="divide-y p-0" aria-hidden>
|
||||
<div className="flex items-center justify-between px-6 py-4" aria-hidden>
|
||||
<div aria-hidden className="flex items-center gap-x-4">
|
||||
<HeadingSkeleton level="h2" characters={16} />
|
||||
<Skeleton className="h-5 w-12 rounded-md" />
|
||||
</div>
|
||||
<IconButtonSkeleton />
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -148,12 +148,6 @@ const useCoreRoutes = (): Omit<NavItemProps, "pathname">[] => {
|
||||
icon: <Envelope />,
|
||||
label: t("shipping.domain"),
|
||||
to: "/shipping",
|
||||
items: [
|
||||
{
|
||||
label: t("shippingProfile.domain"),
|
||||
to: "/shipping-profiles",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
+4
@@ -53,6 +53,10 @@ const useSettingRoutes = (): NavItemProps[] => {
|
||||
label: t("salesChannels.domain"),
|
||||
to: "/settings/sales-channels",
|
||||
},
|
||||
{
|
||||
label: t("shippingProfile.domain"),
|
||||
to: "/settings/shipping-profiles",
|
||||
},
|
||||
],
|
||||
[t]
|
||||
)
|
||||
|
||||
+21
-19
@@ -1,12 +1,12 @@
|
||||
import { EllipseMiniSolid, XMarkMini } from "@medusajs/icons"
|
||||
import { DatePicker, Text, clx } from "@medusajs/ui"
|
||||
import * as Popover from "@radix-ui/react-popover"
|
||||
import { format } from "date-fns"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import { MouseEvent, useMemo, useState } from "react"
|
||||
|
||||
import { t } from "i18next"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useDate } from "../../../../hooks/use-date"
|
||||
import { useSelectedParams } from "../hooks"
|
||||
import { useDataTableFilterContext } from "./context"
|
||||
import { IFilter } from "./types"
|
||||
@@ -17,19 +17,19 @@ type DateComparisonOperator = {
|
||||
/**
|
||||
* The filtered date must be greater than or equal to this value.
|
||||
*/
|
||||
gte?: string
|
||||
$gte?: string
|
||||
/**
|
||||
* The filtered date must be less than or equal to this value.
|
||||
*/
|
||||
lte?: string
|
||||
$lte?: string
|
||||
/**
|
||||
* The filtered date must be less than this value.
|
||||
*/
|
||||
lt?: string
|
||||
$lt?: string
|
||||
/**
|
||||
* The filtered date must be greater than this value.
|
||||
*/
|
||||
gt?: string
|
||||
$gt?: string
|
||||
}
|
||||
|
||||
export const DateFilter = ({
|
||||
@@ -40,6 +40,8 @@ export const DateFilter = ({
|
||||
const [open, setOpen] = useState(openOnMount)
|
||||
const [showCustom, setShowCustom] = useState(false)
|
||||
|
||||
const { getFullDate } = useDate()
|
||||
|
||||
const { key, label } = filter
|
||||
|
||||
const { removeFilter } = useDataTableFilterContext()
|
||||
@@ -60,14 +62,14 @@ export const DateFilter = ({
|
||||
const currentValue = selectedParams.get()
|
||||
|
||||
const currentDateComparison = parseDateComparison(currentValue)
|
||||
const customStartValue = getDateFromComparison(currentDateComparison, "gte")
|
||||
const customEndValue = getDateFromComparison(currentDateComparison, "lte")
|
||||
const customStartValue = getDateFromComparison(currentDateComparison, "$gte")
|
||||
const customEndValue = getDateFromComparison(currentDateComparison, "$lte")
|
||||
|
||||
const handleCustomDateChange = (
|
||||
value: Date | undefined,
|
||||
pos: "start" | "end"
|
||||
) => {
|
||||
const key = pos === "start" ? "gte" : "lte"
|
||||
const key = pos === "start" ? "$gte" : "$lte"
|
||||
const dateValue = value ? value.toISOString() : undefined
|
||||
|
||||
selectedParams.add(
|
||||
@@ -84,7 +86,7 @@ export const DateFilter = ({
|
||||
}
|
||||
|
||||
const formatCustomDate = (date: Date | undefined) => {
|
||||
return date ? format(date, "dd MMM, yyyy") : undefined
|
||||
return date ? getFullDate({ date: date }) : undefined
|
||||
}
|
||||
|
||||
const getCustomDisplayValue = () => {
|
||||
@@ -194,12 +196,12 @@ export const DateFilter = ({
|
||||
<div>
|
||||
<div className="px-2 py-1">
|
||||
<Text size="xsmall" leading="compact" weight="plus">
|
||||
Starting
|
||||
{t("filters.date.from")}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="px-2 py-1">
|
||||
<DatePicker
|
||||
placeholder="MM/DD/YYYY"
|
||||
// placeholder="MM/DD/YYYY" TODO: Fix DatePicker component not working with placeholder
|
||||
toDate={customEndValue}
|
||||
value={customStartValue}
|
||||
onChange={(d) => handleCustomDateChange(d, "start")}
|
||||
@@ -209,12 +211,12 @@ export const DateFilter = ({
|
||||
<div>
|
||||
<div className="px-2 py-1">
|
||||
<Text size="xsmall" leading="compact" weight="plus">
|
||||
Ending
|
||||
{t("filters.date.to")}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="px-2 py-1">
|
||||
<DatePicker
|
||||
placeholder="MM/DD/YYYY"
|
||||
// placeholder="MM/DD/YYYY"
|
||||
fromDate={customStartValue}
|
||||
value={customEndValue || undefined}
|
||||
onChange={(d) => {
|
||||
@@ -301,13 +303,13 @@ const usePresets = () => {
|
||||
{
|
||||
label: t("filters.date.today"),
|
||||
value: {
|
||||
gte: today.toISOString(),
|
||||
$gte: today.toISOString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t("filters.date.lastSevenDays"),
|
||||
value: {
|
||||
gte: new Date(
|
||||
$gte: new Date(
|
||||
today.getTime() - 7 * 24 * 60 * 60 * 1000
|
||||
).toISOString(), // 7 days ago
|
||||
},
|
||||
@@ -315,7 +317,7 @@ const usePresets = () => {
|
||||
{
|
||||
label: t("filters.date.lastThirtyDays"),
|
||||
value: {
|
||||
gte: new Date(
|
||||
$gte: new Date(
|
||||
today.getTime() - 30 * 24 * 60 * 60 * 1000
|
||||
).toISOString(), // 30 days ago
|
||||
},
|
||||
@@ -323,7 +325,7 @@ const usePresets = () => {
|
||||
{
|
||||
label: t("filters.date.lastNinetyDays"),
|
||||
value: {
|
||||
gte: new Date(
|
||||
$gte: new Date(
|
||||
today.getTime() - 90 * 24 * 60 * 60 * 1000
|
||||
).toISOString(), // 90 days ago
|
||||
},
|
||||
@@ -331,7 +333,7 @@ const usePresets = () => {
|
||||
{
|
||||
label: t("filters.date.lastTwelveMonths"),
|
||||
value: {
|
||||
gte: new Date(
|
||||
$gte: new Date(
|
||||
today.getTime() - 365 * 24 * 60 * 60 * 1000
|
||||
).toISOString(), // 365 days ago
|
||||
},
|
||||
@@ -349,7 +351,7 @@ const parseDateComparison = (value: string[]) => {
|
||||
|
||||
const getDateFromComparison = (
|
||||
comparison: DateComparisonOperator | null,
|
||||
key: "gte" | "lte"
|
||||
key: "$gte" | "$lte"
|
||||
) => {
|
||||
return comparison?.[key] ? new Date(comparison[key] as string) : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user