Files
fabric_trade_admin/src/composables/useOrdersPage.ts
T

311 lines
8.4 KiB
TypeScript

import { computed, inject, onMounted, provide, reactive, ref, watch, type InjectionKey } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { companiesApi, customersApi, mapAdminOrder, ordersApi } from '@/services/api'
import { ApiError } from '@/services/http'
import type { Company, Customer, Order, RawOrderStatus, SettlementMethod } from '@/types'
import { rawStatusLabel } from '@/utils/orderStatus'
import { useToast } from '@/composables/useToast'
export function useOrdersPage() {
const route = useRoute()
const router = useRouter()
const toast = useToast()
const companies = ref<Company[]>([])
const customers = ref<Customer[]>([])
const orderList = ref<Order[]>([])
const activeTab = ref(typeof route.query.tab === 'string' && route.query.tab.trim() ? route.query.tab.trim() : 'all')
const selectedOrderId = ref<string>('')
const searchQuery = ref('')
const showAddModal = ref(false)
const showDetailPanel = ref(false)
const showDetailModal = ref(false)
const formError = ref('')
const saving = ref(false)
const emptyForm = () => ({
customerId: '',
customerName: '',
companyId: companies.value[0]?.id ?? '',
settlementMethod: 'check' as SettlementMethod,
meterage: 1000,
customerPhone: '',
})
const form = reactive(emptyForm())
const isFormValid = computed(
() => form.customerId.length > 0 && form.companyId.length > 0,
)
const activeCustomers = computed(() => {
const list = [...customers.value]
list.sort((a, b) => Number(b.isActive) - Number(a.isActive) || a.name.localeCompare(b.name, 'fa'))
return list
})
function applyCustomer(customerId: string) {
const customer = customers.value.find((c) => c.id === customerId)
form.customerId = customerId
form.customerName = customer?.name ?? ''
form.customerPhone = customer?.phone ?? ''
}
async function loadCustomers() {
try {
customers.value = await customersApi.list()
} catch (err) {
toast.error(err instanceof ApiError ? err.message : 'بارگذاری مشتریان انجام نشد.')
}
}
const filteredOrders = computed(() => {
let list = orderList.value
if (activeTab.value !== 'all') {
list = list.filter((o) => o.status === activeTab.value)
}
if (searchQuery.value.trim()) {
const q = searchQuery.value.trim()
list = list.filter(
(o) => o.orderNumber.includes(q) || o.customerName.includes(q),
)
}
return list
})
const selectedOrder = computed(() =>
orderList.value.find((o) => o.id === selectedOrderId.value) ?? null,
)
function findOrderByRef(ref: string): Order | undefined {
const key = ref.trim()
if (!key) return undefined
return orderList.value.find(
(o) => o.id === key || o.orderNumber.toUpperCase() === key.toUpperCase(),
)
}
async function applyOrderDeepLink() {
const orderRef = typeof route.query.order === 'string' ? route.query.order : ''
if (!orderRef || !orderList.value.length) return
const order = findOrderByRef(orderRef)
if (!order) {
searchQuery.value = orderRef
return
}
const focus = typeof route.query.focus === 'string' ? route.query.focus : ''
if (focus === 'pricing') {
await router.replace(`/orders/${order.id}/pricing`)
return
}
if (focus === 'checks') {
await router.replace(`/orders/${order.id}/checks`)
return
}
openOrderDetail(order)
searchQuery.value = order.orderNumber
}
async function loadData() {
try {
const [ordersData, companiesData, customersData] = await Promise.all([
ordersApi.list(),
companiesApi.list(),
customersApi.list(),
])
orderList.value = ordersData.map((o) => mapAdminOrder(o) as Order)
companies.value = companiesData
customers.value = customersData
if (!form.companyId && companiesData[0]) {
form.companyId = companiesData[0].id
}
await applyOrderDeepLink()
} catch (err) {
toast.error(err instanceof ApiError ? err.message : 'بارگذاری سفارش‌ها انجام نشد.')
}
}
onMounted(() => {
void loadData()
})
watch(
() => [route.query.order, route.query.focus] as const,
() => {
void applyOrderDeepLink()
},
)
watch(
() => route.query.tab,
(tab) => {
if (typeof tab === 'string' && tab.trim()) {
activeTab.value = tab.trim()
}
},
)
function selectOrder(order: Order, openDetail = false) {
selectedOrderId.value = order.id
if (openDetail) {
showDetailPanel.value = true
}
}
function openOrderDetail(order: Order) {
selectedOrderId.value = order.id
showDetailModal.value = true
}
function closeDetailPanel() {
showDetailPanel.value = false
}
function closeDetailModal() {
showDetailModal.value = false
}
function settlementLabel(method?: string | null) {
if (!method) return 'انتخاب‌نشده'
return method === 'check' ? 'چکی' : 'نقدی'
}
const statusSaving = ref(false)
async function changeStatus(payload: {
orderId: string
status: RawOrderStatus
note?: string
reason?: string
force?: boolean
}) {
if (statusSaving.value) return null
statusSaving.value = true
try {
const updated = await ordersApi.updateStatus(payload.orderId, {
status: payload.status,
note: payload.note,
reason: payload.reason,
force: payload.force,
})
const mapped = mapAdminOrder(updated) as Order
const index = orderList.value.findIndex((o) => o.id === payload.orderId)
if (index >= 0) orderList.value[index] = { ...orderList.value[index]!, ...mapped }
toast.success(`وضعیت سفارش به «${rawStatusLabel(payload.status)}» تغییر کرد.`)
return mapped
} catch (err) {
toast.error(err instanceof ApiError ? err.message : 'تغییر وضعیت انجام نشد.')
return null
} finally {
statusSaving.value = false
}
}
function goPricing(order: Order) {
router.push(`/orders/${order.id}/pricing`)
}
function goChecks(order: Order) {
router.push(`/orders/${order.id}/checks`)
}
function goCashReceipt(order: Order) {
router.push({ path: '/cash-payments', query: { order: order.orderNumber } })
}
function openAddModal() {
Object.assign(form, emptyForm())
formError.value = ''
showAddModal.value = true
void loadCustomers()
}
function closeAddModal() {
showAddModal.value = false
formError.value = ''
}
async function submitAddOrder() {
if (!isFormValid.value) {
formError.value = 'انتخاب مشتری و شرکت الزامی است.'
return
}
saving.value = true
try {
const created = await ordersApi.create({
companyId: form.companyId,
customerId: form.customerId || undefined,
userId: form.customerId || undefined,
customerName: form.customerName.trim(),
customerPhone: form.customerPhone.trim() || undefined,
settlementMethod: form.settlementMethod,
meterage: form.meterage,
})
const mapped = mapAdminOrder(created) as Order
orderList.value.unshift(mapped)
selectedOrderId.value = mapped.id
toast.success('سفارش جدید ثبت شد.')
closeAddModal()
} catch (err) {
toast.error(err instanceof ApiError ? err.message : 'ثبت سفارش انجام نشد.')
} finally {
saving.value = false
}
}
return {
companies,
customers,
activeCustomers,
applyCustomer,
loadCustomers,
orderList,
activeTab,
selectedOrderId,
searchQuery,
showAddModal,
showDetailPanel,
showDetailModal,
formError,
form,
isFormValid,
filteredOrders,
selectedOrder,
selectOrder,
openOrderDetail,
closeDetailPanel,
closeDetailModal,
settlementLabel,
changeStatus,
statusSaving,
goPricing,
goChecks,
goCashReceipt,
openAddModal,
closeAddModal,
submitAddOrder,
saving,
}
}
export type OrdersPageContext = ReturnType<typeof useOrdersPage>
export const ORDERS_PAGE_KEY: InjectionKey<OrdersPageContext> = Symbol('orders-page')
export function provideOrdersPage() {
const ctx = useOrdersPage()
provide(ORDERS_PAGE_KEY, ctx)
return ctx
}
export function useOrdersPageContext() {
const ctx = inject(ORDERS_PAGE_KEY)
if (!ctx) throw new Error('Orders page context is missing')
return ctx
}