) => {
+ if (isLoading) {
+ return (
+
+ )
+ }
+
+ const noQuery =
+ Object.values(queryObject).filter((v) => Boolean(v)).length === 0
+ const noResults = !isLoading && count === 0 && !noQuery
+ const noRecords = !isLoading && count === 0 && noQuery
+
+ if (noRecords) {
+ return
+ }
+
+ return (
+
+
+
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/data-table/hooks.tsx b/packages/admin-next/dashboard/src/components/table/data-table/hooks.tsx
new file mode 100644
index 0000000000..aeda072454
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/data-table/hooks.tsx
@@ -0,0 +1,73 @@
+import { useSearchParams } from "react-router-dom"
+
+export const useSelectedParams = ({
+ param,
+ prefix,
+ multiple = false,
+}: {
+ param: string
+ prefix?: string
+ multiple?: boolean
+}) => {
+ const [searchParams, setSearchParams] = useSearchParams()
+ const identifier = prefix ? `${prefix}_${param}` : param
+ const offsetKey = prefix ? `${prefix}_offset` : "offset"
+
+ const add = (value: string) => {
+ setSearchParams((prev) => {
+ const newValue = new URLSearchParams(prev)
+
+ const updateMultipleValues = () => {
+ const existingValues = newValue.get(identifier)?.split(",") || []
+
+ if (!existingValues.includes(value)) {
+ existingValues.push(value)
+ newValue.set(identifier, existingValues.join(","))
+ }
+ }
+
+ const updateSingleValue = () => {
+ newValue.set(identifier, value)
+ }
+
+ multiple ? updateMultipleValues() : updateSingleValue()
+ newValue.delete(offsetKey)
+
+ return newValue
+ })
+ }
+
+ const deleteParam = (value?: string) => {
+ const deleteMultipleValues = (prev: URLSearchParams) => {
+ const existingValues = prev.get(identifier)?.split(",") || []
+ const index = existingValues.indexOf(value || "")
+ if (index > -1) {
+ existingValues.splice(index, 1)
+ prev.set(identifier, existingValues.join(","))
+ }
+ }
+
+ const deleteSingleValue = (prev: URLSearchParams) => {
+ prev.delete(identifier)
+ }
+
+ setSearchParams((prev) => {
+ if (value) {
+ multiple ? deleteMultipleValues(prev) : deleteSingleValue(prev)
+ if (!prev.get(identifier)) {
+ prev.delete(identifier)
+ }
+ } else {
+ prev.delete(identifier)
+ }
+ prev.delete(offsetKey)
+ return prev
+ })
+ }
+
+ const get = () => {
+ return searchParams.get(identifier)?.split(",").filter(Boolean) || []
+ }
+
+ return { add, delete: deleteParam, get }
+}
diff --git a/packages/admin-next/dashboard/src/components/table/data-table/index.ts b/packages/admin-next/dashboard/src/components/table/data-table/index.ts
new file mode 100644
index 0000000000..78f00d949d
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/data-table/index.ts
@@ -0,0 +1,2 @@
+export * from "./data-table"
+export type { Filter } from "./data-table-filter"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/date-cell/date-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/common/date-cell/date-cell.tsx
new file mode 100644
index 0000000000..44dda62fb4
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/date-cell/date-cell.tsx
@@ -0,0 +1,41 @@
+import { Tooltip } from "@medusajs/ui"
+import format from "date-fns/format"
+import { useTranslation } from "react-i18next"
+
+type DateCellProps = {
+ date: Date
+}
+
+export const DateCell = ({ date }: DateCellProps) => {
+ const value = new Date(date)
+ value.setMinutes(value.getMinutes() - value.getTimezoneOffset())
+
+ const hour12 = Intl.DateTimeFormat().resolvedOptions().hour12
+ const timestampFormat = hour12 ? "dd MMM yyyy hh:MM a" : "dd MMM yyyy HH:MM"
+
+ return (
+
+ {`${format(
+ value,
+ timestampFormat
+ )}`}
+ }
+ >
+ {format(value, "dd MMM yyyy")}
+
+
+ )
+}
+
+export const DateHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.date")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/date-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/common/date-cell/index.ts
new file mode 100644
index 0000000000..9bf7e52ed0
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/date-cell/index.ts
@@ -0,0 +1 @@
+export * from "./date-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/status-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/common/status-cell/index.ts
new file mode 100644
index 0000000000..8cc5d6026b
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/status-cell/index.ts
@@ -0,0 +1 @@
+export * from "./status-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/status-cell/status-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/common/status-cell/status-cell.tsx
new file mode 100644
index 0000000000..168a94117e
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/status-cell/status-cell.tsx
@@ -0,0 +1,32 @@
+import { clx } from "@medusajs/ui"
+import { PropsWithChildren } from "react"
+
+type StatusCellProps = PropsWithChildren<{
+ color?: "green" | "red" | "blue" | "orange" | "grey" | "purple"
+}>
+
+export const StatusCell = ({ color, children }: StatusCellProps) => {
+ return (
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/customer-cell/customer-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/customer-cell/customer-cell.tsx
new file mode 100644
index 0000000000..af558b959c
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/customer-cell/customer-cell.tsx
@@ -0,0 +1,29 @@
+import { Customer } from "@medusajs/medusa"
+import { useTranslation } from "react-i18next"
+
+export const CustomerCell = ({ customer }: { customer: Customer | null }) => {
+ if (!customer) {
+ return -
+ }
+
+ const { first_name, last_name, email } = customer
+ const name = [first_name, last_name].filter(Boolean).join(" ")
+
+ return (
+
+ )
+}
+
+export const CustomerHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.customer")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/customer-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/customer-cell/index.ts
new file mode 100644
index 0000000000..dbdd97615a
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/customer-cell/index.ts
@@ -0,0 +1 @@
+export * from "./customer-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/display-id-cell/display-id-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/display-id-cell/display-id-cell.tsx
new file mode 100644
index 0000000000..e4f2e48aeb
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/display-id-cell/display-id-cell.tsx
@@ -0,0 +1,19 @@
+import { useTranslation } from "react-i18next"
+
+export const DisplayIdCell = ({ displayId }: { displayId: number }) => {
+ return (
+
+ #{displayId}
+
+ )
+}
+
+export const DisplayIdHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.order")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/display-id-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/display-id-cell/index.ts
new file mode 100644
index 0000000000..057f15de5b
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/display-id-cell/index.ts
@@ -0,0 +1 @@
+export * from "./display-id-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx
new file mode 100644
index 0000000000..d899e85468
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx
@@ -0,0 +1,46 @@
+import type { FulfillmentStatus } from "@medusajs/medusa"
+import { useTranslation } from "react-i18next"
+import { StatusCell } from "../../common/status-cell"
+
+type FulfillmentStatusCellProps = {
+ status: FulfillmentStatus
+}
+
+export const FulfillmentStatusCell = ({
+ status,
+}: FulfillmentStatusCellProps) => {
+ const { t } = useTranslation()
+
+ const [label, color] = {
+ not_fulfilled: [t("orders.fulfillmentStatus.notFulfilled"), "red"],
+ partially_fulfilled: [
+ t("orders.fulfillmentStatus.partiallyFulfilled"),
+ "orange",
+ ],
+ fulfilled: [t("orders.fulfillmentStatus.fulfilled"), "green"],
+ partially_shipped: [
+ t("orders.fulfillmentStatus.partiallyShipped"),
+ "orange",
+ ],
+ shipped: [t("orders.fulfillmentStatus.shipped"), "green"],
+ partially_returned: [
+ t("orders.fulfillmentStatus.partiallyReturned"),
+ "orange",
+ ],
+ returned: [t("orders.fulfillmentStatus.returned"), "green"],
+ canceled: [t("orders.fulfillmentStatus.canceled"), "red"],
+ requires_action: [t("orders.fulfillmentStatus.requresAction"), "orange"],
+ }[status] as [string, "red" | "orange" | "green"]
+
+ return {label}
+}
+
+export const FulfillmentStatusHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.fulfillment")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/index.ts
new file mode 100644
index 0000000000..a0f92c11b9
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/index.ts
@@ -0,0 +1 @@
+export * from "./fulfillment-status-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/items-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/items-cell/index.ts
new file mode 100644
index 0000000000..8df6791eae
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/items-cell/index.ts
@@ -0,0 +1 @@
+export * from "./items-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/items-cell/items-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/items-cell/items-cell.tsx
new file mode 100644
index 0000000000..fc0c3b3403
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/items-cell/items-cell.tsx
@@ -0,0 +1,26 @@
+import type { LineItem } from "@medusajs/medusa"
+import { useTranslation } from "react-i18next"
+
+export const ItemsCell = ({ items }: { items: LineItem[] }) => {
+ const { t } = useTranslation()
+
+ return (
+
+
+ {t("general.items", {
+ count: items.length,
+ })}
+
+
+ )
+}
+
+export const ItemsHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.items")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/payment-status-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/payment-status-cell/index.ts
new file mode 100644
index 0000000000..f6c4b069ee
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/payment-status-cell/index.ts
@@ -0,0 +1 @@
+export * from "./payment-status-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/payment-status-cell/payment-status-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/payment-status-cell/payment-status-cell.tsx
new file mode 100644
index 0000000000..db8828fa26
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/payment-status-cell/payment-status-cell.tsx
@@ -0,0 +1,33 @@
+import type { PaymentStatus } from "@medusajs/medusa"
+import { useTranslation } from "react-i18next"
+import { StatusCell } from "../../common/status-cell"
+
+type PaymentStatusCellProps = {
+ status: PaymentStatus
+}
+
+export const PaymentStatusCell = ({ status }: PaymentStatusCellProps) => {
+ const { t } = useTranslation()
+
+ const [label, color] = {
+ not_paid: [t("orders.paymentStatus.notPaid"), "red"],
+ awaiting: [t("orders.paymentStatus.awaiting"), "orange"],
+ captured: [t("orders.paymentStatus.captured"), "green"],
+ refunded: [t("orders.paymentStatus.refunded"), "green"],
+ partially_refunded: [t("orders.paymentStatus.partiallyRefunded"), "orange"],
+ canceled: [t("orders.paymentStatus.canceled"), "red"],
+ requires_action: [t("orders.paymentStatus.requresAction"), "orange"],
+ }[status] as [string, "red" | "orange" | "green"]
+
+ return {label}
+}
+
+export const PaymentStatusHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.payment")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/sales-channel-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/sales-channel-cell/index.ts
new file mode 100644
index 0000000000..8040cf7b1b
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/sales-channel-cell/index.ts
@@ -0,0 +1 @@
+export * from "./sales-channel-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/sales-channel-cell/sales-channel-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/sales-channel-cell/sales-channel-cell.tsx
new file mode 100644
index 0000000000..17e249d041
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/sales-channel-cell/sales-channel-cell.tsx
@@ -0,0 +1,30 @@
+import { SalesChannel } from "@medusajs/medusa"
+import { useTranslation } from "react-i18next"
+
+export const SalesChannelCell = ({
+ channel,
+}: {
+ channel: SalesChannel | null
+}) => {
+ if (!channel) {
+ return -
+ }
+
+ const { name } = channel
+
+ return (
+
+ {name}
+
+ )
+}
+
+export const SalesChannelHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.salesChannel")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/total-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/order/total-cell/index.ts
new file mode 100644
index 0000000000..a58e61c257
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/total-cell/index.ts
@@ -0,0 +1 @@
+export * from "./total-cell"
diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/total-cell/total-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/total-cell/total-cell.tsx
new file mode 100644
index 0000000000..58646ee933
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/total-cell/total-cell.tsx
@@ -0,0 +1,44 @@
+import { useTranslation } from "react-i18next"
+import { getPresentationalAmount } from "../../../../../lib/money-amount-helpers"
+
+type TotalCellProps = {
+ currencyCode: string
+ total: number | null
+}
+
+export const TotalCell = ({ currencyCode, total }: TotalCellProps) => {
+ if (!total) {
+ return -
+ }
+
+ const formatted = new Intl.NumberFormat(undefined, {
+ style: "currency",
+ currency: currencyCode,
+ currencyDisplay: "narrowSymbol",
+ }).format(0)
+
+ const symbol = formatted.replace(/\d/g, "").replace(/[.,]/g, "").trim()
+
+ const presentationAmount = getPresentationalAmount(total, currencyCode)
+ const formattedTotal = new Intl.NumberFormat(undefined, {
+ style: "decimal",
+ }).format(presentationAmount)
+
+ return (
+
+
+ {symbol} {formattedTotal} {currencyCode.toUpperCase()}
+
+
+ )
+}
+
+export const TotalHeader = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {t("fields.total")}
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/hooks/table/columns/use-order-table-columns.tsx b/packages/admin-next/dashboard/src/hooks/table/columns/use-order-table-columns.tsx
new file mode 100644
index 0000000000..490db287b1
--- /dev/null
+++ b/packages/admin-next/dashboard/src/hooks/table/columns/use-order-table-columns.tsx
@@ -0,0 +1,133 @@
+import { Order } from "@medusajs/medusa"
+import {
+ ColumnDef,
+ ColumnDefBase,
+ createColumnHelper,
+} from "@tanstack/react-table"
+import { useMemo } from "react"
+import {
+ DateCell,
+ DateHeader,
+} from "../../../components/table/table-cells/common/date-cell"
+import {
+ DisplayIdCell,
+ DisplayIdHeader,
+} from "../../../components/table/table-cells/order/display-id-cell"
+import {
+ FulfillmentStatusCell,
+ FulfillmentStatusHeader,
+} from "../../../components/table/table-cells/order/fulfillment-status-cell"
+import {
+ ItemsCell,
+ ItemsHeader,
+} from "../../../components/table/table-cells/order/items-cell"
+import {
+ PaymentStatusCell,
+ PaymentStatusHeader,
+} from "../../../components/table/table-cells/order/payment-status-cell"
+import {
+ SalesChannelCell,
+ SalesChannelHeader,
+} from "../../../components/table/table-cells/order/sales-channel-cell"
+import {
+ TotalCell,
+ TotalHeader,
+} from "../../../components/table/table-cells/order/total-cell"
+
+// We have to use any here, as the type of Order is so complex that it lags the TS server
+const columnHelper = createColumnHelper()
+
+type UseOrderTableColumnsProps = {
+ exclude?: string[]
+}
+
+export const useOrderTableColumns = (props: UseOrderTableColumnsProps) => {
+ const { exclude = [] } = props ?? {}
+
+ const columns = useMemo(
+ () => [
+ columnHelper.accessor("display_id", {
+ header: () => ,
+ cell: ({ getValue }) => {
+ const id = getValue()
+
+ return
+ },
+ }),
+ columnHelper.accessor("created_at", {
+ header: () => ,
+ cell: ({ getValue }) => {
+ const date = new Date(getValue())
+
+ return
+ },
+ }),
+ columnHelper.accessor("sales_channel", {
+ header: () => ,
+ cell: ({ getValue }) => {
+ const channel = getValue()
+
+ return
+ },
+ }),
+ columnHelper.accessor("payment_status", {
+ header: () => ,
+ cell: ({ getValue }) => {
+ const status = getValue()
+
+ return
+ },
+ }),
+ columnHelper.accessor("fulfillment_status", {
+ header: () => ,
+ cell: ({ getValue }) => {
+ const status = getValue()
+
+ return
+ },
+ }),
+ columnHelper.accessor("items", {
+ header: () => ,
+ cell: ({ getValue }) => {
+ const items = getValue()
+
+ return
+ },
+ }),
+ columnHelper.accessor("total", {
+ header: () => ,
+ cell: ({ getValue, row }) => {
+ const total = getValue()
+ const currencyCode = row.original.currency_code
+
+ return
+ },
+ }),
+ ],
+ []
+ )
+
+ const isAccessorColumnDef = (
+ c: any
+ ): c is ColumnDef & { accessorKey: string } => {
+ return c.accessorKey !== undefined
+ }
+
+ const isDisplayColumnDef = (
+ c: any
+ ): c is ColumnDef & { id: string } => {
+ return c.id !== undefined
+ }
+
+ const shouldExclude = >(c: TDef) => {
+ if (isAccessorColumnDef(c)) {
+ return exclude.includes(c.accessorKey)
+ } else if (isDisplayColumnDef(c)) {
+ return exclude.includes(c.id)
+ }
+
+ return false
+ }
+
+ return columns.filter((c) => !shouldExclude(c)) as ColumnDef[]
+}
diff --git a/packages/admin-next/dashboard/src/hooks/table/filters/use-order-table-filters.tsx b/packages/admin-next/dashboard/src/hooks/table/filters/use-order-table-filters.tsx
new file mode 100644
index 0000000000..aa5b309f05
--- /dev/null
+++ b/packages/admin-next/dashboard/src/hooks/table/filters/use-order-table-filters.tsx
@@ -0,0 +1,154 @@
+import { useAdminRegions, useAdminSalesChannels } from "medusa-react"
+import { useTranslation } from "react-i18next"
+
+import type { Filter } from "../../../components/table/data-table"
+
+export const useOrderTableFilters = (): Filter[] => {
+ const { t } = useTranslation()
+
+ const { regions } = useAdminRegions({
+ limit: 1000,
+ fields: "id,name",
+ expand: "",
+ })
+
+ const { sales_channels } = useAdminSalesChannels({
+ limit: 1000,
+ fields: "id,name",
+ expand: "",
+ })
+
+ let filters: Filter[] = []
+
+ if (regions) {
+ const regionFilter: Filter = {
+ key: "region_id",
+ label: t("fields.region"),
+ type: "select",
+ options: regions.map((r) => ({
+ label: r.name,
+ value: r.id,
+ })),
+ multiple: true,
+ searchable: true,
+ }
+
+ filters = [...filters, regionFilter]
+ }
+
+ if (sales_channels) {
+ const salesChannelFilter: Filter = {
+ key: "sales_channel_id",
+ label: t("fields.salesChannel"),
+ type: "select",
+ multiple: true,
+ searchable: true,
+ options: sales_channels.map((s) => ({
+ label: s.name,
+ value: s.id,
+ })),
+ }
+
+ filters = [...filters, salesChannelFilter]
+ }
+
+ const paymentStatusFilter: Filter = {
+ key: "payment_status",
+ label: t("orders.paymentStatusLabel"),
+ type: "select",
+ multiple: true,
+ options: [
+ {
+ label: t("orders.paymentStatus.notPaid"),
+ value: "not_paid",
+ },
+ {
+ label: t("orders.paymentStatus.awaiting"),
+ value: "awaiting",
+ },
+ {
+ label: t("orders.paymentStatus.captured"),
+ value: "captured",
+ },
+ {
+ label: t("orders.paymentStatus.refunded"),
+ value: "refunded",
+ },
+ {
+ label: t("orders.paymentStatus.partiallyRefunded"),
+ value: "partially_refunded",
+ },
+ {
+ label: t("orders.paymentStatus.canceled"),
+ value: "canceled",
+ },
+ {
+ label: t("orders.paymentStatus.requresAction"),
+ value: "requires_action",
+ },
+ ],
+ }
+
+ const fulfillmentStatusFilter: Filter = {
+ key: "fulfillment_status",
+ label: t("orders.fulfillmentStatusLabel"),
+ type: "select",
+ multiple: true,
+ options: [
+ {
+ label: t("orders.fulfillmentStatus.notFulfilled"),
+ value: "not_fulfilled",
+ },
+ {
+ label: t("orders.fulfillmentStatus.fulfilled"),
+ value: "fulfilled",
+ },
+ {
+ label: t("orders.fulfillmentStatus.partiallyFulfilled"),
+ value: "partially_fulfilled",
+ },
+ {
+ label: t("orders.fulfillmentStatus.returned"),
+ value: "returned",
+ },
+ {
+ label: t("orders.fulfillmentStatus.partiallyReturned"),
+ value: "partially_returned",
+ },
+ {
+ label: t("orders.fulfillmentStatus.shipped"),
+ value: "shipped",
+ },
+ {
+ label: t("orders.fulfillmentStatus.partiallyShipped"),
+ value: "partially_shipped",
+ },
+ {
+ label: t("orders.fulfillmentStatus.canceled"),
+ value: "canceled",
+ },
+ {
+ label: t("orders.fulfillmentStatus.requresAction"),
+ value: "requires_action",
+ },
+ ],
+ }
+
+ const dateFilters: Filter[] = [
+ { label: "Created At", key: "created_at" },
+ { label: "Updated At", key: "updated_at" },
+ ].map((f) => ({
+ key: f.key,
+ label: f.label,
+ type: "date",
+ }))
+
+ filters = [
+ ...filters,
+ paymentStatusFilter,
+ fulfillmentStatusFilter,
+ ...dateFilters,
+ ]
+
+ return filters
+}
diff --git a/packages/admin-next/dashboard/src/hooks/table/query/use-order-table-query.tsx b/packages/admin-next/dashboard/src/hooks/table/query/use-order-table-query.tsx
new file mode 100644
index 0000000000..bcf52dad14
--- /dev/null
+++ b/packages/admin-next/dashboard/src/hooks/table/query/use-order-table-query.tsx
@@ -0,0 +1,58 @@
+import { AdminGetOrdersParams } from "@medusajs/medusa"
+import { useQueryParams } from "../../use-query-params"
+
+type UseOrderTableQueryProps = {
+ prefix?: string
+ pageSize?: number
+}
+
+/**
+ * TODO: Enable `order` query param when staging is updated
+ */
+
+export const useOrderTableQuery = ({
+ prefix,
+ pageSize = 50,
+}: UseOrderTableQueryProps) => {
+ const queryObject = useQueryParams(
+ [
+ "offset",
+ "q",
+ "created_at",
+ "updated_at",
+ "region_id",
+ "sales_channel_id",
+ "payment_status",
+ "fulfillment_status",
+ ],
+ prefix
+ )
+
+ const {
+ offset,
+ sales_channel_id,
+ created_at,
+ updated_at,
+ fulfillment_status,
+ payment_status,
+ region_id,
+ q,
+ } = queryObject
+
+ const searchParams: AdminGetOrdersParams = {
+ limit: pageSize,
+ offset: offset ? Number(offset) : 0,
+ sales_channel_id: sales_channel_id?.split(","),
+ fulfillment_status: fulfillment_status?.split(","),
+ payment_status: payment_status?.split(","),
+ created_at: created_at ? JSON.parse(created_at) : undefined,
+ updated_at: updated_at ? JSON.parse(updated_at) : undefined,
+ region_id: region_id?.split(","),
+ q,
+ }
+
+ return {
+ searchParams,
+ raw: queryObject,
+ }
+}
diff --git a/packages/admin-next/dashboard/src/hooks/use-data-table.tsx b/packages/admin-next/dashboard/src/hooks/use-data-table.tsx
new file mode 100644
index 0000000000..5be85018f4
--- /dev/null
+++ b/packages/admin-next/dashboard/src/hooks/use-data-table.tsx
@@ -0,0 +1,112 @@
+import {
+ ColumnDef,
+ OnChangeFn,
+ PaginationState,
+ Row,
+ getCoreRowModel,
+ getPaginationRowModel,
+ useReactTable,
+} from "@tanstack/react-table"
+import { useEffect, useMemo, useState } from "react"
+import { useSearchParams } from "react-router-dom"
+
+type UseDataTableProps = {
+ data?: TData[]
+ columns: ColumnDef[]
+ count?: number
+ pageSize?: number
+ enableRowSelection?: boolean | ((row: Row) => boolean)
+ enablePagination?: boolean
+ getRowId?: (original: TData, index: number) => string
+ prefix?: string
+}
+
+export const useDataTable = ({
+ data = [],
+ columns,
+ count = 0,
+ pageSize: _pageSize = 50,
+ enablePagination = true,
+ enableRowSelection = false,
+ getRowId,
+ prefix,
+}: UseDataTableProps) => {
+ const [searchParams, setSearchParams] = useSearchParams()
+ const offsetKey = `${prefix ? `${prefix}_` : ""}offset`
+ const offset = searchParams.get(offsetKey)
+
+ const [{ pageIndex, pageSize }, setPagination] = useState({
+ pageIndex: offset ? Math.ceil(Number(offset) / _pageSize) : 0,
+ pageSize: _pageSize,
+ })
+ const pagination = useMemo(
+ () => ({
+ pageIndex,
+ pageSize,
+ }),
+ [pageIndex, pageSize]
+ )
+ const [rowSelection, setRowSelection] = useState({})
+
+ useEffect(() => {
+ if (!enablePagination) {
+ return
+ }
+
+ const index = offset ? Math.ceil(Number(offset) / _pageSize) : 0
+
+ if (index === pageIndex) {
+ return
+ }
+
+ setPagination((prev) => ({
+ ...prev,
+ pageIndex: index,
+ }))
+ }, [offset, enablePagination, _pageSize, pageIndex])
+
+ const onPaginationChange = (
+ updater: (old: PaginationState) => PaginationState
+ ) => {
+ const state = updater(pagination)
+ const { pageIndex, pageSize } = state
+
+ setSearchParams((prev) => {
+ if (!pageIndex) {
+ prev.delete(offsetKey)
+ return prev
+ }
+
+ const newSearch = new URLSearchParams(prev)
+ newSearch.set(offsetKey, String(pageIndex * pageSize))
+
+ return newSearch
+ })
+
+ setPagination(state)
+ return state
+ }
+
+ const table = useReactTable({
+ data,
+ columns,
+ state: {
+ rowSelection,
+ pagination: enablePagination ? pagination : undefined,
+ },
+ pageCount: Math.ceil((count ?? 0) / pageSize),
+ enableRowSelection,
+ getRowId,
+ onRowSelectionChange: enableRowSelection ? setRowSelection : undefined,
+ onPaginationChange: enablePagination
+ ? (onPaginationChange as OnChangeFn)
+ : undefined,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: enablePagination
+ ? getPaginationRowModel()
+ : undefined,
+ manualPagination: enablePagination ? true : undefined,
+ })
+
+ return { table }
+}
diff --git a/packages/admin-next/dashboard/src/hooks/use-query-params.tsx b/packages/admin-next/dashboard/src/hooks/use-query-params.tsx
index afb282362e..623975e5fb 100644
--- a/packages/admin-next/dashboard/src/hooks/use-query-params.tsx
+++ b/packages/admin-next/dashboard/src/hooks/use-query-params.tsx
@@ -1,15 +1,23 @@
import { useSearchParams } from "react-router-dom"
+type QueryParams = {
+ [key in T]: string | undefined
+}
+
export function useQueryParams(
- keys: T[]
-): Record {
+ keys: T[],
+ prefix?: string
+): QueryParams {
const [params] = useSearchParams()
// Use a type assertion to initialize the result
- const result = {} as Record
+ const result = {} as QueryParams
keys.forEach((key) => {
- result[key] = params.get(key) || undefined
+ const prefixedKey = prefix ? `${prefix}_${key}` : key
+ const value = params.get(prefixedKey) || undefined
+
+ result[key] = value
})
return result
diff --git a/packages/admin-next/dashboard/src/providers/router-provider/router-provider.tsx b/packages/admin-next/dashboard/src/providers/router-provider/router-provider.tsx
index dbdb2d99dd..3cb8603b76 100644
--- a/packages/admin-next/dashboard/src/providers/router-provider/router-provider.tsx
+++ b/packages/admin-next/dashboard/src/providers/router-provider/router-provider.tsx
@@ -74,7 +74,7 @@ const router = createBrowserRouter([
children: [
{
index: true,
- lazy: () => import("../../routes/orders/list"),
+ lazy: () => import("../../routes/orders/order-list"),
},
{
path: ":id",
diff --git a/packages/admin-next/dashboard/src/routes/customers/customer-detail/components/customer-order-section/customer-order-section.tsx b/packages/admin-next/dashboard/src/routes/customers/customer-detail/components/customer-order-section/customer-order-section.tsx
index 026ad1f84d..a2f307c568 100644
--- a/packages/admin-next/dashboard/src/routes/customers/customer-detail/components/customer-order-section/customer-order-section.tsx
+++ b/packages/admin-next/dashboard/src/routes/customers/customer-detail/components/customer-order-section/customer-order-section.tsx
@@ -1,99 +1,62 @@
-import { ReceiptPercent } from "@medusajs/icons"
-import { Customer, Order } from "@medusajs/medusa"
-import { Button, Container, Heading, Table, clx } from "@medusajs/ui"
-import {
- PaginationState,
- RowSelectionState,
- createColumnHelper,
- flexRender,
- getCoreRowModel,
- useReactTable,
-} from "@tanstack/react-table"
+import { Customer } from "@medusajs/medusa"
+import { Button, Container, Heading } from "@medusajs/ui"
import { useAdminOrders } from "medusa-react"
-import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
-import { useNavigate } from "react-router-dom"
-import { ActionMenu } from "../../../../../components/common/action-menu"
-import { NoRecords } from "../../../../../components/common/empty-table-content"
-import {
- OrderDateCell,
- OrderDisplayIdCell,
- OrderFulfillmentStatusCell,
- OrderPaymentStatusCell,
- OrderTotalCell,
-} from "../../../../../components/common/order-table-cells"
-import { Query } from "../../../../../components/filtering/query"
-import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination"
-import { useQueryParams } from "../../../../../hooks/use-query-params"
+import { DataTable } from "../../../../../components/table/data-table"
+import { useOrderTableColumns } from "../../../../../hooks/table/columns/use-order-table-columns"
+import { useOrderTableFilters } from "../../../../../hooks/table/filters/use-order-table-filters"
+import { useOrderTableQuery } from "../../../../../hooks/table/query/use-order-table-query"
+import { useDataTable } from "../../../../../hooks/use-data-table"
type CustomerGeneralSectionProps = {
customer: Customer
}
const PAGE_SIZE = 10
+const DEFAULT_RELATIONS = "customer,items,sales_channel"
+const DEFAULT_FIELDS =
+ "id,status,display_id,created_at,email,fulfillment_status,payment_status,total,currency_code"
export const CustomerOrderSection = ({
customer,
}: CustomerGeneralSectionProps) => {
const { t } = useTranslation()
- const navigate = useNavigate()
- const [{ pageIndex, pageSize }, setPagination] = useState({
- pageIndex: 0,
+ const { searchParams, raw } = useOrderTableQuery({
pageSize: PAGE_SIZE,
})
-
- const pagination = useMemo(
- () => ({
- pageIndex,
- pageSize,
- }),
- [pageIndex, pageSize]
- )
-
- const [rowSelection, setRowSelection] = useState({})
-
- const params = useQueryParams(["q"])
const { orders, count, isLoading, isError, error } = useAdminOrders(
{
customer_id: customer.id,
- limit: PAGE_SIZE,
- offset: pageIndex * PAGE_SIZE,
- fields:
- "id,status,display_id,created_at,email,fulfillment_status,payment_status,total,currency_code",
- ...params,
+ expand: DEFAULT_RELATIONS,
+ fields: DEFAULT_FIELDS,
+ ...searchParams,
},
{
keepPreviousData: true,
}
)
- const columns = useColumns()
+ const columns = useOrderTableColumns({
+ exclude: ["customer"],
+ })
+ const filters = useOrderTableFilters()
- const table = useReactTable({
+ const { table } = useDataTable({
data: orders ?? [],
columns,
- pageCount: Math.ceil((count ?? 0) / PAGE_SIZE),
- state: {
- pagination,
- rowSelection,
- },
- onPaginationChange: setPagination,
- onRowSelectionChange: setRowSelection,
- getCoreRowModel: getCoreRowModel(),
- manualPagination: true,
+ enablePagination: true,
+ count,
+ pageSize: PAGE_SIZE,
})
- const noRecords =
- Object.values(params).every((v) => !v) && !isLoading && !orders?.length
-
if (isError) {
throw error
}
return (
-
-
+
+
{t("orders.domain")}
- {!noRecords && (
-
- )}
- {noRecords ? (
-
- ) : (
-
-
-
- {table.getHeaderGroups().map((headerGroup) => {
- return (
-
- {headerGroup.headers.map((header) => {
- return (
-
- {flexRender(
- header.column.columnDef.header,
- header.getContext()
- )}
-
- )
- })}
-
- )
- })}
-
-
- {table.getRowModel().rows.map((row) => (
- navigate(`/orders/${row.original.id}`)}
- >
- {row.getVisibleCells().map((cell) => (
-
- {flexRender(
- cell.column.columnDef.cell,
- cell.getContext()
- )}
-
- ))}
-
- ))}
-
-
-
-
- )}
+ `/orders/${row.original.id}`}
+ filters={filters}
+ count={count}
+ isLoading={isLoading}
+ rowCount={PAGE_SIZE}
+ orderBy={["display_id", "created_at", "updated_at"]}
+ search={true}
+ queryObject={raw}
+ />
)
}
-
-const OrderActions = ({ order }: { order: Order }) => {
- const { t } = useTranslation()
-
- return (
-
,
- label: t("customers.viewOrder"),
- to: `/orders/${order.id}/edit`,
- },
- ],
- },
- ]}
- />
- )
-}
-
-const columnHelper = createColumnHelper
()
-
-const useColumns = () => {
- const { t } = useTranslation()
-
- return useMemo(
- () => [
- columnHelper.accessor("display_id", {
- header: "Order",
- cell: ({ getValue }) => ,
- }),
- columnHelper.accessor("created_at", {
- header: "Date",
- cell: ({ getValue }) => ,
- }),
- columnHelper.accessor("fulfillment_status", {
- header: "Fulfillment Status",
- cell: ({ getValue }) => (
-
- ),
- }),
- columnHelper.accessor("payment_status", {
- header: "Payment Status",
- cell: ({ getValue }) => ,
- }),
- columnHelper.accessor("total", {
- header: () => t("fields.total"),
- cell: ({ getValue, row }) => (
-
- ),
- }),
- columnHelper.display({
- id: "actions",
- cell: ({ row }) => ,
- }),
- ],
- [t]
- )
-}
diff --git a/packages/admin-next/dashboard/src/routes/orders/list/index.ts b/packages/admin-next/dashboard/src/routes/orders/list/index.ts
deleted file mode 100644
index ad7ea56183..0000000000
--- a/packages/admin-next/dashboard/src/routes/orders/list/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { OrderList as Component } from "./list";
diff --git a/packages/admin-next/dashboard/src/routes/orders/list/list.tsx b/packages/admin-next/dashboard/src/routes/orders/list/list.tsx
deleted file mode 100644
index d5498116ac..0000000000
--- a/packages/admin-next/dashboard/src/routes/orders/list/list.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Container, Heading } from "@medusajs/ui";
-
-export const OrderList = () => {
- return (
-
-
- Orders
-
-
- );
-};
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-list/components/order-list-table/index.ts b/packages/admin-next/dashboard/src/routes/orders/order-list/components/order-list-table/index.ts
new file mode 100644
index 0000000000..f652070734
--- /dev/null
+++ b/packages/admin-next/dashboard/src/routes/orders/order-list/components/order-list-table/index.ts
@@ -0,0 +1 @@
+export * from "./order-list-table"
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-list/components/order-list-table/order-list-table.tsx b/packages/admin-next/dashboard/src/routes/orders/order-list/components/order-list-table/order-list-table.tsx
new file mode 100644
index 0000000000..05ba27416a
--- /dev/null
+++ b/packages/admin-next/dashboard/src/routes/orders/order-list/components/order-list-table/order-list-table.tsx
@@ -0,0 +1,67 @@
+import { Container, Heading } from "@medusajs/ui"
+import { useAdminOrders } from "medusa-react"
+import { useTranslation } from "react-i18next"
+import { DataTable } from "../../../../../components/table/data-table/data-table"
+import { useOrderTableColumns } from "../../../../../hooks/table/columns/use-order-table-columns"
+import { useOrderTableFilters } from "../../../../../hooks/table/filters/use-order-table-filters"
+import { useOrderTableQuery } from "../../../../../hooks/table/query/use-order-table-query"
+import { useDataTable } from "../../../../../hooks/use-data-table"
+
+const PAGE_SIZE = 50
+const DEFAULT_RELATIONS = "customer,items,sales_channel"
+const DEFAULT_FIELDS =
+ "id,status,display_id,created_at,email,fulfillment_status,payment_status,total,currency_code"
+
+export const OrderListTable = () => {
+ const { t } = useTranslation()
+ const { searchParams, raw } = useOrderTableQuery({
+ pageSize: PAGE_SIZE,
+ })
+
+ const { orders, count, isError, error, isLoading } = useAdminOrders(
+ {
+ expand: DEFAULT_RELATIONS,
+ fields: DEFAULT_FIELDS,
+ ...searchParams,
+ },
+ {
+ keepPreviousData: true,
+ }
+ )
+
+ const filters = useOrderTableFilters()
+ const columns = useOrderTableColumns({})
+
+ const { table } = useDataTable({
+ data: orders ?? [],
+ columns,
+ enablePagination: true,
+ count,
+ pageSize: PAGE_SIZE,
+ })
+
+ if (isError) {
+ throw error
+ }
+
+ return (
+
+
+ {t("orders.domain")}
+
+ `/orders/${row.original.id}`}
+ filters={filters}
+ count={count}
+ search
+ isLoading={isLoading}
+ rowCount={PAGE_SIZE}
+ orderBy={["display_id", "created_at", "updated_at"]}
+ queryObject={raw}
+ />
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-list/index.ts b/packages/admin-next/dashboard/src/routes/orders/order-list/index.ts
new file mode 100644
index 0000000000..0d3535fb84
--- /dev/null
+++ b/packages/admin-next/dashboard/src/routes/orders/order-list/index.ts
@@ -0,0 +1 @@
+export { OrderList as Component } from "./order-list"
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-list/order-list.tsx b/packages/admin-next/dashboard/src/routes/orders/order-list/order-list.tsx
new file mode 100644
index 0000000000..587cb88bf5
--- /dev/null
+++ b/packages/admin-next/dashboard/src/routes/orders/order-list/order-list.tsx
@@ -0,0 +1,9 @@
+import { OrderListTable } from "./components/order-list-table"
+
+export const OrderList = () => {
+ return (
+
+
+
+ )
+}
diff --git a/packages/design-system/ui-preset/package.json b/packages/design-system/ui-preset/package.json
index 53bb8ebf8f..22776839f6 100644
--- a/packages/design-system/ui-preset/package.json
+++ b/packages/design-system/ui-preset/package.json
@@ -31,7 +31,7 @@
},
"devDependencies": {
"@medusajs/toolbox": "^0.0.1",
- "tailwindcss": "^3.3.2",
+ "tailwindcss": "^3.4.1",
"tsup": "^7.1.0",
"typescript": "^5.1.6"
},
diff --git a/packages/design-system/ui/package.json b/packages/design-system/ui/package.json
index d0b0734d22..9653eff581 100644
--- a/packages/design-system/ui/package.json
+++ b/packages/design-system/ui/package.json
@@ -72,7 +72,7 @@
"resize-observer-polyfill": "^1.5.1",
"rimraf": "^5.0.1",
"storybook": "^7.0.23",
- "tailwindcss": "^3.3.2",
+ "tailwindcss": "^3.4.1",
"tsc-alias": "^1.8.7",
"typescript": "^5.1.6",
"vite": "^4.3.9",
diff --git a/packages/design-system/ui/src/components/table/table.tsx b/packages/design-system/ui/src/components/table/table.tsx
index 246371cbad..40f58e18d9 100644
--- a/packages/design-system/ui/src/components/table/table.tsx
+++ b/packages/design-system/ui/src/components/table/table.tsx
@@ -49,7 +49,7 @@ const Cell = React.forwardRef<
HTMLTableCellElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
- |
+ |
))
Cell.displayName = "Table.Cell"
@@ -72,7 +72,11 @@ const HeaderCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes
>(({ className, ...props }, ref) => (
- |
+ |
))
HeaderCell.displayName = "Table.HeaderCell"
diff --git a/yarn.lock b/yarn.lock
index bed468a813..a0c934b17d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8050,8 +8050,10 @@ __metadata:
"@medusajs/ui-preset": "workspace:^"
"@medusajs/vite-plugin-extension": "workspace:^"
"@radix-ui/react-collapsible": 1.0.3
+ "@radix-ui/react-hover-card": ^1.0.7
"@tanstack/react-query": 4.22.0
"@tanstack/react-table": 8.10.7
+ "@types/node": ^20.11.15
"@types/react": 18.2.43
"@types/react-dom": 18.2.17
"@uiw/react-json-view": 2.0.0-alpha.10
@@ -8070,7 +8072,7 @@ __metadata:
react-hook-form: 7.49.1
react-i18next: 13.5.0
react-router-dom: 6.20.1
- tailwindcss: 3.3.6
+ tailwindcss: ^3.4.1
typescript: 5.2.2
vite: 5.0.10
zod: 3.22.4
@@ -8655,7 +8657,7 @@ __metadata:
dependencies:
"@medusajs/toolbox": ^0.0.1
"@tailwindcss/forms": ^0.5.3
- tailwindcss: ^3.3.2
+ tailwindcss: ^3.4.1
tailwindcss-animate: ^1.0.6
tsup: ^7.1.0
typescript: ^5.1.6
@@ -8726,7 +8728,7 @@ __metadata:
rimraf: ^5.0.1
storybook: ^7.0.23
tailwind-merge: ^1.13.2
- tailwindcss: ^3.3.2
+ tailwindcss: ^3.4.1
tsc-alias: ^1.8.7
typescript: ^5.1.6
vite: ^4.3.9
@@ -10424,6 +10426,34 @@ __metadata:
languageName: node
linkType: hard
+"@radix-ui/react-hover-card@npm:^1.0.7":
+ version: 1.0.7
+ resolution: "@radix-ui/react-hover-card@npm:1.0.7"
+ dependencies:
+ "@babel/runtime": ^7.13.10
+ "@radix-ui/primitive": 1.0.1
+ "@radix-ui/react-compose-refs": 1.0.1
+ "@radix-ui/react-context": 1.0.1
+ "@radix-ui/react-dismissable-layer": 1.0.5
+ "@radix-ui/react-popper": 1.1.3
+ "@radix-ui/react-portal": 1.0.4
+ "@radix-ui/react-presence": 1.0.1
+ "@radix-ui/react-primitive": 1.0.3
+ "@radix-ui/react-use-controllable-state": 1.0.1
+ peerDependencies:
+ "@types/react": "*"
+ "@types/react-dom": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ checksum: f29f3da5bd9a967b5a35e91ac2d1b223191c7a074550d9d9cc10a0c0baf62ba0705b32912a7d2ef1ea5c27dd5e130a9fda9cbe6c2a7f3c2037ed5dfed89aa8cc
+ languageName: node
+ linkType: hard
+
"@radix-ui/react-id@npm:1.0.0":
version: 1.0.0
resolution: "@radix-ui/react-id@npm:1.0.0"
@@ -17668,6 +17698,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/node@npm:^20.11.15":
+ version: 20.11.15
+ resolution: "@types/node@npm:20.11.15"
+ dependencies:
+ undici-types: ~5.26.4
+ checksum: 7dfab4208fedc02e9584c619551906f46ade7955bb929b1e32e354a50522eb532d6bfb2844fdaad2c8dca03be84a590674460c64cb101e1a33bb318e1ec448d4
+ languageName: node
+ linkType: hard
+
"@types/node@npm:^8.5.7":
version: 8.10.66
resolution: "@types/node@npm:8.10.66"
@@ -47893,72 +47932,6 @@ __metadata:
languageName: node
linkType: hard
-"tailwindcss@npm:3.3.6":
- version: 3.3.6
- resolution: "tailwindcss@npm:3.3.6"
- dependencies:
- "@alloc/quick-lru": ^5.2.0
- arg: ^5.0.2
- chokidar: ^3.5.3
- didyoumean: ^1.2.2
- dlv: ^1.1.3
- fast-glob: ^3.3.0
- glob-parent: ^6.0.2
- is-glob: ^4.0.3
- jiti: ^1.19.1
- lilconfig: ^2.1.0
- micromatch: ^4.0.5
- normalize-path: ^3.0.0
- object-hash: ^3.0.0
- picocolors: ^1.0.0
- postcss: ^8.4.23
- postcss-import: ^15.1.0
- postcss-js: ^4.0.1
- postcss-load-config: ^4.0.1
- postcss-nested: ^6.0.1
- postcss-selector-parser: ^6.0.11
- resolve: ^1.22.2
- sucrase: ^3.32.0
- bin:
- tailwind: lib/cli.js
- tailwindcss: lib/cli.js
- checksum: 69caade773249cb963c33e81f85b7fc423dcb74b416727483f434f4e12874187f633970c9de864fa96736289abaf71189314a53589ada0be6c09ccb0e8b78391
- languageName: node
- linkType: hard
-
-"tailwindcss@npm:^3.3.2":
- version: 3.3.4
- resolution: "tailwindcss@npm:3.3.4"
- dependencies:
- "@alloc/quick-lru": ^5.2.0
- arg: ^5.0.2
- chokidar: ^3.5.3
- didyoumean: ^1.2.2
- dlv: ^1.1.3
- fast-glob: ^3.3.0
- glob-parent: ^6.0.2
- is-glob: ^4.0.3
- jiti: ^1.19.1
- lilconfig: ^2.1.0
- micromatch: ^4.0.5
- normalize-path: ^3.0.0
- object-hash: ^3.0.0
- picocolors: ^1.0.0
- postcss: ^8.4.23
- postcss-import: ^15.1.0
- postcss-js: ^4.0.1
- postcss-load-config: ^4.0.1
- postcss-nested: ^6.0.1
- postcss-selector-parser: ^6.0.11
- resolve: ^1.22.2
- sucrase: ^3.32.0
- bin:
- tailwind: lib/cli.js
- tailwindcss: lib/cli.js
- checksum: a1a0c8c1793b1b1b67503484fe924dc84f79e74c1ddc576095d616eaecc18bbd8fcdbf7c62e07a181673466f4913ebc20d92b93b87da730148b05f7c95e6c83e
- languageName: node
- linkType: hard
-
"tailwindcss@npm:^3.3.6":
version: 3.4.0
resolution: "tailwindcss@npm:3.4.0"
@@ -47992,6 +47965,39 @@ __metadata:
languageName: node
linkType: hard
+"tailwindcss@npm:^3.4.1":
+ version: 3.4.1
+ resolution: "tailwindcss@npm:3.4.1"
+ dependencies:
+ "@alloc/quick-lru": ^5.2.0
+ arg: ^5.0.2
+ chokidar: ^3.5.3
+ didyoumean: ^1.2.2
+ dlv: ^1.1.3
+ fast-glob: ^3.3.0
+ glob-parent: ^6.0.2
+ is-glob: ^4.0.3
+ jiti: ^1.19.1
+ lilconfig: ^2.1.0
+ micromatch: ^4.0.5
+ normalize-path: ^3.0.0
+ object-hash: ^3.0.0
+ picocolors: ^1.0.0
+ postcss: ^8.4.23
+ postcss-import: ^15.1.0
+ postcss-js: ^4.0.1
+ postcss-load-config: ^4.0.1
+ postcss-nested: ^6.0.1
+ postcss-selector-parser: ^6.0.11
+ resolve: ^1.22.2
+ sucrase: ^3.32.0
+ bin:
+ tailwind: lib/cli.js
+ tailwindcss: lib/cli.js
+ checksum: eec3d758f1cd4f51ab3b4c201927c3ecd18e55f8ac94256af60276aaf8d1df78f9dddb5e9fb1e057dfa7cea3c1356add4994cc3d42da9739df874e67047e656f
+ languageName: node
+ linkType: hard
+
"tapable@npm:^1.0.0, tapable@npm:^1.1.3":
version: 1.1.3
resolution: "tapable@npm:1.1.3"