478 lines
20 KiB
TypeScript
478 lines
20 KiB
TypeScript
import type {
|
|
AccountSummary,
|
|
Aftersale,
|
|
AftersaleApplyBody,
|
|
AftersaleArbitration,
|
|
AftersaleDetail,
|
|
AftersaleMessage,
|
|
AftersaleMessageBody,
|
|
AftersaleReturnTrackingBody,
|
|
AftersaleStatus,
|
|
Address,
|
|
AddressBookEntry,
|
|
AuthTokens,
|
|
Brand,
|
|
BrandInput,
|
|
Cart,
|
|
Category,
|
|
ContentInputByKind,
|
|
ContentKind,
|
|
Coupon,
|
|
CouponTemplate,
|
|
CouponTemplateInput,
|
|
Currency,
|
|
Favorite,
|
|
FavoriteKind,
|
|
FlashSaleItem,
|
|
FlashSaleItemInput,
|
|
FlashSaleSession,
|
|
FlashSaleSessionInput,
|
|
GroupBuyIntent,
|
|
GroupBuyingActivity,
|
|
GroupBuyingActivityInput,
|
|
GroupBuyingActivityView,
|
|
HomeContent,
|
|
IntegralOrder,
|
|
IntegralProduct,
|
|
IntegralProductInput,
|
|
Invoice,
|
|
InvoiceKind,
|
|
LocalizedText,
|
|
Order,
|
|
OrderStatus,
|
|
Paged,
|
|
Product,
|
|
ProductStatus,
|
|
PublicFlashSaleSession,
|
|
RedeemPointsBody,
|
|
Shipment,
|
|
Shop,
|
|
ShopFlashSaleSession,
|
|
ShopProfile,
|
|
ShopProfileInput,
|
|
ShopProfileSelfInput,
|
|
Sku,
|
|
User,
|
|
} from "./types";
|
|
|
|
export interface ApiClientOptions {
|
|
baseUrl: string;
|
|
getToken?: () => string | null;
|
|
}
|
|
|
|
export class ApiError extends Error {
|
|
code: string;
|
|
status: number;
|
|
constructor(status: number, code: string, message: string) {
|
|
super(message);
|
|
this.status = status;
|
|
this.code = code;
|
|
}
|
|
}
|
|
|
|
// ---- request bodies ----
|
|
|
|
export interface ProductListQuery {
|
|
page?: number;
|
|
per_page?: number;
|
|
category_id?: string;
|
|
brand_id?: string;
|
|
q?: string;
|
|
shop_id?: string;
|
|
/** `price` orders by lowest active SKU price; `sales` by units sold. */
|
|
sort?: "price" | "sales";
|
|
order?: "asc" | "desc";
|
|
}
|
|
|
|
export interface ProductUpsertBody {
|
|
category_id?: string | null;
|
|
brand_id?: string | null;
|
|
slug: string;
|
|
name: LocalizedText;
|
|
description?: LocalizedText;
|
|
images?: string[];
|
|
}
|
|
|
|
export interface SkuUpsertBody {
|
|
sku_code: string;
|
|
attributes?: Record<string, string>;
|
|
price_minor: number;
|
|
currency: string;
|
|
stock: number;
|
|
active?: boolean;
|
|
}
|
|
|
|
export interface ShipmentItemBody {
|
|
order_item_id: string;
|
|
qty: number;
|
|
}
|
|
|
|
export interface AddressInput extends Address {
|
|
is_default?: boolean;
|
|
}
|
|
|
|
export interface CurrencyUpsertBody {
|
|
code: string;
|
|
name: LocalizedText;
|
|
symbol: string;
|
|
exponent: number;
|
|
rate_to_base: string;
|
|
enabled: boolean;
|
|
}
|
|
|
|
export interface ShopProductQuery {
|
|
page?: number;
|
|
per_page?: number;
|
|
status?: ProductStatus;
|
|
}
|
|
|
|
export interface ShopOrderQuery {
|
|
page?: number;
|
|
per_page?: number;
|
|
status?: OrderStatus;
|
|
}
|
|
|
|
export interface ConvertResult {
|
|
amount_minor: number;
|
|
currency: string;
|
|
}
|
|
|
|
export interface FavoriteListQuery {
|
|
kind: FavoriteKind;
|
|
target_id?: string;
|
|
page?: number;
|
|
per_page?: number;
|
|
}
|
|
|
|
type Query = Record<string, string | number | boolean | undefined>;
|
|
|
|
async function request<T>(
|
|
opts: ApiClientOptions,
|
|
method: string,
|
|
path: string,
|
|
body?: unknown,
|
|
query?: Query,
|
|
): Promise<T> {
|
|
const url = new URL(opts.baseUrl + path);
|
|
if (query) {
|
|
for (const [k, v] of Object.entries(query)) {
|
|
if (v !== undefined && v !== null && v !== "") url.searchParams.set(k, String(v));
|
|
}
|
|
}
|
|
const headers: Record<string, string> = { "content-type": "application/json" };
|
|
const token = opts.getToken?.();
|
|
if (token) headers.authorization = `Bearer ${token}`;
|
|
const res = await fetch(url.toString(), {
|
|
method,
|
|
headers,
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
if (!res.ok) {
|
|
let code = "UNKNOWN";
|
|
let message = res.statusText;
|
|
try {
|
|
const data = (await res.json()) as { error?: { code?: string; message?: string } };
|
|
code = data?.error?.code ?? code;
|
|
message = data?.error?.message ?? message;
|
|
} catch {
|
|
/* non-JSON error body */
|
|
}
|
|
throw new ApiError(res.status, code, message);
|
|
}
|
|
if (res.status === 204) return undefined as T;
|
|
return (await res.json()) as T;
|
|
}
|
|
|
|
export interface ApiClient {
|
|
register(email: string, password: string, displayName: string): Promise<AuthTokens>;
|
|
login(email: string, password: string): Promise<AuthTokens>;
|
|
me(): Promise<User>;
|
|
/** The signed-in customer's own money and points balances. */
|
|
getAccountSummary(): Promise<AccountSummary>;
|
|
listProducts(q?: ProductListQuery): Promise<Paged<Product>>;
|
|
getProduct(idOrSlug: string): Promise<Product>;
|
|
listCategories(): Promise<Category[]>;
|
|
listBrands(): Promise<Brand[]>;
|
|
listCurrencies(): Promise<Currency[]>;
|
|
convert(amountMinor: number, from: string, to: string): Promise<ConvertResult>;
|
|
getCart(): Promise<Cart>;
|
|
addCartItem(skuId: string, qty: number): Promise<Cart>;
|
|
updateCartItem(skuId: string, qty: number): Promise<Cart>;
|
|
removeCartItem(skuId: string): Promise<Cart>;
|
|
checkout(
|
|
shippingAddress: Address,
|
|
currency: string,
|
|
/** At most one owned coupon per generated shop order. */
|
|
couponByShop?: Record<string, string>,
|
|
/** Optional group-buying intent for one activity SKU at quantity 1. */
|
|
groupBuy?: GroupBuyIntent,
|
|
): Promise<Order[]>;
|
|
listMyOrders(page?: number): Promise<Paged<Order>>;
|
|
getOrder(id: string): Promise<Order>;
|
|
cancelOrder(id: string): Promise<Order>;
|
|
payOrder(id: string): Promise<Order>;
|
|
confirmDelivered(shipmentId: string): Promise<Shipment>;
|
|
listMyShipments(): Promise<Shipment[]>;
|
|
requestInvoice(
|
|
orderId: string,
|
|
title: string,
|
|
taxNo: string | null,
|
|
kind: InvoiceKind,
|
|
): Promise<Invoice>;
|
|
listMyInvoices(): Promise<Invoice[]>;
|
|
/** Public home-page marketing content: banners, promos, quick links, floor adverts. */
|
|
getHomeContent(): Promise<HomeContent>;
|
|
/** Public store directory: active shops with whatever profile they have. */
|
|
listShops(): Promise<ShopProfile[]>;
|
|
getShop(slug: string): Promise<ShopProfile>;
|
|
listMyAddresses(): Promise<AddressBookEntry[]>;
|
|
createAddress(address: AddressInput): Promise<AddressBookEntry>;
|
|
updateAddress(id: string, address: AddressInput): Promise<AddressBookEntry>;
|
|
deleteAddress(id: string): Promise<AddressBookEntry[]>;
|
|
setDefaultAddress(id: string): Promise<AddressBookEntry>;
|
|
listFavorites(q: FavoriteListQuery): Promise<Paged<Favorite>>;
|
|
addProductFavorite(productId: string): Promise<Favorite>;
|
|
removeProductFavorite(productId: string): Promise<void>;
|
|
addShopFavorite(shopId: string): Promise<Favorite>;
|
|
removeShopFavorite(shopId: string): Promise<void>;
|
|
/** Public: coupons a shopper could claim from this shop right now. */
|
|
listShopCouponTemplates(shopId: string): Promise<CouponTemplate[]>;
|
|
listMyCoupons(): Promise<Coupon[]>;
|
|
claimCoupon(templateId: string): Promise<Coupon>;
|
|
applyForAftersale(body: AftersaleApplyBody): Promise<AftersaleDetail>;
|
|
listMyAftersales(): Promise<Aftersale[]>;
|
|
getAftersale(id: string): Promise<AftersaleDetail>;
|
|
cancelAftersale(id: string): Promise<Aftersale>;
|
|
reopenAftersale(id: string): Promise<Aftersale>;
|
|
submitAftersaleReturnTracking(
|
|
id: string,
|
|
body: AftersaleReturnTrackingBody,
|
|
): Promise<Aftersale>;
|
|
addAftersaleMessage(id: string, body: AftersaleMessageBody): Promise<AftersaleMessage>;
|
|
/** Public points catalog: published products only. */
|
|
listPointsProducts(): Promise<IntegralProduct[]>;
|
|
listMyRedemptions(): Promise<IntegralOrder[]>;
|
|
redeemPoints(body: RedeemPointsBody): Promise<IntegralOrder>;
|
|
/** Public: active flash-sale sessions with their purchasable items. */
|
|
listFlashSales(): Promise<PublicFlashSaleSession[]>;
|
|
/** Public: active group-buying activities with their open groups. */
|
|
listGroupBuyingActivities(): Promise<GroupBuyingActivityView[]>;
|
|
shop: {
|
|
getMyShop(): Promise<Shop>;
|
|
/** Merchant self-write of the own shop profile; scores stay platform-owned. */
|
|
updateMyProfile(body: ShopProfileSelfInput): Promise<ShopProfile>;
|
|
listMyProducts(q?: ShopProductQuery): Promise<Paged<Product>>;
|
|
getProduct(id: string): Promise<Product>;
|
|
createProduct(body: ProductUpsertBody): Promise<Product>;
|
|
updateProduct(id: string, body: ProductUpsertBody): Promise<Product>;
|
|
publish(id: string): Promise<Product>;
|
|
unpublish(id: string): Promise<Product>;
|
|
upsertSku(productId: string, body: SkuUpsertBody): Promise<Sku>;
|
|
listOrders(q?: ShopOrderQuery): Promise<Paged<Order>>;
|
|
getOrder(id: string): Promise<Order>;
|
|
createShipment(
|
|
orderId: string,
|
|
carrier: string,
|
|
trackingNo: string,
|
|
items: ShipmentItemBody[],
|
|
): Promise<Shipment>;
|
|
listShipments(): Promise<Shipment[]>;
|
|
markShipped(id: string): Promise<Shipment>;
|
|
listInvoices(): Promise<Invoice[]>;
|
|
issueInvoice(id: string): Promise<Invoice>;
|
|
listCouponTemplates(): Promise<CouponTemplate[]>;
|
|
createCouponTemplate(body: CouponTemplateInput): Promise<CouponTemplate>;
|
|
updateCouponTemplate(id: string, body: CouponTemplateInput): Promise<CouponTemplate>;
|
|
deleteCouponTemplate(id: string): Promise<void>;
|
|
listFlashSales(): Promise<ShopFlashSaleSession[]>;
|
|
createFlashSale(body: FlashSaleSessionInput): Promise<FlashSaleSession>;
|
|
updateFlashSale(id: string, body: FlashSaleSessionInput): Promise<FlashSaleSession>;
|
|
deleteFlashSale(id: string): Promise<void>;
|
|
addFlashSaleItem(sessionId: string, body: FlashSaleItemInput): Promise<FlashSaleItem>;
|
|
updateFlashSaleItem(id: string, body: FlashSaleItemInput): Promise<FlashSaleItem>;
|
|
deleteFlashSaleItem(id: string): Promise<void>;
|
|
listGroupBuyingActivities(): Promise<GroupBuyingActivityView[]>;
|
|
createGroupBuyingActivity(body: GroupBuyingActivityInput): Promise<GroupBuyingActivity>;
|
|
updateGroupBuyingActivity(
|
|
id: string,
|
|
body: GroupBuyingActivityInput,
|
|
): Promise<GroupBuyingActivity>;
|
|
deleteGroupBuyingActivity(id: string): Promise<void>;
|
|
listAftersales(status?: AftersaleStatus): Promise<Aftersale[]>;
|
|
getAftersale(id: string): Promise<AftersaleDetail>;
|
|
approveAftersale(id: string): Promise<Aftersale>;
|
|
rejectAftersale(id: string): Promise<Aftersale>;
|
|
confirmAftersaleReceipt(id: string): Promise<Aftersale>;
|
|
refundAftersale(id: string): Promise<Aftersale>;
|
|
addAftersaleMessage(id: string, body: AftersaleMessageBody): Promise<AftersaleMessage>;
|
|
};
|
|
admin: {
|
|
listUsers(page?: number): Promise<Paged<User>>;
|
|
setUserRole(id: string, role: string, shopId: string | null): Promise<User>;
|
|
listShops(): Promise<Shop[]>;
|
|
createShop(name: LocalizedText, slug: string): Promise<Shop>;
|
|
setShopStatus(id: string, status: "active" | "suspended"): Promise<Shop>;
|
|
listOrders(page?: number): Promise<Paged<Order>>;
|
|
listCurrencies(): Promise<Currency[]>;
|
|
upsertCurrency(body: CurrencyUpsertBody): Promise<Currency>;
|
|
setRate(code: string, rateToBase: string): Promise<Currency>;
|
|
/** Every content kind, inactive rows included. */
|
|
getContent(): Promise<HomeContent>;
|
|
/** Replaces one kind from an ordered list; positions follow the array order. */
|
|
replaceContent<K extends ContentKind>(
|
|
kind: K,
|
|
items: ContentInputByKind[K],
|
|
): Promise<HomeContent>;
|
|
listAftersales(status?: AftersaleStatus): Promise<Aftersale[]>;
|
|
getAftersale(id: string): Promise<AftersaleDetail>;
|
|
arbitrateAftersale(id: string, outcome: AftersaleArbitration): Promise<Aftersale>;
|
|
};
|
|
}
|
|
|
|
export function createApi(opts: ApiClientOptions): ApiClient {
|
|
const r = <T>(m: string, p: string, b?: unknown, q?: Query): Promise<T> =>
|
|
request<T>(opts, m, p, b, q);
|
|
return {
|
|
register: (email, password, displayName) =>
|
|
r("POST", "/auth/register", { email, password, display_name: displayName }),
|
|
login: (email, password) => r("POST", "/auth/login", { email, password }),
|
|
me: () => r("GET", "/auth/me"),
|
|
getAccountSummary: () => r("GET", "/me/stats"),
|
|
listProducts: (q = {}) => r("GET", "/products", undefined, { ...q }),
|
|
getProduct: (idOrSlug) => r("GET", `/products/${idOrSlug}`),
|
|
listCategories: () => r("GET", "/categories"),
|
|
listBrands: () => r("GET", "/brands"),
|
|
listCurrencies: () => r("GET", "/currencies"),
|
|
convert: (amountMinor, from, to) =>
|
|
r("GET", "/currencies/convert", undefined, { amount_minor: amountMinor, from, to }),
|
|
getCart: () => r("GET", "/cart"),
|
|
addCartItem: (skuId, qty) => r("POST", "/cart/items", { sku_id: skuId, qty }),
|
|
updateCartItem: (skuId, qty) => r("PUT", `/cart/items/${skuId}`, { qty }),
|
|
removeCartItem: (skuId) => r("DELETE", `/cart/items/${skuId}`),
|
|
checkout: (shippingAddress, currency, couponByShop = {}, groupBuy) =>
|
|
r("POST", "/orders/checkout", {
|
|
shipping_address: shippingAddress,
|
|
currency,
|
|
coupon_by_shop: couponByShop,
|
|
group_buy: groupBuy ?? null,
|
|
}),
|
|
listMyOrders: (page = 1) => r("GET", "/orders", undefined, { page }),
|
|
getOrder: (id) => r("GET", `/orders/${id}`),
|
|
cancelOrder: (id) => r("POST", `/orders/${id}/cancel`),
|
|
payOrder: (id) => r("POST", `/orders/${id}/pay`),
|
|
confirmDelivered: (shipmentId) => r("POST", `/shipments/${shipmentId}/confirm-delivered`),
|
|
listMyShipments: () => r("GET", "/shipments"),
|
|
requestInvoice: (orderId, title, taxNo, kind) =>
|
|
r("POST", `/orders/${orderId}/invoice`, { title, tax_no: taxNo, kind }),
|
|
listMyInvoices: () => r("GET", "/invoices"),
|
|
getHomeContent: () => r("GET", "/content/home"),
|
|
listShops: () => r("GET", "/shops"),
|
|
getShop: (slug) => r("GET", `/shops/${slug}`),
|
|
listMyAddresses: () => r("GET", "/addresses"),
|
|
createAddress: (address) => r("POST", "/addresses", address),
|
|
updateAddress: (id, address) => r("PUT", `/addresses/${id}`, address),
|
|
deleteAddress: (id) => r("DELETE", `/addresses/${id}`),
|
|
setDefaultAddress: (id) => r("POST", `/addresses/${id}/default`),
|
|
listFavorites: (q) => r("GET", "/favorites", undefined, { ...q }),
|
|
addProductFavorite: (productId) => r("PUT", `/favorites/products/${productId}`),
|
|
removeProductFavorite: (productId) => r("DELETE", `/favorites/products/${productId}`),
|
|
addShopFavorite: (shopId) => r("PUT", `/favorites/shops/${shopId}`),
|
|
removeShopFavorite: (shopId) => r("DELETE", `/favorites/shops/${shopId}`),
|
|
listShopCouponTemplates: (shopId) => r("GET", `/shops/${shopId}/coupon-templates`),
|
|
listMyCoupons: () => r("GET", "/me/coupons"),
|
|
claimCoupon: (templateId) => r("POST", "/me/coupons", { template_id: templateId }),
|
|
applyForAftersale: (body) => r("POST", "/aftersales", body),
|
|
listMyAftersales: () => r("GET", "/aftersales"),
|
|
getAftersale: (id) => r("GET", `/aftersales/${id}`),
|
|
cancelAftersale: (id) => r("POST", `/aftersales/${id}/cancel`),
|
|
reopenAftersale: (id) => r("POST", `/aftersales/${id}/reopen`),
|
|
submitAftersaleReturnTracking: (id, body) =>
|
|
r("POST", `/aftersales/${id}/return-tracking`, body),
|
|
addAftersaleMessage: (id, body) => r("POST", `/aftersales/${id}/messages`, body),
|
|
listPointsProducts: () => r("GET", "/points/products"),
|
|
listMyRedemptions: () => r("GET", "/points/redemptions"),
|
|
redeemPoints: (body) => r("POST", "/points/redemptions", body),
|
|
listFlashSales: () => r("GET", "/flash-sales"),
|
|
listGroupBuyingActivities: () => r("GET", "/group-buying/activities"),
|
|
shop: {
|
|
getMyShop: () => r("GET", "/shop/profile"),
|
|
updateMyProfile: (body) => r("PUT", "/shop/profile", body),
|
|
listMyProducts: (q = {}) => r("GET", "/shop/products", undefined, { ...q }),
|
|
getProduct: (id) => r("GET", `/shop/products/${id}`),
|
|
createProduct: (body) => r("POST", "/shop/products", body),
|
|
updateProduct: (id, body) => r("PUT", `/shop/products/${id}`, body),
|
|
publish: (id) => r("POST", `/shop/products/${id}/publish`),
|
|
unpublish: (id) => r("POST", `/shop/products/${id}/unpublish`),
|
|
upsertSku: (productId, body) => r("POST", `/shop/products/${productId}/skus`, body),
|
|
listOrders: (q = {}) => r("GET", "/shop/orders", undefined, { ...q }),
|
|
getOrder: (id) => r("GET", `/shop/orders/${id}`),
|
|
createShipment: (orderId, carrier, trackingNo, items) =>
|
|
r("POST", `/shop/orders/${orderId}/shipments`, {
|
|
carrier,
|
|
tracking_no: trackingNo,
|
|
items,
|
|
}),
|
|
listShipments: () => r("GET", "/shop/shipments"),
|
|
markShipped: (id) => r("POST", `/shop/shipments/${id}/ship`),
|
|
listInvoices: () => r("GET", "/shop/invoices"),
|
|
issueInvoice: (id) => r("POST", `/shop/invoices/${id}/issue`),
|
|
listCouponTemplates: () => r("GET", "/shop/coupon-templates"),
|
|
createCouponTemplate: (body) => r("POST", "/shop/coupon-templates", body),
|
|
updateCouponTemplate: (id, body) => r("PUT", `/shop/coupon-templates/${id}`, body),
|
|
deleteCouponTemplate: (id) => r("DELETE", `/shop/coupon-templates/${id}`),
|
|
listFlashSales: () => r("GET", "/shop/flash-sales"),
|
|
createFlashSale: (body) => r("POST", "/shop/flash-sales", body),
|
|
updateFlashSale: (id, body) => r("PUT", `/shop/flash-sales/${id}`, body),
|
|
deleteFlashSale: (id) => r("DELETE", `/shop/flash-sales/${id}`),
|
|
addFlashSaleItem: (sessionId, body) =>
|
|
r("POST", `/shop/flash-sales/${sessionId}/items`, body),
|
|
updateFlashSaleItem: (id, body) => r("PUT", `/shop/flash-sale-items/${id}`, body),
|
|
deleteFlashSaleItem: (id) => r("DELETE", `/shop/flash-sale-items/${id}`),
|
|
listGroupBuyingActivities: () => r("GET", "/shop/group-buying-activities"),
|
|
createGroupBuyingActivity: (body) => r("POST", "/shop/group-buying-activities", body),
|
|
updateGroupBuyingActivity: (id, body) =>
|
|
r("PUT", `/shop/group-buying-activities/${id}`, body),
|
|
deleteGroupBuyingActivity: (id) => r("DELETE", `/shop/group-buying-activities/${id}`),
|
|
listAftersales: (status) => r("GET", "/shop/aftersales", undefined, { status }),
|
|
getAftersale: (id) => r("GET", `/shop/aftersales/${id}`),
|
|
approveAftersale: (id) => r("POST", `/shop/aftersales/${id}/approve`),
|
|
rejectAftersale: (id) => r("POST", `/shop/aftersales/${id}/reject`),
|
|
confirmAftersaleReceipt: (id) => r("POST", `/shop/aftersales/${id}/confirm-receipt`),
|
|
refundAftersale: (id) => r("POST", `/shop/aftersales/${id}/refund`),
|
|
addAftersaleMessage: (id, body) => r("POST", `/shop/aftersales/${id}/messages`, body),
|
|
},
|
|
admin: {
|
|
listUsers: (page = 1) => r("GET", "/admin/users", undefined, { page }),
|
|
setUserRole: (id, role, shopId) =>
|
|
r("PUT", `/admin/users/${id}/role`, { role, shop_id: shopId }),
|
|
listShops: () => r("GET", "/admin/shops"),
|
|
createShop: (name, slug) => r("POST", "/admin/shops", { name, slug }),
|
|
setShopStatus: (id, status) => r("PUT", `/admin/shops/${id}/status`, { status }),
|
|
listOrders: (page = 1) => r("GET", "/admin/orders", undefined, { page }),
|
|
listCurrencies: () => r("GET", "/admin/currencies"),
|
|
upsertCurrency: (body) => r("POST", "/admin/currencies", body),
|
|
setRate: (code, rateToBase) => r("PUT", `/admin/currencies/${code}/rate`, {
|
|
rate_to_base: rateToBase,
|
|
}),
|
|
getContent: () => r("GET", "/admin/content"),
|
|
replaceContent: (kind, items) => r("PUT", `/admin/content/${kind}`, items),
|
|
setShopProfile: (id, body) => r("PUT", `/admin/shops/${id}/profile`, body),
|
|
/** Replaces the whole ordered brand list. */
|
|
getBrands: () => r("GET", "/brands"),
|
|
replaceBrands: (items) => r("PUT", "/admin/brands", items),
|
|
listAftersales: (status) => r("GET", "/admin/aftersales", undefined, { status }),
|
|
getAftersale: (id) => r("GET", `/admin/aftersales/${id}`),
|
|
arbitrateAftersale: (id, outcome) =>
|
|
r("POST", `/admin/aftersales/${id}/arbitrate`, { outcome }),
|
|
listPointsProducts: () => r("GET", "/admin/points/products"),
|
|
createPointsProduct: (body) => r("POST", "/admin/points/products", body),
|
|
updatePointsProduct: (id, body) => r("PUT", `/admin/points/products/${id}`, body),
|
|
setPointsProductPublished: (id, published) =>
|
|
r("POST", `/admin/points/products/${id}/${published ? "publish" : "unpublish"}`),
|
|
listPointsRedemptions: (page = 1) => r("GET", "/admin/points/orders", undefined, { page }),
|
|
fulfillRedemption: (id) => r("POST", `/admin/points/orders/${id}/fulfill`),
|
|
cancelRedemption: (id) => r("POST", `/admin/points/orders/${id}/cancel`),
|
|
},
|
|
};
|
|
}
|