Add the Statistics pages row for static_page views, wire specials AI match UI, and tidy Businesses page styles. Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
2.5 KiB
TypeScript
102 lines
2.5 KiB
TypeScript
import { apiRequest } from '../lib/api'
|
|
import { getActiveBusinessId } from '../lib/businessContext'
|
|
import type { StoreSpecial } from '../types/storeSpecial'
|
|
|
|
export interface StoreSpecialsListResponse {
|
|
items: StoreSpecial[]
|
|
total: number
|
|
page: number
|
|
pageSize: number
|
|
}
|
|
|
|
export interface CreateStoreSpecialPayload {
|
|
key: string
|
|
title: string
|
|
storeItemIds?: string[]
|
|
productIds?: string[]
|
|
sortOrder?: number
|
|
isActive?: boolean
|
|
}
|
|
|
|
export interface UpdateStoreSpecialPayload {
|
|
key?: string
|
|
title?: string
|
|
storeItemIds?: string[]
|
|
productIds?: string[]
|
|
sortOrder?: number
|
|
isActive?: boolean
|
|
}
|
|
|
|
function businessPath(suffix = '') {
|
|
const businessId = getActiveBusinessId()
|
|
if (!businessId) {
|
|
throw new Error('No active business selected. Please sign in again.')
|
|
}
|
|
return `/businesses/${businessId}/store-specials${suffix}`
|
|
}
|
|
|
|
export async function listStoreSpecials(page = 1, pageSize = 50, signal?: AbortSignal) {
|
|
return apiRequest<StoreSpecialsListResponse>(
|
|
`${businessPath()}?page=${page}&pageSize=${pageSize}`,
|
|
{ auth: true, signal },
|
|
)
|
|
}
|
|
|
|
export async function createStoreSpecial(payload: CreateStoreSpecialPayload) {
|
|
return apiRequest<{ message: string; special: StoreSpecial }>(businessPath(), {
|
|
method: 'POST',
|
|
auth: true,
|
|
body: payload,
|
|
})
|
|
}
|
|
|
|
export async function updateStoreSpecial(specialId: string, payload: UpdateStoreSpecialPayload) {
|
|
return apiRequest<{ message: string; special: StoreSpecial }>(businessPath(`/${specialId}`), {
|
|
method: 'PATCH',
|
|
auth: true,
|
|
body: payload,
|
|
})
|
|
}
|
|
|
|
export async function deleteStoreSpecial(specialId: string) {
|
|
return apiRequest<{ message: string }>(businessPath(`/${specialId}`), {
|
|
method: 'DELETE',
|
|
auth: true,
|
|
})
|
|
}
|
|
|
|
export interface SpecialItemsAiMatchCandidate {
|
|
storeItemId: string
|
|
productId: string
|
|
productTitle: string
|
|
productNameFa: string
|
|
productImage: string | null
|
|
variantId: string
|
|
variantLabel: string
|
|
currentPrice: number | null
|
|
score: number
|
|
}
|
|
|
|
export interface SpecialItemsAiMatchRow {
|
|
lineIndex: number
|
|
raw: string
|
|
price: number | null
|
|
selected: boolean
|
|
updatePrice: boolean
|
|
confidence: number
|
|
reason: string | null
|
|
candidate: SpecialItemsAiMatchCandidate | null
|
|
}
|
|
|
|
export async function matchStoreSpecialListWithAi(text: string, signal?: AbortSignal) {
|
|
return apiRequest<{ message: string; matches: SpecialItemsAiMatchRow[] }>(
|
|
businessPath('/ai-match'),
|
|
{
|
|
method: 'POST',
|
|
auth: true,
|
|
body: { text },
|
|
signal,
|
|
},
|
|
)
|
|
}
|