feat(api): add shop management clients
This commit is contained in:
+14
-1
@@ -21,7 +21,20 @@ export {
|
||||
listServicesByPlayer,
|
||||
updatePlayerService,
|
||||
} from "./services"
|
||||
export { getShopById, getShopByOwnerId, listShops } from "./shops"
|
||||
export {
|
||||
addShopAnnouncement,
|
||||
deleteShopAnnouncement,
|
||||
getMyShop,
|
||||
getShopById,
|
||||
getShopByOwnerId,
|
||||
getShopIncomeStats,
|
||||
inviteShopPlayer,
|
||||
listShopInvitations,
|
||||
listShops,
|
||||
removeShopPlayer,
|
||||
updateShop,
|
||||
updateShopTemplate,
|
||||
} from "./shops"
|
||||
export {
|
||||
getWalletBalance,
|
||||
listWalletTransactions,
|
||||
|
||||
+155
-6
@@ -1,10 +1,11 @@
|
||||
import { getDefaultShopSections } from "@/lib/domain/shop-template"
|
||||
import { isApiError } from "@/lib/errors"
|
||||
import type { Shop } from "@/lib/types"
|
||||
import type { Shop, ShopTemplateConfig } from "@/lib/types"
|
||||
|
||||
import { httpJson } from "./http"
|
||||
|
||||
type Paginated<T> = {
|
||||
items: T[]
|
||||
items: T[] | null
|
||||
meta: {
|
||||
total: number
|
||||
offset: number
|
||||
@@ -12,18 +13,101 @@ type Paginated<T> = {
|
||||
}
|
||||
}
|
||||
|
||||
type ShopResponse = Omit<Shop, "announcements" | "templateConfig"> & {
|
||||
announcements?: string[] | null
|
||||
templateConfig?: ShopTemplateConfig | null
|
||||
}
|
||||
|
||||
export type ShopUpdateInput = Pick<
|
||||
Shop,
|
||||
| "name"
|
||||
| "description"
|
||||
| "commissionType"
|
||||
| "commissionValue"
|
||||
| "allowMultiShop"
|
||||
| "allowIndependentOrders"
|
||||
| "dispatchMode"
|
||||
>
|
||||
|
||||
export type ShopInvitation = {
|
||||
id: number
|
||||
shopId: number
|
||||
playerId: number
|
||||
status: string
|
||||
invitedBy: number
|
||||
createdAt: number
|
||||
respondedAt?: number
|
||||
}
|
||||
|
||||
export type ShopIncomeStats = {
|
||||
monthlyIncome: string
|
||||
pendingSettlement: string
|
||||
totalWithdrawn: string
|
||||
totalOrders: number
|
||||
completedOrders: number
|
||||
}
|
||||
|
||||
type ShopInvitationList = {
|
||||
items: ShopInvitation[] | null
|
||||
}
|
||||
|
||||
function normalizeAnnouncement(value: string): string {
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed.startsWith('"') || !trimmed.endsWith('"')) return value
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as unknown
|
||||
return typeof parsed === "string" ? parsed : value
|
||||
} catch {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeShop(shop: ShopResponse): Shop {
|
||||
const templateConfig = shop.templateConfig ?? { sections: getDefaultShopSections() }
|
||||
|
||||
return {
|
||||
...shop,
|
||||
announcements: (shop.announcements ?? []).map(normalizeAnnouncement),
|
||||
templateConfig: {
|
||||
...templateConfig,
|
||||
sections: Array.isArray(templateConfig.sections)
|
||||
? templateConfig.sections
|
||||
: getDefaultShopSections(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function listShops(): Promise<Shop[]> {
|
||||
const res = await httpJson<Paginated<Shop>>("/api/v1/shops?offset=0&limit=100", {
|
||||
const res = await httpJson<Paginated<ShopResponse>>("/api/v1/shops?offset=0&limit=100", {
|
||||
cache: "no-store",
|
||||
})
|
||||
return res.items
|
||||
return (res.items ?? []).map(normalizeShop)
|
||||
}
|
||||
|
||||
export async function getMyShop(): Promise<Shop | undefined> {
|
||||
try {
|
||||
const shop = await httpJson<ShopResponse>("/api/v1/shops/mine", {
|
||||
cache: "no-store",
|
||||
})
|
||||
return normalizeShop(shop)
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
||||
throw error
|
||||
}
|
||||
if (isApiError(error) && error.code === 404) {
|
||||
return undefined
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function getShopById(shopId: string): Promise<Shop | undefined> {
|
||||
try {
|
||||
return await httpJson<Shop>(`/api/v1/shops/${encodeURIComponent(shopId)}`, {
|
||||
const shop = await httpJson<ShopResponse>(`/api/v1/shops/${encodeURIComponent(shopId)}`, {
|
||||
cache: "no-store",
|
||||
})
|
||||
return normalizeShop(shop)
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
||||
throw error
|
||||
@@ -37,9 +121,10 @@ export async function getShopById(shopId: string): Promise<Shop | undefined> {
|
||||
|
||||
export async function getShopByOwnerId(ownerId: string): Promise<Shop | undefined> {
|
||||
try {
|
||||
return await httpJson<Shop>(`/api/v1/users/${encodeURIComponent(ownerId)}/shop`, {
|
||||
const shop = await httpJson<ShopResponse>(`/api/v1/users/${encodeURIComponent(ownerId)}/shop`, {
|
||||
cache: "no-store",
|
||||
})
|
||||
return normalizeShop(shop)
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
||||
throw error
|
||||
@@ -50,3 +135,67 @@ export async function getShopByOwnerId(ownerId: string): Promise<Shop | undefine
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateShop(shopId: string, input: ShopUpdateInput): Promise<Shop> {
|
||||
const shop = await httpJson<ShopResponse>(`/api/v1/shops/${encodeURIComponent(shopId)}`, {
|
||||
method: "PUT",
|
||||
json: input,
|
||||
})
|
||||
return normalizeShop(shop)
|
||||
}
|
||||
|
||||
export async function updateShopTemplate(
|
||||
shopId: string,
|
||||
templateConfig: ShopTemplateConfig,
|
||||
): Promise<void> {
|
||||
await httpJson<unknown>(`/api/v1/shops/${encodeURIComponent(shopId)}/template`, {
|
||||
method: "PUT",
|
||||
json: {
|
||||
sections: JSON.stringify(templateConfig),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function addShopAnnouncement(shopId: string, content: string): Promise<void> {
|
||||
await httpJson<unknown>(`/api/v1/shops/${encodeURIComponent(shopId)}/announcements`, {
|
||||
method: "POST",
|
||||
json: { content },
|
||||
})
|
||||
}
|
||||
|
||||
export async function deleteShopAnnouncement(shopId: string, index: number): Promise<void> {
|
||||
await httpJson<unknown>(
|
||||
`/api/v1/shops/${encodeURIComponent(shopId)}/announcements/${encodeURIComponent(String(index))}`,
|
||||
{ method: "DELETE" },
|
||||
)
|
||||
}
|
||||
|
||||
export async function inviteShopPlayer(shopId: string, playerId: string): Promise<void> {
|
||||
await httpJson<unknown>(`/api/v1/shops/${encodeURIComponent(shopId)}/invitations`, {
|
||||
method: "POST",
|
||||
json: {
|
||||
playerId: Number(playerId),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function listShopInvitations(shopId: string): Promise<ShopInvitation[]> {
|
||||
const res = await httpJson<ShopInvitationList>(
|
||||
`/api/v1/shops/${encodeURIComponent(shopId)}/invitations`,
|
||||
{ cache: "no-store" },
|
||||
)
|
||||
return res.items ?? []
|
||||
}
|
||||
|
||||
export async function removeShopPlayer(shopId: string, playerId: string): Promise<void> {
|
||||
await httpJson<unknown>(
|
||||
`/api/v1/shops/${encodeURIComponent(shopId)}/players/${encodeURIComponent(playerId)}`,
|
||||
{ method: "DELETE" },
|
||||
)
|
||||
}
|
||||
|
||||
export async function getShopIncomeStats(shopId: string): Promise<ShopIncomeStats> {
|
||||
return httpJson<ShopIncomeStats>(`/api/v1/shops/${encodeURIComponent(shopId)}/income-stats`, {
|
||||
cache: "no-store",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { Shop, ShopSection } from "@/lib/types"
|
||||
|
||||
const defaultSectionTypes: ShopSection["type"][] = [
|
||||
"banner",
|
||||
"intro",
|
||||
"services",
|
||||
"players",
|
||||
"announcements",
|
||||
"reviews",
|
||||
]
|
||||
|
||||
export function getDefaultShopSections(): ShopSection[] {
|
||||
return defaultSectionTypes.map((type, order) => ({
|
||||
type,
|
||||
enabled: true,
|
||||
order,
|
||||
}))
|
||||
}
|
||||
|
||||
export function getShopSections(shop: Pick<Shop, "templateConfig">): ShopSection[] {
|
||||
const sections = shop.templateConfig.sections
|
||||
if (!Array.isArray(sections) || sections.length === 0) return getDefaultShopSections()
|
||||
return [...sections].sort((a, b) => a.order - b.order)
|
||||
}
|
||||
Reference in New Issue
Block a user