feat: Add Draft Order plugin (#13291)

* feat: Add draft order plugin

* version draft order plugin

* update readme

* chore: Update scripts

* Create purple-dolls-cheer.md

* port over latest changes

* chore: Make package public
This commit is contained in:
Oli Juhl
2025-08-27 10:14:17 +02:00
committed by GitHub
parent 7c96bc4376
commit a0fca16570
93 changed files with 14526 additions and 4 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
export const getLocaleAmount = (amount: number, currencyCode: string) => {
const formatter = new Intl.NumberFormat([], {
style: "currency",
currencyDisplay: "narrowSymbol",
currency: currencyCode,
})
return formatter.format(amount)
}
export const getNativeSymbol = (currencyCode: string) => {
const formatted = new Intl.NumberFormat([], {
style: "currency",
currency: currencyCode,
currencyDisplay: "narrowSymbol",
}).format(0)
return formatted.replace(/\d/g, "").replace(/[.,]/g, "").trim()
}
export const getDecimalDigits = (currencyCode: string) => {
const formatter = new Intl.NumberFormat(undefined, {
style: "currency",
currency: currencyCode,
})
return formatter.resolvedOptions().maximumFractionDigits
}
export const getStylizedAmount = (amount: number, currencyCode: string) => {
const symbol = getNativeSymbol(currencyCode)
const decimalDigits = getDecimalDigits(currencyCode)
const total = amount.toLocaleString(undefined, {
minimumFractionDigits: decimalDigits,
maximumFractionDigits: decimalDigits,
})
return `${symbol} ${total} ${currencyCode.toUpperCase()}`
}

View File

@@ -0,0 +1,7 @@
const orderDetailQuery = (id: string) => ({
queryKey: ordersQueryKeys.detail(id),
queryFn: async () =>
sdk.admin.order.retrieve(id, {
fields: DEFAULT_FIELDS,
}),
})

View File

@@ -0,0 +1,8 @@
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
baseUrl: "/",
auth: {
type: "session",
},
})

View File

@@ -0,0 +1,14 @@
import { z } from "zod"
export const addressSchema = z.object({
country_code: z.string().min(1),
first_name: z.string().min(1),
last_name: z.string().min(1),
address_1: z.string().min(1),
address_2: z.string().optional(),
company: z.string().optional(),
city: z.string().min(1),
province: z.string().optional(),
postal_code: z.string().min(1),
phone: z.string().optional(),
})

View File

@@ -0,0 +1,88 @@
import { HttpTypes } from "@medusajs/types"
import { getCountryByIso2 } from "../data/countries"
export function isSameAddress(
a?: HttpTypes.AdminOrderAddress | null,
b?: HttpTypes.AdminOrderAddress | null
) {
if (!a || !b) {
return false
}
return (
a.first_name === b.first_name &&
a.last_name === b.last_name &&
a.address_1 === b.address_1 &&
a.address_2 === b.address_2 &&
a.city === b.city &&
a.postal_code === b.postal_code &&
a.province === b.province &&
a.country_code === b.country_code &&
a.phone === b.phone &&
a.company === b.company
)
}
export function getFormattedAddress(
address?: HttpTypes.AdminOrderAddress | HttpTypes.AdminCustomerAddress | null
) {
if (!address) {
return []
}
const {
first_name,
last_name,
company,
address_1,
address_2,
city,
postal_code,
province,
country_code,
} = address
const country = "country" in address ? address.country : null
const name = [first_name, last_name].filter(Boolean).join(" ")
const formattedAddress: string[] = []
if (name) {
formattedAddress.push(name)
}
if (company) {
formattedAddress.push(company)
}
if (address_1) {
formattedAddress.push(address_1)
}
if (address_2) {
formattedAddress.push(address_2)
}
const cityProvincePostal = [city, province, postal_code]
.filter(Boolean)
.join(" ")
if (cityProvincePostal) {
formattedAddress.push(cityProvincePostal)
}
if (country) {
formattedAddress.push(country.display_name!)
} else if (country_code) {
const country = getCountryByIso2(country_code)
if (country) {
formattedAddress.push(country.display_name)
} else {
formattedAddress.push(country_code.toUpperCase())
}
}
return formattedAddress
}

View File

@@ -0,0 +1,33 @@
import { format, formatDistance, sub } from "date-fns"
import { enUS } from "date-fns/locale"
const LOCALE = enUS
export function getRelativeDate(date: string | Date): string {
const now = new Date()
return formatDistance(sub(new Date(date), { minutes: 0 }), now, {
addSuffix: true,
locale: LOCALE,
})
}
export const getFullDate = ({
date,
includeTime = false,
}: {
date: string | Date
includeTime?: boolean
}) => {
const ensuredDate = new Date(date)
if (isNaN(ensuredDate.getTime())) {
return ""
}
const timeFormat = includeTime ? "p" : ""
return format(ensuredDate, `PP ${timeFormat}`, {
locale: LOCALE,
})
}

View File

@@ -0,0 +1,3 @@
export function convertNumber(value?: string | number) {
return typeof value === "string" ? Number(value.replace(",", ".")) : value
}

View File

@@ -0,0 +1,40 @@
import { HttpTypes } from "@medusajs/types"
export function getUniqueShippingProfiles(
items: HttpTypes.AdminOrderLineItem[]
): HttpTypes.AdminShippingProfile[] {
const profiles = new Map<string, HttpTypes.AdminShippingProfile>()
items.forEach((item) => {
const profile = item.variant?.product?.shipping_profile
if (profile) {
profiles.set(profile.id, profile)
}
})
return Array.from(profiles.values())
}
export function getItemsWithShippingProfile(
shipping_profile_id: string,
items: HttpTypes.AdminOrderLineItem[]
) {
return items.filter(
(item) =>
item.variant?.product?.shipping_profile?.id === shipping_profile_id
)
}
export function getOrderCustomer(obj: HttpTypes.AdminOrder) {
const { first_name: sFirstName, last_name: sLastName } =
obj.shipping_address || {}
const { first_name: bFirstName, last_name: bLastName } =
obj.billing_address || {}
const { first_name: cFirstName, last_name: cLastName } = obj.customer || {}
const customerName = [cFirstName, cLastName].filter(Boolean).join(" ")
const shippingName = [sFirstName, sLastName].filter(Boolean).join(" ")
const billingName = [bFirstName, bLastName].filter(Boolean).join(" ")
const name = customerName || shippingName || billingName
return name
}

View File

@@ -0,0 +1,3 @@
export function pluralize(count: number, plural: string, singular: string) {
return count === 1 ? singular : plural
}