some edits
This commit is contained in:
@@ -1,12 +1,7 @@
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
|
||||
export type BreakpointName =
|
||||
| 'xs'
|
||||
| 'sm'
|
||||
| 'md'
|
||||
| 'lg'
|
||||
| 'xl'
|
||||
| '2xl'
|
||||
export type BreakpointName = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
||||
|
||||
const BREAKPOINTS = {
|
||||
xs: 0,
|
||||
@@ -27,22 +22,8 @@ function getBreakpoint(width: number): BreakpointName {
|
||||
}
|
||||
|
||||
export function useBreakpoint() {
|
||||
const width = ref(typeof window !== 'undefined' ? window.innerWidth : 1280)
|
||||
const breakpoint = ref<BreakpointName>(getBreakpoint(width.value))
|
||||
|
||||
function update() {
|
||||
width.value = window.innerWidth
|
||||
breakpoint.value = getBreakpoint(width.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
update()
|
||||
window.addEventListener('resize', update, { passive: true })
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', update)
|
||||
})
|
||||
const { width } = useWindowSize()
|
||||
const breakpoint = computed(() => getBreakpoint(width.value))
|
||||
|
||||
const isDesktop = () => breakpoint.value === 'xl' || breakpoint.value === '2xl'
|
||||
const isTablet = () => breakpoint.value === 'md' || breakpoint.value === 'lg'
|
||||
|
||||
@@ -1,68 +1,84 @@
|
||||
import { computed } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
checkDeadlines,
|
||||
dashboardStats,
|
||||
orders,
|
||||
salesChartData,
|
||||
} from '@/data/mockData'
|
||||
|
||||
export const statCards = [
|
||||
{
|
||||
label: 'سفارشهای جدید',
|
||||
value: dashboardStats.newOrders,
|
||||
icon: 'cart',
|
||||
tone: 'info',
|
||||
link: '/orders',
|
||||
},
|
||||
{
|
||||
label: 'در انتظار بررسی',
|
||||
value: dashboardStats.pendingReview,
|
||||
icon: 'file',
|
||||
tone: 'warning',
|
||||
link: '/orders?tab=pending_review',
|
||||
},
|
||||
{
|
||||
label: 'در انتظار چک',
|
||||
value: dashboardStats.pendingCheck,
|
||||
icon: 'wallet',
|
||||
tone: 'purple',
|
||||
link: '/orders?tab=pending_check',
|
||||
},
|
||||
{
|
||||
label: 'تأیید نهایی شده',
|
||||
value: dashboardStats.finalApproval,
|
||||
icon: 'check-circle',
|
||||
tone: 'success',
|
||||
link: '/orders?tab=final_approval',
|
||||
},
|
||||
] as const
|
||||
import { dashboardApi, mapAdminOrder, type DashboardStats } from '@/services/api'
|
||||
import { ApiError } from '@/services/http'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import type { CheckDeadline, Order } from '@/types'
|
||||
|
||||
export const iconBg: Record<string, string> = {
|
||||
info: '#eef3ff',
|
||||
warning: '#fff6e5',
|
||||
purple: '#ede9fe',
|
||||
success: '#eaf8f1',
|
||||
info: 'var(--color-info-light)',
|
||||
warning: 'var(--color-warning-light)',
|
||||
purple: 'var(--color-purple-light)',
|
||||
success: 'var(--color-success-light)',
|
||||
}
|
||||
|
||||
export const iconColor: Record<string, string> = {
|
||||
info: '#17379f',
|
||||
warning: '#d97706',
|
||||
purple: '#7c3aed',
|
||||
success: '#159660',
|
||||
info: 'var(--color-info)',
|
||||
warning: 'var(--color-warning-dark)',
|
||||
purple: 'var(--color-purple)',
|
||||
success: 'var(--color-success)',
|
||||
}
|
||||
|
||||
const emptyStats = (): DashboardStats => ({
|
||||
newOrders: 0,
|
||||
pendingReview: 0,
|
||||
pendingCheck: 0,
|
||||
finalApproval: 0,
|
||||
todaySales: 0,
|
||||
todaySalesGrowth: 0,
|
||||
monthSales: 0,
|
||||
})
|
||||
|
||||
export function useDashboard() {
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
|
||||
const recentOrders = computed(() => orders.slice(0, 5))
|
||||
const dashboardStats = ref<DashboardStats>(emptyStats())
|
||||
const recentOrders = ref<Order[]>([])
|
||||
const salesChartData = ref<number[]>(Array.from({ length: 28 }, () => 0))
|
||||
const chartAxisLabels = ref(['۱', '۷', '۱۴', '۲۱', '۲۸'])
|
||||
const checkDeadlines = ref<CheckDeadline[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
const statCards = computed(() => [
|
||||
{
|
||||
label: 'سفارشهای جدید',
|
||||
value: dashboardStats.value.newOrders,
|
||||
icon: 'cart',
|
||||
tone: 'info',
|
||||
link: '/orders',
|
||||
},
|
||||
{
|
||||
label: 'در انتظار بررسی',
|
||||
value: dashboardStats.value.pendingReview,
|
||||
icon: 'file',
|
||||
tone: 'warning',
|
||||
link: '/orders?tab=pending_review',
|
||||
},
|
||||
{
|
||||
label: 'در انتظار چک',
|
||||
value: dashboardStats.value.pendingCheck,
|
||||
icon: 'wallet',
|
||||
tone: 'purple',
|
||||
link: '/orders?tab=pending_check',
|
||||
},
|
||||
{
|
||||
label: 'تأیید نهایی شده',
|
||||
value: dashboardStats.value.finalApproval,
|
||||
icon: 'check-circle',
|
||||
tone: 'success',
|
||||
link: '/orders?tab=final_approval',
|
||||
},
|
||||
] as const)
|
||||
|
||||
const chartPoints = computed(() => {
|
||||
const w = 280
|
||||
const h = 120
|
||||
const max = Math.max(...salesChartData)
|
||||
const step = w / (salesChartData.length - 1)
|
||||
return salesChartData
|
||||
const data = salesChartData.value
|
||||
if (!data.length) return ''
|
||||
const max = Math.max(...data, 0.001)
|
||||
const step = data.length > 1 ? w / (data.length - 1) : w
|
||||
return data
|
||||
.map((v, i) => {
|
||||
const x = i * step
|
||||
const y = h - (v / max) * (h - 10) - 5
|
||||
@@ -71,6 +87,30 @@ export function useDashboard() {
|
||||
.join(' ')
|
||||
})
|
||||
|
||||
async function loadDashboard() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await dashboardApi.get()
|
||||
dashboardStats.value = data.stats
|
||||
recentOrders.value = (data.recentOrders ?? []).map((o) => mapAdminOrder(o) as Order)
|
||||
salesChartData.value = data.salesChartData?.length
|
||||
? data.salesChartData
|
||||
: Array.from({ length: 28 }, () => 0)
|
||||
chartAxisLabels.value = data.chartAxisLabels?.length
|
||||
? data.chartAxisLabels
|
||||
: ['۱', '۷', '۱۴', '۲۱', '۲۸']
|
||||
checkDeadlines.value = data.checkDeadlines ?? []
|
||||
} catch (err) {
|
||||
toast.error(err instanceof ApiError ? err.message : 'بارگذاری داشبورد انجام نشد.')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadDashboard()
|
||||
})
|
||||
|
||||
function goOrders() {
|
||||
router.push('/orders')
|
||||
}
|
||||
@@ -81,7 +121,10 @@ export function useDashboard() {
|
||||
recentOrders,
|
||||
chartPoints,
|
||||
salesChartData,
|
||||
chartAxisLabels: ['۱', '۷', '۱۴', '۲۱', '۲۸'],
|
||||
chartAxisLabels,
|
||||
statCards,
|
||||
loading,
|
||||
goOrders,
|
||||
loadDashboard,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { computed, inject, provide, reactive, ref, type InjectionKey } from 'vue'
|
||||
import { computed, inject, onMounted, provide, reactive, ref, type InjectionKey } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { companies, orders as initialOrders } from '@/data/mockData'
|
||||
import { generateOrderNumber } from '@/utils/format'
|
||||
import type { Order, SettlementMethod } from '@/types'
|
||||
import { companiesApi, mapAdminOrder, ordersApi } from '@/services/api'
|
||||
import { ApiError } from '@/services/http'
|
||||
import type { Company, Order, SettlementMethod } from '@/types'
|
||||
|
||||
import { useToast } from '@/composables/useToast'
|
||||
|
||||
@@ -10,7 +10,8 @@ export function useOrdersPage() {
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
|
||||
const orderList = ref<Order[]>([...initialOrders])
|
||||
const companies = ref<Company[]>([])
|
||||
const orderList = ref<Order[]>([])
|
||||
const activeTab = ref('all')
|
||||
const selectedOrderId = ref<string>('')
|
||||
const searchQuery = ref('')
|
||||
@@ -18,10 +19,11 @@ export function useOrdersPage() {
|
||||
const showDetailPanel = ref(false)
|
||||
const showDetailModal = ref(false)
|
||||
const formError = ref('')
|
||||
const saving = ref(false)
|
||||
|
||||
const emptyForm = () => ({
|
||||
customerName: '',
|
||||
companyId: companies[0]?.id ?? '',
|
||||
companyId: companies.value[0]?.id ?? '',
|
||||
settlementMethod: 'check' as SettlementMethod,
|
||||
meterage: 1000,
|
||||
customerPhone: '',
|
||||
@@ -51,6 +53,26 @@ export function useOrdersPage() {
|
||||
orderList.value.find((o) => o.id === selectedOrderId.value) ?? null,
|
||||
)
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
const [ordersData, companiesData] = await Promise.all([
|
||||
ordersApi.list(),
|
||||
companiesApi.list(),
|
||||
])
|
||||
orderList.value = ordersData.map((o) => mapAdminOrder(o) as Order)
|
||||
companies.value = companiesData
|
||||
if (!form.companyId && companiesData[0]) {
|
||||
form.companyId = companiesData[0].id
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err instanceof ApiError ? err.message : 'بارگذاری سفارشها انجام نشد.')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadData()
|
||||
})
|
||||
|
||||
function selectOrder(order: Order, openDetail = false) {
|
||||
selectedOrderId.value = order.id
|
||||
if (openDetail) {
|
||||
@@ -94,32 +116,31 @@ export function useOrdersPage() {
|
||||
formError.value = ''
|
||||
}
|
||||
|
||||
function submitAddOrder() {
|
||||
async function submitAddOrder() {
|
||||
if (!isFormValid.value) {
|
||||
formError.value = 'نام مشتری و شرکت الزامی هستند.'
|
||||
return
|
||||
}
|
||||
|
||||
const company = companies.find((c) => c.id === form.companyId)
|
||||
const newOrder: Order = {
|
||||
id: `ord-${crypto.randomUUID().slice(0, 8)}`,
|
||||
orderNumber: generateOrderNumber(),
|
||||
customerName: form.customerName.trim(),
|
||||
companyName: company?.name ?? '—',
|
||||
companyId: form.companyId,
|
||||
settlementMethod: form.settlementMethod,
|
||||
totalAmount: null,
|
||||
meterage: form.meterage,
|
||||
status: 'pending_review',
|
||||
createdAt: new Date().toISOString(),
|
||||
customerPhone: form.customerPhone.trim() || undefined,
|
||||
items: [],
|
||||
saving.value = true
|
||||
try {
|
||||
const created = await ordersApi.create({
|
||||
companyId: form.companyId,
|
||||
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
|
||||
}
|
||||
|
||||
orderList.value.unshift(newOrder)
|
||||
selectedOrderId.value = newOrder.id
|
||||
toast.success('سفارش جدید ثبت شد.')
|
||||
closeAddModal()
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -146,6 +167,7 @@ export function useOrdersPage() {
|
||||
openAddModal,
|
||||
closeAddModal,
|
||||
submitAddOrder,
|
||||
saving,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user