diff --git a/.changeset/slimy-cobras-search.md b/.changeset/slimy-cobras-search.md new file mode 100644 index 0000000000..5869ae3022 --- /dev/null +++ b/.changeset/slimy-cobras-search.md @@ -0,0 +1,6 @@ +--- +"@medusajs/link-modules": patch +"@medusajs/modules-sdk": patch +--- + +Default MedusaApp schema scalar diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index 0c8f60beec..1a6698762d 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -19,6 +19,7 @@ "is": "is", "timeline": "Timeline", "success": "Success", + "warning": "Warning", "error": "Error", "select": "Select", "selected": "Selected", @@ -540,6 +541,10 @@ "canceled": "Canceled", "requiresAction": "Requires action" }, + "toast": { + "canceled": "Fulfillment successfully canceled", + "fulfillmentShipped": "Cannot cancel an already shipped fulfillment" + }, "trackingLabel": "Tracking", "shippingFromLabel": "Shipping from", "itemsLabel": "Items" 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 index c3d7f076fb..ad2a983019 100644 --- 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 @@ -12,6 +12,11 @@ export const FulfillmentStatusCell = ({ }: FulfillmentStatusCellProps) => { const { t } = useTranslation() + if (!status) { + // TODO: remove this once fulfillment<>order link is added + return "-" + } + const { label, color } = getOrderFulfillmentStatus(t, status) return {label} 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 index 2f5f1f8a6b..a7b2d6b9cb 100644 --- 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 @@ -10,6 +10,9 @@ type PaymentStatusCellProps = { export const PaymentStatusCell = ({ status }: PaymentStatusCellProps) => { const { t } = useTranslation() + // TODO: remove this when Order<>Payments are linked + return "-" + const { label, color } = getOrderPaymentStatus(t, status) return {label} diff --git a/packages/admin-next/dashboard/src/hooks/api/orders.ts b/packages/admin-next/dashboard/src/hooks/api/orders.ts new file mode 100644 index 0000000000..a12c25dfd9 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/orders.ts @@ -0,0 +1,40 @@ +import { QueryKey, useQuery, UseQueryOptions } from "@tanstack/react-query" + +import { queryKeysFactory } from "../../lib/query-key-factory" +import { client } from "../../lib/client" + +const ORDERS_QUERY_KEY = "orders" as const +export const ordersQueryKeys = queryKeysFactory(ORDERS_QUERY_KEY) + +export const useOrder = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.orders.retrieve(id, query), + queryKey: ordersQueryKeys.detail(id, query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useOrders = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.orders.list(query), + queryKey: ordersQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} 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 index 1288ec7b8b..d73059c55a 100644 --- 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 @@ -1,18 +1,19 @@ -import { useAdminRegions, useAdminSalesChannels } from "medusa-react" import { useTranslation } from "react-i18next" import type { Filter } from "../../../components/table/data-table" +import { useRegions } from "../../api/regions" +import { useSalesChannels } from "../../api/sales-channels" export const useOrderTableFilters = (): Filter[] => { const { t } = useTranslation() - const { regions } = useAdminRegions({ + const { regions } = useRegions({ limit: 1000, fields: "id,name", expand: "", }) - const { sales_channels } = useAdminSalesChannels({ + const { sales_channels } = useSalesChannels({ limit: 1000, fields: "id,name", expand: "", @@ -145,8 +146,9 @@ export const useOrderTableFilters = (): Filter[] => { filters = [ ...filters, - paymentStatusFilter, - fulfillmentStatusFilter, + // TODO: enable when Payment, Fulfillments <> Orders are linked + // paymentStatusFilter, + // fulfillmentStatusFilter, ...dateFilters, ] diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index 19d2cd7c8a..67d0aa2ce8 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -23,6 +23,7 @@ import { stores } from "./stores" import { tags } from "./tags" import { taxes } from "./taxes" import { users } from "./users" +import { orders } from "./orders" import { workflowExecutions } from "./workflow-executions" import { shippingProfiles } from "./shipping-profiles" @@ -43,6 +44,7 @@ export const client = { shippingProfiles: shippingProfiles, tags: tags, users: users, + orders: orders, regions: regions, taxes: taxes, invites: invites, diff --git a/packages/admin-next/dashboard/src/lib/client/orders.ts b/packages/admin-next/dashboard/src/lib/client/orders.ts new file mode 100644 index 0000000000..75d32a37c4 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/orders.ts @@ -0,0 +1,15 @@ +import { getRequest } from "./common" +import { OrderListRes, OrderRes } from "../../types/api-responses" + +async function retrieveOrder(id: string, query?: Record) { + return getRequest>(`/admin/orders/${id}`, query) +} + +async function listOrders(query?: Record) { + return getRequest>(`/admin/orders`, query) +} + +export const orders = { + list: listOrders, + retrieve: retrieveOrder, +} diff --git a/packages/admin-next/dashboard/src/lib/order-helpers.ts b/packages/admin-next/dashboard/src/lib/order-helpers.ts index bb777bef6a..1cfcae4898 100644 --- a/packages/admin-next/dashboard/src/lib/order-helpers.ts +++ b/packages/admin-next/dashboard/src/lib/order-helpers.ts @@ -5,7 +5,7 @@ export const getOrderPaymentStatus = ( t: TFunction<"translation">, status: PaymentStatus ) => { - const [label, color] = { + const [label, color] = ({ not_paid: [t("orders.payment.status.notPaid"), "red"], awaiting: [t("orders.payment.status.awaiting"), "orange"], captured: [t("orders.payment.status.captured"), "green"], @@ -16,7 +16,9 @@ export const getOrderPaymentStatus = ( ], canceled: [t("orders.payment.status.canceled"), "red"], requires_action: [t("orders.payment.status.requiresAction"), "orange"], - }[status] as [string, "red" | "orange" | "green"] + }[status] || + // TODO: remove this when Order<>Payment are linked + "not_paid") as [string, "red" | "orange" | "green"] return { label, color } } diff --git a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx index 2aafd0f003..37469c9d14 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx @@ -156,6 +156,10 @@ export const RouteMap: RouteObject[] = [ path: "", lazy: () => import("../../v2-routes/orders/order-list"), }, + { + path: ":id", + lazy: () => import("../../v2-routes/orders/order-detail"), + }, ], }, { diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx index 86a80d2d9f..22f2a9bbd8 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx @@ -99,6 +99,11 @@ const FulfillmentBadge = ({ order }: { order: Order }) => { const PaymentBadge = ({ order }: { order: Order }) => { const { t } = useTranslation() + /** + * TODO: revisit when Order<>Payment are linked + */ + return null + const { label, color } = getOrderPaymentStatus(t, order.payment_status) return ( diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index 125da3e912..1631ce3aa4 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -9,6 +9,7 @@ import { FulfillmentProviderDTO, InventoryNext, InviteDTO, + OrderDTO, PaymentProviderDTO, PriceListDTO, ProductCategoryDTO, @@ -99,6 +100,10 @@ export type InviteRes = { invite: InviteDTO } export type InviteListRes = { invites: InviteDTO[] } & ListRes export type InviteDeleteRes = DeleteRes +// Orders +export type OrderRes = { order: OrderDTO } +export type OrderListRes = { orders: OrderDTO[] } & ListRes + // Products export type ExtendedProductDTO = ProductDTO & { variants: ProductVariantDTO[] | null diff --git a/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/index.ts new file mode 100644 index 0000000000..9f94b9d3ea --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/index.ts @@ -0,0 +1 @@ +export * from "./order-activity-section" diff --git a/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/order-activity-section.tsx b/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/order-activity-section.tsx new file mode 100644 index 0000000000..591513c10f --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/order-activity-section.tsx @@ -0,0 +1,25 @@ +import { Container, Heading } from "@medusajs/ui" +import { useTranslation } from "react-i18next" +import { OrderNoteForm } from "./order-note-form" +import { OrderTimeline } from "./order-timeline" +import { OrderDTO } from "@medusajs/types" + +type OrderActivityProps = { + order: OrderDTO +} + +export const OrderActivitySection = ({ order }: OrderActivityProps) => { + const { t } = useTranslation() + + return ( + +
+
+ {t("orders.activity.header")} +
+ +
+ +
+ ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/order-note-form.tsx b/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/order-note-form.tsx new file mode 100644 index 0000000000..da5af72ccf --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/orders/order-detail/components/order-activity-section/order-note-form.tsx @@ -0,0 +1,111 @@ +import { zodResolver } from "@hookform/resolvers/zod" +import { ArrowUpCircleSolid } from "@medusajs/icons" +import { IconButton } from "@medusajs/ui" +import { useRef } from "react" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { useTranslation } from "react-i18next" +import { Form } from "../../../../../components/common/form" +import { OrderDTO } from "@medusajs/types" + +type OrderNoteFormProps = { + order: OrderDTO +} + +const OrderNoteSchema = z.object({ + value: z.string().min(1), +}) + +export const OrderNoteForm = ({ order }: OrderNoteFormProps) => { + const { t } = useTranslation() + const textareaRef = useRef(null) + + const form = useForm>({ + defaultValues: { + value: "", + }, + resolver: zodResolver(OrderNoteSchema), + }) + + const { mutateAsync, isLoading } = {} + + const handleSubmit = form.handleSubmit(async (values) => { + mutateAsync( + { + resource_id: order.id, + resource_type: "order", + value: values.value, + }, + { + onSuccess: () => { + form.reset() + handleResetSize() + }, + } + ) + }) + + const handleResize = () => { + const textarea = textareaRef.current + if (textarea) { + textarea.style.height = "auto" + textarea.style.height = textarea.scrollHeight + "px" + } + } + + const handleResetSize = () => { + const textarea = textareaRef.current + if (textarea) { + textarea.style.height = "auto" + } + } + + return ( +
+
+ +
+ { + return ( + + + +