Add shipping cost management and checkout shipping fees.

Business store shipping groups/static rates UI, and customer checkout that quotes and applies shipping on delivery and payment.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-02 21:52:47 +03:30
co-authored by Cursor
parent 95390933a7
commit c59ca4444f
38 changed files with 5353 additions and 98 deletions
@@ -62,6 +62,14 @@ export interface StoreSettings {
torobEnabled: boolean
orderProcessSteps: OrderProcessStep[]
ePayment: EPaymentSettings
staticShippingEnabled?: boolean
staticShippingAmount?: number
staticShippingProvinceExceptions?: StaticShippingProvinceException[]
}
export interface StaticShippingProvinceException {
provinceId: string
amount: number
}
export const DEFAULT_EPAYMENT_SETTINGS: EPaymentSettings = {
@@ -0,0 +1,92 @@
import { apiRequest } from '../lib/api'
import { getActiveBusinessId } from '../lib/businessContext'
import type {
ShippingCostGroup,
ShippingCostGroupFormPayload,
ShippingCostGroupsListResponse,
} from '../types/shippingCostGroup'
function businessPath(suffix = '') {
const businessId = getActiveBusinessId()
if (!businessId) {
throw new Error('No active business selected. Please sign in again.')
}
return `/businesses/${businessId}/shipping-cost-groups${suffix}`
}
export async function listShippingCostGroups(
page = 1,
pageSize = 100,
signal?: AbortSignal,
) {
const params = new URLSearchParams({
page: String(page),
pageSize: String(pageSize),
})
return apiRequest<ShippingCostGroupsListResponse>(
`${businessPath()}?${params.toString()}`,
{ auth: true, signal },
)
}
export async function listAllShippingCostGroups(signal?: AbortSignal) {
const pageSize = 100
let page = 1
let items: ShippingCostGroup[] = []
let total = Infinity
while (items.length < total) {
const data = await listShippingCostGroups(page, pageSize, signal)
items = items.concat(data.items)
total = data.total
page += 1
}
return items
}
export async function getShippingCostGroup(groupId: string, signal?: AbortSignal) {
return apiRequest<{ group: ShippingCostGroup }>(businessPath(`/${groupId}`), {
auth: true,
signal,
})
}
export async function createShippingCostGroup(payload: ShippingCostGroupFormPayload) {
return apiRequest<{ message: string; group: ShippingCostGroup }>(businessPath(), {
method: 'POST',
auth: true,
body: payload,
})
}
export async function updateShippingCostGroup(
groupId: string,
payload: Partial<ShippingCostGroupFormPayload>,
) {
return apiRequest<{ message: string; group: ShippingCostGroup }>(
businessPath(`/${groupId}`),
{
method: 'PATCH',
auth: true,
body: payload,
},
)
}
export function getGroupProvinceExceptions(group: ShippingCostGroup) {
if (group.provinceExceptions?.length) return group.provinceExceptions
return (group.cityRates ?? []).map((rate) => ({
id: rate.id,
provinceId: rate.cityId,
amount: rate.amount,
}))
}
export async function deleteShippingCostGroup(groupId: string) {
return apiRequest<{ message: string }>(businessPath(`/${groupId}`), {
method: 'DELETE',
auth: true,
})
}