202 lines
5.2 KiB
TypeScript
202 lines
5.2 KiB
TypeScript
import { getDefaultShopSections } from "@/lib/domain/shop-template"
|
|
import { isApiError } from "@/lib/errors"
|
|
import type { Shop, ShopTemplateConfig } from "@/lib/types"
|
|
|
|
import { httpJson } from "./http"
|
|
|
|
type Paginated<T> = {
|
|
items: T[] | null
|
|
meta: {
|
|
total: number
|
|
offset: number
|
|
limit: number
|
|
}
|
|
}
|
|
|
|
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<ShopResponse>>("/api/v1/shops?offset=0&limit=100", {
|
|
cache: "no-store",
|
|
})
|
|
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 {
|
|
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
|
|
}
|
|
if (isApiError(error) && error.code === 404) {
|
|
return undefined
|
|
}
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export async function getShopByOwnerId(ownerId: string): Promise<Shop | undefined> {
|
|
try {
|
|
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
|
|
}
|
|
if (isApiError(error) && error.code === 404) {
|
|
return undefined
|
|
}
|
|
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",
|
|
})
|
|
}
|