diff --git a/.changeset/violet-trainers-sleep.md b/.changeset/violet-trainers-sleep.md new file mode 100644 index 0000000000000..fdbf82d75f616 --- /dev/null +++ b/.changeset/violet-trainers-sleep.md @@ -0,0 +1,7 @@ +--- +"@medusajs/admin-shared": patch +"@medusajs/dashboard": patch +"@medusajs/js-sdk": patch +--- + +feat(dashboard,js-sdk,admin-shared): add customer addresses + layout change diff --git a/packages/admin/admin-shared/src/extensions/widgets/constants.ts b/packages/admin/admin-shared/src/extensions/widgets/constants.ts index b4a83fc1a97cc..e45f38458bedb 100644 --- a/packages/admin/admin-shared/src/extensions/widgets/constants.ts +++ b/packages/admin/admin-shared/src/extensions/widgets/constants.ts @@ -10,6 +10,8 @@ const ORDER_INJECTION_ZONES = [ const CUSTOMER_INJECTION_ZONES = [ "customer.details.before", "customer.details.after", + "customer.details.side.before", + "customer.details.side.after", "customer.list.before", "customer.list.after", ] as const diff --git a/packages/admin/dashboard/src/components/common/listicle/index.ts b/packages/admin/dashboard/src/components/common/listicle/index.ts new file mode 100644 index 0000000000000..49b9a3a3ee0ff --- /dev/null +++ b/packages/admin/dashboard/src/components/common/listicle/index.ts @@ -0,0 +1 @@ +export * from "./listicle" diff --git a/packages/admin/dashboard/src/components/common/listicle/listicle.tsx b/packages/admin/dashboard/src/components/common/listicle/listicle.tsx new file mode 100644 index 0000000000000..05b7836d3fbf9 --- /dev/null +++ b/packages/admin/dashboard/src/components/common/listicle/listicle.tsx @@ -0,0 +1,34 @@ +import { Text } from "@medusajs/ui" +import { ReactNode } from "react" + +export interface ListicleProps { + labelKey: string + descriptionKey: string + children?: ReactNode +} + +export const Listicle = ({ + labelKey, + descriptionKey, + children, +}: ListicleProps) => { + return ( +
+
+
+
+ + {labelKey} + + + {descriptionKey} + +
+
+ {children} +
+
+
+
+ ) +} diff --git a/packages/admin/dashboard/src/components/common/sidebar-link/sidebar-link.tsx b/packages/admin/dashboard/src/components/common/sidebar-link/sidebar-link.tsx index cb2f9ffba6f2f..5da28dc2ec9ad 100644 --- a/packages/admin/dashboard/src/components/common/sidebar-link/sidebar-link.tsx +++ b/packages/admin/dashboard/src/components/common/sidebar-link/sidebar-link.tsx @@ -1,9 +1,9 @@ import { ReactNode } from "react" import { Link } from "react-router-dom" -import { IconAvatar } from "../icon-avatar" -import { Text } from "@medusajs/ui" import { TriangleRightMini } from "@medusajs/icons" +import { Text } from "@medusajs/ui" +import { IconAvatar } from "../icon-avatar" export interface SidebarLinkProps { to: string diff --git a/packages/admin/dashboard/src/hooks/api/customers.tsx b/packages/admin/dashboard/src/hooks/api/customers.tsx index 5f2ef7b66a5f1..88cd2a482e069 100644 --- a/packages/admin/dashboard/src/hooks/api/customers.tsx +++ b/packages/admin/dashboard/src/hooks/api/customers.tsx @@ -14,6 +14,9 @@ import { customerGroupsQueryKeys } from "./customer-groups" const CUSTOMERS_QUERY_KEY = "customers" as const export const customersQueryKeys = queryKeysFactory(CUSTOMERS_QUERY_KEY) +export const customerAddressesQueryKeys = queryKeysFactory( + `${CUSTOMERS_QUERY_KEY}-addresses` +) export const useCustomer = ( id: string, @@ -148,3 +151,113 @@ export const useBatchCustomerCustomerGroups = ( ...options, }) } + +export const useCreateCustomerAddress = ( + id: string, + options?: UseMutationOptions< + HttpTypes.AdminCustomerResponse, + FetchError, + HttpTypes.AdminCreateCustomerAddress + > +) => { + return useMutation({ + mutationFn: (payload) => sdk.admin.customer.createAddress(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: customersQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: customersQueryKeys.detail(id) }) + queryClient.invalidateQueries({ + queryKey: customerAddressesQueryKeys.list(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateCustomerAddress = ( + id: string, + addressId: string, + options?: UseMutationOptions< + HttpTypes.AdminCustomerResponse, + FetchError, + HttpTypes.AdminUpdateCustomerAddress + > +) => { + return useMutation({ + mutationFn: (payload) => + sdk.admin.customer.updateAddress(id, addressId, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: customersQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: customersQueryKeys.detail(id) }) + queryClient.invalidateQueries({ + queryKey: customerAddressesQueryKeys.list(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteCustomerAddress = ( + id: string, + options?: UseMutationOptions< + HttpTypes.AdminCustomerResponse, + FetchError, + string + > +) => { + return useMutation({ + mutationFn: (addressId: string) => + sdk.admin.customer.deleteAddress(id, addressId), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: customersQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: customersQueryKeys.detail(id) }) + queryClient.invalidateQueries({ + queryKey: customerAddressesQueryKeys.list(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useListCustomerAddresses = ( + id: string, + query?: Record, + options?: UseQueryOptions< + HttpTypes.AdminCustomerResponse, + FetchError, + HttpTypes.AdminCustomerResponse, + QueryKey + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => sdk.admin.customer.listAddresses(id, query), + queryKey: customerAddressesQueryKeys.list(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCustomerAddress = ( + id: string, + addressId: string, + options?: UseQueryOptions< + HttpTypes.AdminCustomerResponse, + FetchError, + HttpTypes.AdminCustomerResponse, + QueryKey + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => sdk.admin.customer.retrieveAddress(id, addressId), + queryKey: customerAddressesQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin/dashboard/src/i18n/translations/$schema.json b/packages/admin/dashboard/src/i18n/translations/$schema.json index 9c513134e44b9..3bc7a07fb813a 100644 --- a/packages/admin/dashboard/src/i18n/translations/$schema.json +++ b/packages/admin/dashboard/src/i18n/translations/$schema.json @@ -134,6 +134,9 @@ "areYouSure": { "type": "string" }, + "areYouSureDescription": { + "type": "string" + }, "noRecordsFound": { "type": "string" }, @@ -1346,6 +1349,17 @@ "addresses": { "type": "object", "properties": { + "addressTypes": { + "shipping": { + "type": "string" + }, + "billing": { + "type": "string" + } + }, + "title": { + "type": "string" + }, "shippingAddress": { "type": "object", "properties": { @@ -3491,6 +3505,84 @@ }, "hasAccount": { "type": "string" + }, + "addresses": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "fields": { + "type": "object", + "properties": { + "addressName": { + "type": "string" + }, + "address1": { + "type": "string" + }, + "address2": { + "type": "string" + }, + "city": { + "type": "string" + }, + "province": { + "type": "string" + }, + "postalCode": { + "type": "string" + }, + "country": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "company": { + "type": "string" + }, + "countryCode": { + "type": "string" + }, + "provinceCode": { + "type": "string" + } + }, + "required": [ + "addressName", + "address1", + "address2", + "city", + "province", + "postalCode", + "country", + "phone", + "company", + "countryCode", + "provinceCode" + ], + "additionalProperties": false + }, + "create": { + "type": "object", + "properties": { + "header": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "successToast": { + "type": "string" + } + }, + "required": ["header", "hint", "successToast"], + "additionalProperties": false + } + }, + "required": ["title", "create"], + "additionalProperties": false } }, "required": [ diff --git a/packages/admin/dashboard/src/i18n/translations/en.json b/packages/admin/dashboard/src/i18n/translations/en.json index e987f8dfb2969..6729b6301e8ea 100644 --- a/packages/admin/dashboard/src/i18n/translations/en.json +++ b/packages/admin/dashboard/src/i18n/translations/en.json @@ -43,6 +43,7 @@ "plusCount": "+ {{count}}", "plusCountMore": "+ {{count}} more", "areYouSure": "Are you sure?", + "areYouSureDescription": "You are about to delete the {{entity}} {{title}}. This action cannot be undone.", "noRecordsFound": "No records found", "typeToConfirm": "Please type {val} to confirm:", "noResultsTitle": "No results", @@ -349,6 +350,11 @@ "backToDashboard": "Back to dashboard" }, "addresses": { + "title": "Addresses", + "addressTypes": { + "shipping": "Shipping", + "billing": "Billing" + }, "shippingAddress": { "header": "Shipping Address", "editHeader": "Edit Shipping Address", @@ -931,7 +937,28 @@ }, "registered": "Registered", "guest": "Guest", - "hasAccount": "Has account" + "hasAccount": "Has account", + "addresses": { + "title": "Addresses", + "fields": { + "addressName": "Address name", + "address1": "Address 1", + "address2": "Address 2", + "city": "City", + "province": "Province", + "postalCode": "Postal code", + "country": "Country", + "phone": "Phone", + "company": "Company", + "countryCode": "Country code", + "provinceCode": "Province code" + }, + "create": { + "header": "Create Address", + "hint": "Create a new address for the customer.", + "successToast": "Address was successfully created." + } + } }, "customerGroups": { "domain": "Customer Groups", diff --git a/packages/admin/dashboard/src/providers/router-provider/route-map.tsx b/packages/admin/dashboard/src/providers/router-provider/route-map.tsx index a6213b404d267..1f4fa66764439 100644 --- a/packages/admin/dashboard/src/providers/router-provider/route-map.tsx +++ b/packages/admin/dashboard/src/providers/router-provider/route-map.tsx @@ -641,6 +641,11 @@ export const RouteMap: RouteObject[] = [ path: "edit", lazy: () => import("../../routes/customers/customer-edit"), }, + { + path: "create-address", + lazy: () => + import("../../routes/customers/customer-create-address"), + }, { path: "add-customer-groups", lazy: () => diff --git a/packages/admin/dashboard/src/routes/customers/customer-create-address/components/create-customer-address-form/create-customer-address-form.tsx b/packages/admin/dashboard/src/routes/customers/customer-create-address/components/create-customer-address-form/create-customer-address-form.tsx new file mode 100644 index 0000000000000..93da3b8a25c10 --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-create-address/components/create-customer-address-form/create-customer-address-form.tsx @@ -0,0 +1,255 @@ +import { zodResolver } from "@hookform/resolvers/zod" +import { Button, Heading, Input, Text, toast } from "@medusajs/ui" +import { useForm } from "react-hook-form" +import { useTranslation } from "react-i18next" +import { useParams } from "react-router-dom" +import * as zod from "zod" +import { Form } from "../../../../../components/common/form" +import { CountrySelect } from "../../../../../components/inputs/country-select" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/modals" +import { KeyboundForm } from "../../../../../components/utilities/keybound-form" +import { useCreateCustomerAddress } from "../../../../../hooks/api/customers" + +const CreateCustomerAddressSchema = zod.object({ + address_name: zod.string().min(1), + address_1: zod.string().min(1), + address_2: zod.string().optional(), + country_code: zod.string().min(2).max(2), + city: zod.string().optional(), + postal_code: zod.string().optional(), + province: zod.string().optional(), + company: zod.string().optional(), + phone: zod.string().optional(), +}) + +export const CreateCustomerAddressForm = () => { + const { t } = useTranslation() + const { id } = useParams() + const { handleSuccess } = useRouteModal() + + const form = useForm>({ + defaultValues: { + address_name: "", + address_1: "", + address_2: "", + city: "", + company: "", + country_code: "", + phone: "", + postal_code: "", + province: "", + }, + resolver: zodResolver(CreateCustomerAddressSchema), + }) + + const { mutateAsync, isPending } = useCreateCustomerAddress(id!) + + const handleSubmit = form.handleSubmit(async (values) => { + await mutateAsync( + { + address_name: values.address_name, + address_1: values.address_1, + address_2: values.address_2, + country_code: values.country_code, + city: values.city, + postal_code: values.postal_code, + province: values.province, + company: values.company, + phone: values.phone, + }, + { + onSuccess: () => { + toast.success(t("customers.addresses.create.successToast")) + + handleSuccess(`/customers/${id}`) + }, + onError: (e) => { + toast.error(e.message) + }, + } + ) + }) + + return ( + + + + +
+
+
+ + {t("customers.addresses.create.header")} + + + {t("customers.addresses.create.hint")} + +
+
+ { + return ( + + + {t("customers.addresses.fields.addressName")} + + + + + + + ) + }} + /> +
+
+ { + return ( + + {t("fields.address")} + + + + + + ) + }} + /> + { + return ( + + {t("fields.address2")} + + + + + + ) + }} + /> + { + return ( + + + {t("fields.postalCode")} + + + + + + + ) + }} + /> + { + return ( + + {t("fields.city")} + + + + + + ) + }} + /> + { + return ( + + {t("fields.country")} + + + + + + ) + }} + /> + { + return ( + + {t("fields.state")} + + + + + + ) + }} + /> + { + return ( + + {t("fields.company")} + + + + + + ) + }} + /> + { + return ( + + {t("fields.phone")} + + + + + + ) + }} + /> +
+
+
+
+ +
+ + + + +
+
+
+
+ ) +} diff --git a/packages/admin/dashboard/src/routes/customers/customer-create-address/components/create-customer-address-form/index.ts b/packages/admin/dashboard/src/routes/customers/customer-create-address/components/create-customer-address-form/index.ts new file mode 100644 index 0000000000000..d7d23558b4392 --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-create-address/components/create-customer-address-form/index.ts @@ -0,0 +1 @@ +export * from "./create-customer-address-form" diff --git a/packages/admin/dashboard/src/routes/customers/customer-create-address/customer-create-address.tsx b/packages/admin/dashboard/src/routes/customers/customer-create-address/customer-create-address.tsx new file mode 100644 index 0000000000000..d8e3f43ce41d1 --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-create-address/customer-create-address.tsx @@ -0,0 +1,10 @@ +import { RouteFocusModal } from "../../../components/modals" +import { CreateCustomerAddressForm } from "./components/create-customer-address-form" + +export const CustomerCreateAddress = () => { + return ( + + + + ) +} diff --git a/packages/admin/dashboard/src/routes/customers/customer-create-address/index.ts b/packages/admin/dashboard/src/routes/customers/customer-create-address/index.ts new file mode 100644 index 0000000000000..06c21ad3c2f4a --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-create-address/index.ts @@ -0,0 +1 @@ +export { CustomerCreateAddress as Component } from "./customer-create-address" diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/components/customer-address-section/customer-address-section.tsx b/packages/admin/dashboard/src/routes/customers/customer-detail/components/customer-address-section/customer-address-section.tsx new file mode 100644 index 0000000000000..24fc5c91ce0e8 --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/components/customer-address-section/customer-address-section.tsx @@ -0,0 +1,92 @@ +import { HttpTypes } from "@medusajs/types" +import { Container, Heading, toast, usePrompt } from "@medusajs/ui" +import { useTranslation } from "react-i18next" + +import { Trash } from "@medusajs/icons" +import { Link, useNavigate } from "react-router-dom" +import { ActionMenu } from "../../../../../components/common/action-menu" +import { Listicle } from "../../../../../components/common/listicle" +import { useDeleteCustomerAddress } from "../../../../../hooks/api/customers" + +type CustomerAddressSectionProps = { + customer: HttpTypes.AdminCustomer +} + +export const CustomerAddressSection = ({ + customer, +}: CustomerAddressSectionProps) => { + const { t } = useTranslation() + const prompt = usePrompt() + const navigate = useNavigate() + const { mutateAsync: deleteAddress } = useDeleteCustomerAddress(customer.id) + + const addresses = customer.addresses + + const handleDelete = async (address: HttpTypes.AdminCustomerAddress) => { + const confirm = await prompt({ + title: t("general.areYouSure"), + description: t("general.areYouSureDescription", { + entity: t("fields.address"), + title: address.address_name ?? "n/a", + }), + verificationInstruction: t("general.typeToConfirm"), + verificationText: address.address_name ?? "address", + confirmText: t("actions.delete"), + cancelText: t("actions.cancel"), + }) + + if (!confirm) { + return + } + + await deleteAddress(address.id, { + onSuccess: () => { + toast.success( + t("general.success", { name: address.address_name ?? "address" }) + ) + + navigate(`/customers/${customer.id}`, { replace: true }) + }, + onError: (e) => { + toast.error(e.message) + }, + }) + } + + return ( + +
+ {t("addresses.title")} + + Add + +
+ + {addresses.map((address) => { + return ( + + , + label: t("actions.delete"), + onClick: async () => { + await handleDelete(address) + }, + }, + ], + }, + ]} + /> + + ) + })} +
+ ) +} diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/components/customer-address-section/index.ts b/packages/admin/dashboard/src/routes/customers/customer-detail/components/customer-address-section/index.ts new file mode 100644 index 0000000000000..d3e48708939fe --- /dev/null +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/components/customer-address-section/index.ts @@ -0,0 +1 @@ +export * from "./customer-address-section" diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/customer-detail.tsx b/packages/admin/dashboard/src/routes/customers/customer-detail/customer-detail.tsx index 4c77a5d99cb8d..916bb36d7e65f 100644 --- a/packages/admin/dashboard/src/routes/customers/customer-detail/customer-detail.tsx +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/customer-detail.tsx @@ -1,9 +1,10 @@ import { useLoaderData, useParams } from "react-router-dom" import { SingleColumnPageSkeleton } from "../../../components/common/skeleton" -import { SingleColumnPage } from "../../../components/layout/pages" +import { TwoColumnPage } from "../../../components/layout/pages" import { useDashboardExtension } from "../../../extensions" import { useCustomer } from "../../../hooks/api/customers" +import { CustomerAddressSection } from "./components/customer-address-section/customer-address-section" import { CustomerGeneralSection } from "./components/customer-general-section" import { CustomerGroupSection } from "./components/customer-group-section" import { CustomerOrderSection } from "./components/customer-order-section" @@ -15,9 +16,11 @@ export const CustomerDetail = () => { const initialData = useLoaderData() as Awaited< ReturnType > - const { customer, isLoading, isError, error } = useCustomer(id!, undefined, { - initialData, - }) + const { customer, isLoading, isError, error } = useCustomer( + id!, + { fields: "+*addresses" }, + { initialData } + ) const { getWidgets } = useDashboardExtension() @@ -30,19 +33,26 @@ export const CustomerDetail = () => { } return ( - - - - - + + + + + + + + + ) } diff --git a/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts b/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts index eebfaa84f9378..301e21403ed44 100644 --- a/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts +++ b/packages/admin/dashboard/src/routes/customers/customer-detail/loader.ts @@ -5,7 +5,10 @@ import { queryClient } from "../../../lib/query-client" const customerDetailQuery = (id: string) => ({ queryKey: productsQueryKeys.detail(id), - queryFn: async () => sdk.admin.customer.retrieve(id), + queryFn: async () => + sdk.admin.customer.retrieve(id, { + fields: "+*addresses", + }), }) export const customerLoader = async ({ params }: LoaderFunctionArgs) => { diff --git a/packages/admin/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx b/packages/admin/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx index 6cb2ae04bc613..533b6763a520e 100644 --- a/packages/admin/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx +++ b/packages/admin/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx @@ -33,6 +33,7 @@ export const ProductListTable = () => { const { products, count, isLoading, isError, error } = useProducts( { ...searchParams, + is_giftcard: false, }, { initialData, diff --git a/packages/admin/dashboard/src/routes/products/product-list/loader.ts b/packages/admin/dashboard/src/routes/products/product-list/loader.ts index a75c08c9d9964..60eb724f9287e 100644 --- a/packages/admin/dashboard/src/routes/products/product-list/loader.ts +++ b/packages/admin/dashboard/src/routes/products/product-list/loader.ts @@ -6,8 +6,13 @@ import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" const productsListQuery = () => ({ - queryKey: productsQueryKeys.list({ limit: 20, offset: 0 }), - queryFn: async () => sdk.admin.product.list({ limit: 20, offset: 0 }), + queryKey: productsQueryKeys.list({ + limit: 20, + offset: 0, + is_giftcard: false, + }), + queryFn: async () => + sdk.admin.product.list({ limit: 20, offset: 0, is_giftcard: false }), }) export const productsLoader = (client: QueryClient) => { diff --git a/packages/core/js-sdk/src/admin/customer.ts b/packages/core/js-sdk/src/admin/customer.ts index 4ce71c078f701..76a9729add1f0 100644 --- a/packages/core/js-sdk/src/admin/customer.ts +++ b/packages/core/js-sdk/src/admin/customer.ts @@ -1,7 +1,4 @@ -import { - HttpTypes, - SelectParams, -} from "@medusajs/types" +import { HttpTypes, SelectParams } from "@medusajs/types" import { Client } from "../client" import { ClientHeaders } from "../types" @@ -20,12 +17,12 @@ export class Customer { /** * This method creates a customer. It sends a request to the * [Create Customer](https://docs.medusajs.com/api/admin#customers_postcustomers) API route. - * + * * @param body - The customer's details. * @param query - Configure the fields to retrieve in the customer. * @param headers - Headers to pass in the request. * @returns The customer's details. - * + * * @example * sdk.admin.customer.create({ * email: "customer@gmail.com" @@ -39,26 +36,27 @@ export class Customer { query?: SelectParams, headers?: ClientHeaders ) { - return this.client.fetch< - HttpTypes.AdminCustomerResponse - >(`/admin/customers`, { - method: "POST", - headers, - body, - query, - }) + return this.client.fetch( + `/admin/customers`, + { + method: "POST", + headers, + body, + query, + } + ) } /** * This method updates a customer's details. It sends a request to the * [Update Customer](https://docs.medusajs.com/api/admin#customers_postcustomersid) API route. - * + * * @param id - The customer's ID. * @param body - The details to update of the customer. * @param query - Configure the fields to retrieve in the customer. * @param headers - Headers to pass in the request. * @returns The customer's details. - * + * * @example * sdk.admin.customer.update("cus_123", { * first_name: "John" @@ -88,25 +86,25 @@ export class Customer { * This method retrieves a paginated list of customers. It sends a request to the * [List Customers](https://docs.medusajs.com/api/admin#customers_getcustomers) * API route. - * + * * @param queryParams - Filters and pagination configurations. * @param headers - Headers to pass in the request. * @returns The paginated list of customers. - * + * * @example * To retrieve the list of customers: - * + * * ```ts * sdk.admin.customer.list() * .then(({ customers, count, limit, offset }) => { * console.log(customers) * }) * ``` - * + * * To configure the pagination, pass the `limit` and `offset` query parameters. - * + * * For example, to retrieve only 10 items and skip 10 items: - * + * * ```ts * sdk.admin.customer.list({ * limit: 10, @@ -116,10 +114,10 @@ export class Customer { * console.log(customers) * }) * ``` - * + * * Using the `fields` query parameter, you can specify the fields and relations to retrieve * in each customer: - * + * * ```ts * sdk.admin.customer.list({ * fields: "id,*groups" @@ -128,43 +126,44 @@ export class Customer { * console.log(customers) * }) * ``` - * + * * Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/api/store#select-fields-and-relations). */ async list( queryParams?: HttpTypes.AdminCustomerFilters, headers?: ClientHeaders ) { - return this.client.fetch< - HttpTypes.AdminCustomerListResponse - >(`/admin/customers`, { - headers, - query: queryParams, - }) + return this.client.fetch( + `/admin/customers`, + { + headers, + query: queryParams, + } + ) } /** - * This method retrieves a customer by its ID. It sends a request to the + * This method retrieves a customer by its ID. It sends a request to the * [Get Customer](https://docs.medusajs.com/api/admin#customers_getcustomersid) * API route. - * + * * @param id - The customer's ID. * @param query - Configure the fields to retrieve in the customer. * @param headers - Headers to pass in the request. * @returns The customer's details. - * + * * @example * To retrieve a customer by its ID: - * + * * ```ts * sdk.admin.customer.retrieve("cus_123") * .then(({ customer }) => { * console.log(customer) * }) * ``` - * + * * To specify the fields and relations to retrieve: - * + * * ```ts * sdk.admin.customer.retrieve("cus_123", { * fields: "id,*groups" @@ -173,7 +172,7 @@ export class Customer { * console.log(customer) * }) * ``` - * + * * Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/api/store#select-fields-and-relations). */ async retrieve(id: string, query?: SelectParams, headers?: ClientHeaders) { @@ -187,14 +186,14 @@ export class Customer { } /** - * This method deletes a customer by its ID. It sends a request to the + * This method deletes a customer by its ID. It sends a request to the * [Delete Customer](https://docs.medusajs.com/api/admin#customers_deletecustomersid) * API route. - * + * * @param id - The customer's ID. * @param headers - Headers to pass in the request. * @returns The deletion's details. - * + * * @example * sdk.admin.customer.delete("cus_123") * .then(({ deleted }) => { @@ -244,4 +243,157 @@ export class Customer { } ) } + + /** + * This method creates a customer address. It sends a request to the + * [Create Customer Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddresses) + * API route. + * + * @param id - The customer's ID. + * @param body - The customer address's details. + * @param headers - Headers to pass in the request. + * @returns The customer address's details. + * + * @example + * sdk.admin.customer.createAddress("cus_123", { + * address_1: "123 Main St", + * city: "Anytown", + * country_code: "US", + * postal_code: "12345" + * }) + * .then(({ customer }) => { + * console.log(customer) + * }) + */ + async createAddress( + id: string, + body: HttpTypes.AdminCreateCustomerAddress, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/customers/${id}/addresses`, + { + method: "POST", + headers, + body, + } + ) + } + + /** + * This method updates a customer address. It sends a request to the + * [Update Customer Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddressesaddressid) + * API route. + * + * @param id - The customer's ID. + * @param addressId - The customer address's ID. + * @param body - The customer address's details. + * @param headers - Headers to pass in the request. + * @returns The customer address's details. + * + * @example + * sdk.admin.customer.updateAddress("cus_123", "cus_addr_123", { + * address_1: "123 Main St", + * city: "Anytown", + * country_code: "US", + * postal_code: "12345" + * }) + * .then(({ customer }) => { + * console.log(customer) + * }) + */ + async updateAddress( + id: string, + addressId: string, + body: HttpTypes.AdminUpdateCustomerAddress, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/customers/${id}/addresses/${addressId}`, + { + method: "POST", + headers, + body, + } + ) + } + + /** + * This method deletes a customer address. It sends a request to the + * [Delete Customer Address](https://docs.medusajs.com/api/admin#customers_deletecustomersidaddressesaddressid) + * API route. + * + * @param id - The customer's ID. + * @param addressId - The customer address's ID. + * @param headers - Headers to pass in the request. + * @returns The customer address's details. + * + * @example + * sdk.admin.customer.deleteAddress("cus_123", "cus_addr_123") + * .then(({ customer }) => { + * console.log(customer) + * }) + */ + async deleteAddress(id: string, addressId: string, headers?: ClientHeaders) { + return await this.client.fetch( + `/admin/customers/${id}/addresses/${addressId}`, + { + method: "DELETE", + headers, + } + ) + } + + /** + * This method retrieves a customer address by its ID. It sends a request to the + * [Get Customer Address](https://docs.medusajs.com/api/admin#customers_getcustomersidaddressesaddressid) + * API route. + * + * @param id - The customer's ID. + * @param addressId - The customer address's ID. + * @param headers - Headers to pass in the request. + * @returns The customer address's details. + * + * @example + * sdk.admin.customer.retrieveAddress("cus_123", "cus_addr_123") + * .then(({ customer }) => { + * console.log(customer) + * }) + */ + async retrieveAddress( + id: string, + addressId: string, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/customers/${id}/addresses/${addressId}`, + { + headers, + } + ) + } + + /** + * This method retrieves a list of customer addresses. It sends a request to the + * [List Customer Addresses](https://docs.medusajs.com/api/admin#customers_getcustomersidaddresses) + * API route. + * + * @param id - The customer's ID. + * @param headers - Headers to pass in the request. + * @returns The list of customer addresses. + * + * @example + * sdk.admin.customer.listAddresses("cus_123") + * .then(({ addresses }) => { + * console.log(addresses) + * }) + */ + async listAddresses(id: string, headers?: ClientHeaders) { + return await this.client.fetch( + `/admin/customers/${id}/addresses`, + { + headers, + } + ) + } }