some edits

This commit is contained in:
2026-08-06 17:12:28 +03:30
parent 4411443fba
commit 43ebebd8c4
41 changed files with 5887 additions and 865 deletions
+5 -24
View File
@@ -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'
+94 -51
View File
@@ -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,
}
}
+48 -26
View File
@@ -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,
}
}