import { apiRequest } from '@/services/http' import type { AdminNotification, AdminUser, BankAccount, CashPayment, Catalog, Check, CheckDeadline, Company, Customer, InternalMessage, Order, OrderItem, OrderStatus, OrderStatusHistoryItem, Product, RawOrderStatus, SettlementMethod, Shipment, ShipmentStatus, UserRole, } from '@/types' /** Normalize admin list order status badges */ export function mapAdminOrder< T extends { status: string adminStatus?: string allowedTransitions?: string[] settlementMethod?: string | null settlementType?: string | null meterage?: number totalMeterage?: number }, >( order: T, ): T & { status: OrderStatus rawStatus: RawOrderStatus allowedTransitions: RawOrderStatus[] settlementMethod: SettlementMethod | null meterage: number } { return { ...order, status: (order.adminStatus ?? order.status) as OrderStatus, rawStatus: order.status as RawOrderStatus, allowedTransitions: (order.allowedTransitions ?? []) as RawOrderStatus[], settlementMethod: (order.settlementMethod ?? order.settlementType ?? null) as SettlementMethod | null, meterage: order.meterage ?? order.totalMeterage ?? 0, } } export function slugifyCompanyName(name: string): string { const slug = name .trim() .toLowerCase() .replace(/\s+/g, '-') .replace(/[^a-z0-9-]/g, '') .replace(/-+/g, '-') .replace(/^-|-$/g, '') return slug || `c-${Date.now()}` } /* ── Companies ─────────────────────────────────────────────── */ export type CreateCompanyPayload = { name: string slug: string description?: string logoUrl?: string | null displayOrder?: number isActive?: boolean } export type UpdateCompanyPayload = Partial> & { slug?: string } export const companiesApi = { list: (activeOnly?: boolean) => apiRequest('/companies', { query: { activeOnly: activeOnly ? 'true' : undefined } }), get: (id: string) => apiRequest(`/companies/${id}`), create: (body: CreateCompanyPayload) => apiRequest('/companies', { body }), update: (id: string, body: UpdateCompanyPayload) => apiRequest(`/companies/${id}`, { method: 'PATCH', body }), setActive: (id: string, isActive: boolean) => apiRequest(`/companies/${id}/active`, { method: 'PATCH', body: { isActive } }), } /* ── Bank accounts ─────────────────────────────────────────── */ export type CreateBankAccountPayload = { bankName: string accountHolder: string accountNumber: string cardNumber: string iban: string displayOrder?: number isActive?: boolean notes?: string } export type UpdateBankAccountPayload = Partial export const bankAccountsApi = { listByCompany: (companyId: string) => apiRequest(`/companies/${companyId}/bank-accounts`), create: (companyId: string, body: CreateBankAccountPayload) => apiRequest(`/companies/${companyId}/bank-accounts`, { body }), update: (id: string, body: UpdateBankAccountPayload) => apiRequest(`/bank-accounts/${id}`, { method: 'PATCH', body }), setActive: (id: string, isActive: boolean) => apiRequest(`/bank-accounts/${id}/active`, { method: 'PATCH', body: { isActive } }), } /* ── Catalogs ──────────────────────────────────────────────── */ export type CreateCatalogPayload = { companyId: string name: string code: string description?: string displayOrder?: number isActive?: boolean } export type UpdateCatalogPayload = Partial> export const catalogsApi = { list: (params?: { companyId?: string; activeOnly?: boolean }) => apiRequest('/catalogs', { query: { companyId: params?.companyId, activeOnly: params?.activeOnly ? 'true' : undefined, }, }), create: (body: CreateCatalogPayload) => apiRequest('/catalogs', { body }), update: (id: string, body: UpdateCatalogPayload) => apiRequest(`/catalogs/${id}`, { method: 'PATCH', body }), setActive: (id: string, isActive: boolean) => apiRequest(`/catalogs/${id}/active`, { method: 'PATCH', body: { isActive } }), } /* ── Products ──────────────────────────────────────────────── */ export type CreateProductPayload = { companyId: string catalogId: string name: string code?: string productCode?: string color: string minMeterage?: number orderStep?: number orderMultiplier?: number price?: number | null basePrice?: number | null internalStock?: number | null stock?: number | null isActive?: boolean description?: string usageCategory?: string season?: string composition?: string | null weaveType?: string | null subtitle?: string | null rollMeters?: number widthCm?: number | null weightGrams?: number | null isNew?: boolean colorsJson?: string[] imageUrlsJson?: string[] tagsJson?: string[] } export type UpdateProductPayload = Partial> export const productsApi = { list: (params?: { catalogId?: string; companyId?: string; activeOnly?: boolean }) => apiRequest('/products', { query: { catalogId: params?.catalogId, companyId: params?.companyId, activeOnly: params?.activeOnly ? 'true' : undefined, }, }), create: (body: CreateProductPayload) => apiRequest('/products', { body }), update: (id: string, body: UpdateProductPayload) => apiRequest(`/products/${id}`, { method: 'PATCH', body }), setActive: (id: string, isActive: boolean) => apiRequest(`/products/${id}/active`, { method: 'PATCH', body: { isActive } }), } /* ── Customers ─────────────────────────────────────────────── */ export type CreateCustomerPayload = { name?: string firstName?: string lastName?: string phone: string nationalId?: string customerType?: Customer['customerType'] companyId?: string | null isActive?: boolean levelDefId?: string | null assignedAgentId?: string | null appRole?: 'customer' | 'agent' agentCode?: string | null brandIds?: string[] } export type UpdateCustomerPayload = Partial export const customersApi = { list: (q?: string, appRole?: 'customer' | 'agent') => apiRequest('/admin/customers', { query: { q, appRole } }), create: (body: CreateCustomerPayload) => apiRequest('/admin/customers', { body }), update: (id: string, body: UpdateCustomerPayload) => apiRequest(`/admin/customers/${id}`, { method: 'PATCH', body }), setActive: (id: string, isActive: boolean) => apiRequest(`/admin/customers/${id}/active`, { method: 'PATCH', body: { isActive } }), } /* ── Orders ────────────────────────────────────────────────── */ export type AdminCreateOrderPayload = { companyId?: string customerId?: string customerName?: string customerPhone?: string userId?: string settlementMethod?: SettlementMethod settlementType?: SettlementMethod meterage?: number notes?: string items?: Array<{ productId: string; meterage: number; notes?: string }> } export type OrderItemPricingPayload = { id?: string productId?: string requestedMeterage?: number confirmedMeterage?: number unitPrice?: number supplyPrice?: number } export type UpdateOrderPricingPayload = { items: OrderItemPricingPayload[] shippingCost?: number note?: string deletedItemIds?: string[] } export type ApiOrder = Order & { adminStatus?: string settlementType?: SettlementMethod | null totalMeterage?: number shippingCost?: number | null finalAmount?: number | null checkRassDays?: number | null checkRassDate?: string items?: OrderItem[] checks?: Check[] statusHistory?: OrderStatusHistoryItem[] /** وضعیت‌های مجاز بعدی از دید ماشین وضعیت سرور */ allowedTransitions?: string[] cashReceipt?: { id: string } | null } export type UpdateOrderStatusPayload = { status: string note?: string reason?: string /** گذر خارج از ماشین وضعیت؛ سرور reason را اجباری می‌کند */ force?: boolean } export const ordersApi = { list: (params?: { status?: string; q?: string }) => apiRequest('/admin/orders', { query: { status: params?.status, q: params?.q } }), get: (id: string) => apiRequest(`/admin/orders/${id}`), create: (body: AdminCreateOrderPayload) => apiRequest('/admin/orders', { body }), updatePricing: (id: string, body: UpdateOrderPricingPayload) => apiRequest(`/admin/orders/${id}/pricing`, { method: 'PATCH', body }), updateStatus: (id: string, body: UpdateOrderStatusPayload) => apiRequest(`/admin/orders/${id}/status`, { method: 'PATCH', body }), } /* ── Checks ────────────────────────────────────────────────── */ export const checksApi = { list: (status?: Check['status']) => apiRequest('/admin/checks', { query: { status } }), listByOrder: (orderId: string) => apiRequest(`/admin/orders/${orderId}/checks`), updateStatus: (id: string, body: { status: Check['status']; note?: string }) => apiRequest(`/admin/checks/${id}/status`, { method: 'PATCH', body }), } /* ── Cash payments ─────────────────────────────────────────── */ export const cashPaymentsApi = { list: (status?: CashPayment['status']) => apiRequest('/admin/cash-payments', { query: { status } }), updateStatus: (id: string, body: { status: CashPayment['status']; note?: string }) => apiRequest(`/admin/cash-payments/${id}/status`, { method: 'PATCH', body }), } /* ── Shipments ─────────────────────────────────────────────── */ export type CreateShipmentPayload = { orderId: string carrier: string trackingNumber?: string meterage?: number status?: ShipmentStatus } export const shipmentsApi = { list: (status?: ShipmentStatus) => apiRequest('/admin/shipments', { query: { status } }), create: (body: CreateShipmentPayload) => apiRequest('/admin/shipments', { body }), update: (id: string, body: Partial) => apiRequest(`/admin/shipments/${id}`, { method: 'PATCH', body }), advanceStatus: (id: string, body?: { status?: ShipmentStatus; trackingNumber?: string }) => apiRequest(`/admin/shipments/${id}/status`, { method: 'PATCH', body: body ?? {} }), } /* ── Admin users ───────────────────────────────────────────── */ export type CreateAdminUserPayload = { fullName: string phone: string email?: string password?: string role?: UserRole isActive?: boolean } export type UpdateAdminUserPayload = Partial export const adminUsersApi = { list: () => apiRequest('/admin/users'), create: (body: CreateAdminUserPayload) => apiRequest('/admin/users', { body }), update: (id: string, body: UpdateAdminUserPayload) => apiRequest(`/admin/users/${id}`, { method: 'PATCH', body }), setActive: (id: string, isActive: boolean) => apiRequest(`/admin/users/${id}/active`, { method: 'PATCH', body: { isActive } }), } /* ── Dashboard & reports ───────────────────────────────────── */ export type DashboardStats = { newOrders: number pendingReview: number pendingCheck: number finalApproval: number todaySales: number todaySalesGrowth: number monthSales: number } export type DashboardResponse = { stats: DashboardStats recentOrders: ApiOrder[] salesChartData: number[] chartAxisLabels: string[] checkDeadlines: CheckDeadline[] notifications?: AdminNotification[] messages?: InternalMessage[] } export type ReportsResponse = { reportSalesByCompany: { companyName: string orderCount: number totalAmount: number meterage: number }[] reportOrdersByStatus: { status: string; count: number }[] reportSalesByMonth?: { month: string orderCount: number totalAmount: number meterage: number }[] reportSalesByProduct?: { productId: string productName: string productCode: string orderCount: number meterage: number totalAmount: number }[] reportSalesByCustomer?: { customerName: string orderCount: number totalAmount: number meterage: number }[] reportCustomers?: { active: number inactive: number newLast30Days: number noPurchase: number noPurchase90Days: number withAgent: number } } export const dashboardApi = { get: () => apiRequest('/admin/dashboard'), reports: () => apiRequest('/admin/reports'), } /* ── System settings ──────────────────────────────────────── */ export type SystemSettings = { id: string maxMeteragePerInvoice: number cashInvoiceValidityHours: number defaultSettlementCheck: boolean defaultSettlementCash: boolean minOrderRollsPerProduct?: number showCatalogPublicPrice?: boolean allowAgentPricing?: boolean agentMinUnitPrice?: number | null smsOrderRegistered: boolean smsPricingReady: boolean smsPaymentReminder: boolean smsCheckReview: boolean smsShippingUpdate: boolean supportPhone: string | null supportEmail: string | null categoryImagesJson?: Record updatedAt: string } export type UpdateSettingsPayload = Partial> export const settingsApi = { get: () => apiRequest('/admin/settings'), update: (body: UpdateSettingsPayload) => apiRequest('/admin/settings', { method: 'PATCH', body }), } /* ── Admin notifications ──────────────────────────────────── */ export const notificationsApi = { markRead: (id: string) => apiRequest<{ ok: boolean }>(`/admin/notifications/${id}/read`, { method: 'PATCH' }), markAllRead: () => apiRequest<{ ok: boolean }>('/admin/notifications/read-all', { method: 'PATCH' }), broadcast: (body: { title: string; body: string; customerIds?: string[]; tone?: string }) => apiRequest<{ sent: number }>('/admin/notifications/broadcast', { method: 'POST', body }), } /* ── Catalog requests (physical) ──────────────────────────── */ export type CatalogRequestRow = { id: string customerId: string customerName: string customerPhone: string status: string note?: string createdAt: string items: { id: string productId: string productName: string productCode: string quantity: number holderType: string holderCustomerId?: string | null movements?: { id: string fromHolder: string toHolder: string note?: string movedAt: string }[] }[] } export const catalogRequestsApi = { list: (status?: string) => apiRequest('/admin/catalog-requests', { query: { status }, }), updateStatus: (id: string, status: string) => apiRequest(`/admin/catalog-requests/${id}/status`, { method: 'PATCH', body: { status }, }), updateHolder: ( itemId: string, body: { holderType: string; holderCustomerId?: string | null; note?: string }, ) => apiRequest(`/admin/catalog-request-items/${itemId}/holder`, { method: 'PATCH', body, }), } /* ── Brands ───────────────────────────────────────────────── */ export type Brand = { id: string name: string slug?: string description?: string isActive: boolean displayOrder: number customersCount?: number } export type AppBanner = { id: string slot: string title: string subtitle: string ctaLabel: string ctaLink: string imageUrl: string displayOrder: number isActive: boolean } export type BannerPayload = { slot: string title: string subtitle?: string | null ctaLabel?: string | null ctaLink?: string | null imageUrl?: string | null displayOrder?: number isActive?: boolean } export const bannersApi = { list: (slot?: string) => apiRequest('/admin/banners', { query: { slot } }), create: (body: BannerPayload) => apiRequest('/admin/banners', { body }), update: (id: string, body: Partial) => apiRequest(`/admin/banners/${id}`, { method: 'PATCH', body }), remove: (id: string) => apiRequest<{ ok: boolean }>(`/admin/banners/${id}`, { method: 'DELETE' }), } export const brandsApi = { list: () => apiRequest('/admin/brands'), create: (body: Partial & { name: string }) => apiRequest('/admin/brands', { body }), update: (id: string, body: Partial) => apiRequest(`/admin/brands/${id}`, { method: 'PATCH', body }), } /* ── Levels ───────────────────────────────────────────────── */ export type CustomerLevel = { id: string code: string label: string sortOrder: number isActive: boolean customersCount: number benefits: { id?: string; title: string; description?: string }[] } export const levelsApi = { list: () => apiRequest('/admin/levels'), create: (body: { code: string label: string sortOrder?: number benefits?: { title: string; description?: string }[] }) => apiRequest('/admin/levels', { body }), update: (id: string, body: Partial & { benefits?: { title: string; description?: string }[] }) => apiRequest(`/admin/levels/${id}`, { method: 'PATCH', body }), } /* ── Suggestions ──────────────────────────────────────────── */ export type ProductSuggestion = { id: string customerId: string customerName: string productId: string productName: string productCode: string note?: string isActive: boolean createdAt: string } export const suggestionsApi = { list: (customerId?: string) => apiRequest('/admin/suggestions', { query: { customerId } }), create: (body: { customerId: string; productId: string; note?: string }) => apiRequest('/admin/suggestions', { body }), setActive: (id: string, isActive: boolean) => apiRequest(`/admin/suggestions/${id}/active`, { method: 'PATCH', body: { isActive }, }), } /* ── Stock alerts ─────────────────────────────────────────── */ export type StockAlert = { id: string customerName: string customerPhone: string productName: string productCode: string stock: number | null active: boolean createdAt: string } export const stockAlertsApi = { list: () => apiRequest('/admin/stock-alerts'), notify: (id: string) => apiRequest(`/admin/stock-alerts/${id}/notify`, { method: 'PATCH' }), }