Add store-product Excel bulk edit and polish the business dashboard.

Export/import price and stock from Store Products, keep card order after saves, localize variations, and drop unused website nav items.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-19 01:02:43 +03:30
co-authored by Cursor
parent 7b51dda82a
commit 832ac32a09
16 changed files with 617 additions and 181 deletions
+95
View File
@@ -120,3 +120,98 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
return payload as T
}
function filenameFromDisposition(header: string | null) {
if (!header) return null
const utf = header.match(/filename\*=UTF-8''([^;]+)/i)
if (utf?.[1]) {
try {
return decodeURIComponent(utf[1])
} catch {
return utf[1]
}
}
const plain = header.match(/filename="?([^"]+)"?/i)
return plain?.[1] ?? null
}
export async function apiRequestBlob(
path: string,
options: { auth?: boolean; signal?: AbortSignal } = {},
): Promise<{ blob: Blob; filename: string | null }> {
const { auth = false, signal } = options
const requestHeaders = new Headers()
if (auth) {
const accessToken = getAccessToken()
if (accessToken) {
requestHeaders.set('Authorization', `Bearer ${accessToken}`)
}
}
const response = await fetch(`${API_BASE_URL}${path}`, {
headers: requestHeaders,
signal,
})
if (response.status === 401 && auth) {
const refreshed = await refreshAccessToken()
if (refreshed) {
return apiRequestBlob(path, options)
}
}
if (!response.ok) {
const payload = await response.json().catch(() => null)
throw new ApiError(
parseErrorMessage(payload, `Request failed with status ${response.status}`),
response.status,
)
}
return {
blob: await response.blob(),
filename: filenameFromDisposition(response.headers.get('Content-Disposition')),
}
}
export async function apiRequestForm<T>(
path: string,
formData: FormData,
options: { method?: string; auth?: boolean; signal?: AbortSignal } = {},
): Promise<T> {
const { method = 'POST', auth = false, signal } = options
const requestHeaders = new Headers()
if (auth) {
const accessToken = getAccessToken()
if (accessToken) {
requestHeaders.set('Authorization', `Bearer ${accessToken}`)
}
}
const response = await fetch(`${API_BASE_URL}${path}`, {
method,
headers: requestHeaders,
body: formData,
signal,
})
if (response.status === 401 && auth) {
const refreshed = await refreshAccessToken()
if (refreshed) {
return apiRequestForm<T>(path, formData, options)
}
}
const payload = await response.json().catch(() => null)
if (!response.ok) {
throw new ApiError(
parseErrorMessage(payload, `Request failed with status ${response.status}`),
response.status,
)
}
return payload as T
}