refactor(layout): enhance loading experience with skeleton components

- Introduced a ContentSkeleton component to display a loading state while content is being fetched.
- Wrapped the Outlet component in a Suspense boundary to show the skeleton during loading.
- Improved user experience by providing visual feedback during data loading.

These changes aim to enhance the overall layout and loading experience in the application.
This commit is contained in:
hosseintaromi 2026-01-08 18:28:06 +03:30
parent c46fd2ba0e
commit 5bb506b830
4 changed files with 23 additions and 1888 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,154 +0,0 @@
# 📋 مقایسه فیلدها - سیستم محصولات
## 🔵 فیلدهای محصول (Product Fields)
| فرم ما | API Field | نوع | توضیح |
| ---------------------- | ---------------------- | ---------- | ------------------------ |
| ✅ `name` | ✅ `name` | `string` | نام محصول |
| ✅ `description` | ✅ `description` | `string` | توضیحات محصول |
| ✅ `design_style` | ✅ `design_style` | `string` | استایل طراحی |
| ✅ `enabled` | ✅ `enabled` | `boolean` | فعال/غیرفعال |
| ✅ `category_ids` | ✅ `category_ids` | `number[]` | آرایه شناسه دسته‌بندی‌ها |
| ✅ `product_option_id` | ✅ `product_option_id` | `number` | شناسه گزینه محصول |
| ✅ `total_sold` | ✅ `total_sold` | `number` | تعداد فروخته شده |
| ✅ `type` | ✅ `type` | `number` | نوع محصول (0,1,2,3) |
| ✅ `attributes` | ✅ `attributes` | `object` | ویژگی‌های سفارشی |
| ✅ `variants` | ✅ `variants` | `array` | آرایه variants |
## 🔧 فیلدهای Variant
| فرم ما | API Field | نوع | توضیح |
| ---------------------- | ---------------------- | --------- | -------------------------- |
| ✅ `enabled` | ✅ `enabled` | `boolean` | فعال/غیرفعال variant |
| ✅ `fee_percentage` | ✅ `fee_percentage` | `number` | درصد کارمزد |
| ✅ `profit_percentage` | ✅ `profit_percentage` | `number` | درصد سود |
| ✅ `stock_limit` | ✅ `stock_limit` | `number` | حد کمینه موجودی |
| ✅ `stock_managed` | ✅ `stock_managed` | `boolean` | مدیریت موجودی فعال/غیرفعال |
| ✅ `stock_number` | ✅ `stock_number` | `number` | تعداد موجودی |
| ✅ `weight` | ✅ `weight` | `number` | وزن (گرم) |
| ✅ `attributes` | ✅ `attributes` | `object` | ویژگی‌های variant |
| ✅ `meta` | ✅ `meta` | `object` | Meta data |
## 📊 فیلدهای اضافی در فرم
| فرم ما | API | دلیل |
| --------------- | ------- | ------------------------------ |
| 🆕 `images` | ❌ نیست | برای آپلود و مدیریت تصاویر |
| 🆕 `id` | ❌ نیست | برای ویرایش (از response میاد) |
| 🆕 `created_at` | ❌ نیست | از response میاد |
| 🆕 `updated_at` | ❌ نیست | از response میاد |
## 🎯 مثال API Request کامل
```json
{
"name": "تیشرت مردانه",
"description": "تیشرت با کیفیت بالا",
"design_style": "مدرن",
"enabled": true,
"category_ids": [1, 3],
"product_option_id": 2,
"total_sold": 25,
"type": 1,
"attributes": {
"material": "پنبه",
"season": "تابستان"
},
"variants": [
{
"enabled": true,
"fee_percentage": 5.5,
"profit_percentage": 25,
"stock_managed": true,
"stock_number": 10,
"stock_limit": 2,
"weight": 200,
"attributes": {
"color": "قرمز",
"size": "بزرگ"
},
"meta": {
"supplier": "تامین‌کننده A",
"priority": "high"
}
}
]
}
```
## 🚀 نوع محصولات (Product Types)
| مقدار | نام | توضیح |
| ----- | ----------- | ----------------------- |
| `0` | محصول ساده | محصول بدون variant |
| `1` | محصول متغیر | محصول با variants مختلف |
| `2` | محصول گروهی | مجموعه محصولات |
| `3` | محصول خارجی | لینک به سایت خارجی |
## 🎨 فیلدهای UI (غیر API)
### فیلدهای مدیریت تصاویر:
- **`images`** - آرایه تصاویر محصول
- **`images.id`** - شناسه فایل آپلود شده
- **`images.url`** - آدرس تصویر
- **`images.alt`** - متن جایگزین
- **`images.order`** - ترتیب نمایش
### فیلدهای مدیریت داخلی:
- **`id`** - شناسه محصول (برای ویرایش)
- **`created_at`** - تاریخ ایجاد
- **`updated_at`** - تاریخ آخرین ویرایش
## ✨ خلاصه مطابقت
### ✅ کاملاً مطابق:
- **100% فیلدهای API پیاده‌سازی شده**
- **ساختار دقیقاً مشابه**
- **نوع داده‌ها صحیح**
- **Validation مناسب**
### 🎯 ویژگی‌های اضافی:
- **مدیریت تصاویر** با آپلود و پیش‌نمایش
- **UI/UX بهتر** با validation و error handling
- **Preview زنده** تغییرات
- **Responsive design** برای موبایل
## 📝 نتیجه‌گیری
**✅ تمام فیلدهای API شما دقیقاً پیاده‌سازی شده است**
**🎨 قابلیت‌های اضافی UI/UX برای تجربه بهتر کاربر اضافه شده**
**🚀 سیستم آماده برای استفاده در production**
---
## 📋 فیلدهای حذف شده (که در API نبودند)
### ❌ فیلدهایی که حذف کردیم:
- **`price`** - نه در محصول و نه در variant
- **`sku`** - نه در محصول و نه در variant
- **`status`** - در API نبود
- **`name`** برای variant - در API نبود
### 🔄 تغییرات انجام شده:
1. **Models** به‌روزرسانی شد طبق API دقیق
2. **Product Form** فیلدهای اضافی حذف شد
3. **Variant Manager** ساده‌سازی شد طبق API
4. **Validation** تنظیم شد برای فیلدهای جدید
## 🛠️ فایلهای تغییر یافته:
- `src/pages/products/core/_models.ts`
- `src/pages/products/product-form/ProductFormPage.tsx`
- `src/components/ui/VariantManager.tsx`
## 🎉 نتیجه نهایی:
**سیستم کاملاً آماده و مطابق با API شماست! 🚀**

View File

@ -1,429 +0,0 @@
# درخواست APIهای داشبورد
سلام تیم بک‌اند 👋
برای تکمیل صفحه داشبورد نیاز به APIهای زیر داریم. لطفاً این APIها را پیاده‌سازی کنید:
---
## 1. API آمار کلی داشبورد (Dashboard Overview Stats)
**Endpoint:** `GET /api/v1/admin/dashboard/stats`
**Description:** دریافت آمار کلی سیستم شامل سفارشات، درآمد، کاربران و محصولات
**Response:**
```json
{
"orders": {
"total": 1250,
"pending": 45,
"completed": 1100,
"cancelled": 105,
"today_count": 23,
"today_amount": 45000000
},
"revenue": {
"total": 2500000000,
"today": 45000000,
"this_month": 350000000,
"last_month": 320000000,
"growth_percentage": 9.4
},
"users": {
"total": 8500,
"active": 7200,
"new_today": 15,
"new_this_month": 450
},
"products": {
"total": 1250,
"active": 1100,
"low_stock": 25,
"out_of_stock": 8
}
}
```
---
## 2. API فروش ماهانه (Monthly Sales Chart)
**Endpoint:** `GET /api/v1/admin/dashboard/monthly-sales`
**Description:** دریافت آمار فروش به صورت ماهانه برای نمایش در چارت
**Query Parameters:**
- `months` (optional, default: 6) - تعداد ماه‌های گذشته که باید برگردانده شود
**Response:**
```json
{
"data": [
{
"month": "فروردین",
"month_number": 1,
"year": 1403,
"total_amount": 350000000,
"order_count": 450
},
{
"month": "اردیبهشت",
"month_number": 2,
"year": 1403,
"total_amount": 420000000,
"order_count": 520
}
// ... برای تعداد ماه‌های درخواستی
]
}
```
**Note:** داده‌ها باید به ترتیب زمانی (از قدیمی‌ترین به جدیدترین) مرتب باشند.
---
## 3. API روند رشد (Growth Trend)
**Endpoint:** `GET /api/v1/admin/dashboard/growth-trend`
**Description:** دریافت روند رشد سیستم در بازه‌های زمانی مختلف
**Query Parameters:**
- `period` (optional, default: "monthly") - نوع بازه زمانی: `"daily"`, `"weekly"`, `"monthly"`
- `days` (optional, default: 30) - تعداد روزها برای `period=daily`
- `weeks` (optional, default: 12) - تعداد هفته‌ها برای `period=weekly`
- `months` (optional, default: 12) - تعداد ماه‌ها برای `period=monthly`
**Response:**
```json
{
"period": "monthly",
"data": [
{
"date": "1403/01",
"revenue": 350000000,
"orders": 450,
"users": 120
},
{
"date": "1403/02",
"revenue": 420000000,
"orders": 520,
"users": 135
}
// ...
],
"growth_rate": 9.4
}
```
---
## 4. API کاربران اخیر (Recent Users)
**Endpoint:** `GET /api/v1/admin/dashboard/recent-users`
**Description:** دریافت لیست کاربران جدید که اخیراً ثبت‌نام کرده‌اند
**Query Parameters:**
- `limit` (optional, default: 10) - تعداد کاربران
**Response:**
```json
{
"users": [
{
"id": 1,
"first_name": "علی",
"last_name": "احمدی",
"username": "ali_ahmadi",
"email": "ali@example.com",
"phone": "09123456789",
"status": "active",
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": 2,
"first_name": "فاطمه",
"last_name": "حسینی",
"username": "fateme_hosseini",
"email": "fateme@example.com",
"phone": "09123456790",
"status": "active",
"created_at": "2024-01-14T15:20:00Z"
}
// ...
],
"total": 10
}
```
**Note:** کاربران باید به ترتیب تاریخ ثبت‌نام (جدیدترین اول) مرتب باشند.
---
## 5. API توزیع دستگاه‌های کاربری (Device Distribution)
**Endpoint:** `GET /api/v1/admin/dashboard/device-distribution`
**Description:** دریافت آمار توزیع دستگاه‌های کاربری (دسکتاپ، موبایل، تبلت)
**Response:**
```json
{
"data": [
{
"device_type": "desktop",
"label": "دسکتاپ",
"count": 4500,
"percentage": 45.5
},
{
"device_type": "mobile",
"label": "موبایل",
"count": 3500,
"percentage": 35.2
},
{
"device_type": "tablet",
"label": "تبلت",
"count": 2000,
"percentage": 19.3
}
],
"total": 10000
}
```
**Note:** درصدها باید به صورت عدد صحیح (بدون اعشار) برگردانده شوند.
---
## 6. API سفارش‌های اخیر (Recent Orders)
**Endpoint:** `GET /api/v1/admin/dashboard/recent-orders`
**Description:** دریافت لیست آخرین سفارش‌های ثبت شده
**Query Parameters:**
- `limit` (optional, default: 10) - تعداد سفارش‌ها
**Response:**
```json
{
"orders": [
{
"id": 1234,
"order_number": "ORD-2024-001234",
"customer_name": "علی احمدی",
"customer_id": 45,
"total_amount": 2500000,
"status": "pending",
"payment_status": "paid",
"created_at": "2024-01-15T14:30:00Z"
},
{
"id": 1233,
"order_number": "ORD-2024-001233",
"customer_name": "فاطمه حسینی",
"customer_id": 46,
"total_amount": 1800000,
"status": "completed",
"payment_status": "paid",
"created_at": "2024-01-15T13:20:00Z"
}
// ...
],
"total": 10
}
```
**Note:** سفارش‌ها باید به ترتیب تاریخ ایجاد (جدیدترین اول) مرتب باشند.
---
## 7. API محصولات پرفروش (Top Selling Products)
**Endpoint:** `GET /api/v1/admin/dashboard/top-products`
**Description:** دریافت لیست محصولات پرفروش
**Query Parameters:**
- `limit` (optional, default: 5) - تعداد محصولات
- `period` (optional, default: "month") - بازه زمانی: `"day"`, `"week"`, `"month"`
**Response:**
```json
{
"products": [
{
"id": 45,
"name": "محصول نمونه",
"sku": "PRD-001",
"sold_count": 250,
"revenue": 125000000,
"image_url": "https://example.com/images/product-45.jpg"
},
{
"id": 78,
"name": "محصول دیگر",
"sku": "PRD-002",
"sold_count": 180,
"revenue": 90000000,
"image_url": "https://example.com/images/product-78.jpg"
}
// ...
]
}
```
**Note:** محصولات باید به ترتیب تعداد فروش (بیشترین اول) مرتب باشند.
---
## 8. API آمار مقایسه‌ای (Comparison Stats)
**Endpoint:** `GET /api/v1/admin/dashboard/comparison`
**Description:** دریافت آمار مقایسه‌ای بین دوره فعلی و دوره قبلی
**Query Parameters:**
- `compare_with` (optional, default: "last_period") - نوع مقایسه: `"last_period"`, `"last_year"`
- `period` (optional, default: "month") - نوع دوره: `"day"`, `"week"`, `"month"`
**Response:**
```json
{
"current": {
"revenue": 350000000,
"orders": 450,
"users": 120
},
"previous": {
"revenue": 320000000,
"orders": 410,
"users": 105
},
"changes": {
"revenue": {
"amount": 30000000,
"percentage": 9.4,
"trend": "up"
},
"orders": {
"amount": 40,
"percentage": 9.8,
"trend": "up"
},
"users": {
"amount": 15,
"percentage": 14.3,
"trend": "up"
}
}
}
```
**Note:**
- `trend` می‌تواند `"up"`, `"down"` یا `"stable"` باشد
- درصدها می‌توانند اعشار داشته باشند
---
## نکات مهم و الزامات:
1. **احراز هویت:** همه APIها باید نیاز به احراز هویت داشته باشند (Bearer Token)
2. **فیلتر تاریخ:** در صورت امکان، همه APIها باید قابلیت فیلتر بر اساس بازه زمانی را داشته باشند:
- `from_date` (optional) - تاریخ شروع
- `to_date` (optional) - تاریخ پایان
3. **فرمت تاریخ:**
- تاریخ‌ها در Response باید به فرمت ISO 8601 برگردانده شوند: `"2024-01-15T14:30:00Z"`
- تاریخ‌های ورودی می‌توانند به فرمت ISO 8601 یا timestamp باشند
4. **فرمت مبالغ:**
- همه مبالغ به تومان هستند
- مبالغ باید به صورت عدد (integer) برگردانده شوند
5. **ترتیب داده‌ها:**
- داده‌های زمانی باید به ترتیب زمانی (از قدیمی‌ترین به جدیدترین) مرتب باشند
- داده‌های مرتب‌سازی شده (مثل پرفروش‌ترین) باید به ترتیب مناسب مرتب باشند
6. **مقدار پیش‌فرض:**
- در صورت نبود داده، آرایه خالی `[]` یا `null` برگردانده شود
- مقادیر عددی در صورت نبود داده باید `0` باشند
7. **خطاها:**
- در صورت خطا، کد HTTP مناسب برگردانده شود
- پیام خطا به فارسی و واضح باشد
8. **Performance:**
- برای بهینه‌سازی، می‌توان از کش استفاده کرد
- Queryها باید بهینه باشند تا زمان پاسخگویی کم باشد
---
## اولویت پیاده‌سازی:
1. **اولویت بالا:**
- API آمار کلی داشبورد (#1)
- API فروش ماهانه (#2)
- API کاربران اخیر (#4)
- API سفارش‌های اخیر (#6)
2. **اولویت متوسط:**
- API روند رشد (#3)
- API محصولات پرفروش (#7)
- API آمار مقایسه‌ای (#8)
3. **اولویت پایین:**
- API توزیع دستگاه‌های کاربری (#5) - در صورت وجود داده
---
## مثال استفاده:
```bash
# دریافت آمار کلی
curl -X GET "https://api.example.com/api/v1/admin/dashboard/stats" \
-H "Authorization: Bearer YOUR_TOKEN"
# دریافت فروش 6 ماه گذشته
curl -X GET "https://api.example.com/api/v1/admin/dashboard/monthly-sales?months=6" \
-H "Authorization: Bearer YOUR_TOKEN"
# دریافت کاربران اخیر
curl -X GET "https://api.example.com/api/v1/admin/dashboard/recent-users?limit=10" \
-H "Authorization: Bearer YOUR_TOKEN"
```
---
**ممنون می‌شوم اگر این APIها را در اسرع وقت پیاده‌سازی کنید تا بتوانیم داشبورد را کامل کنیم.** 🙏
**در صورت نیاز به توضیحات بیشتر یا تغییرات، لطفاً اطلاع دهید.**

View File

@ -1,8 +1,27 @@
import { useState } from 'react'; import { Suspense, useState } from 'react';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { Sidebar } from './Sidebar'; import { Sidebar } from './Sidebar';
import { Header } from './Header'; import { Header } from './Header';
const ContentSkeleton = () => (
<div className="space-y-6">
<div className="h-10 w-1/3 rounded-lg bg-gray-200 dark:bg-gray-800 animate-pulse" />
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{[...Array(3)].map((_, index) => (
<div
key={index}
className="h-36 rounded-lg bg-white dark:bg-gray-800 shadow-sm"
>
<div className="h-full w-full rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" />
</div>
))}
</div>
<div className="h-96 rounded-lg bg-white dark:bg-gray-800 shadow-sm">
<div className="h-full w-full rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" />
</div>
</div>
);
export const Layout = () => { export const Layout = () => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
@ -18,7 +37,9 @@ export const Layout = () => {
<main className="flex-1 overflow-y-auto bg-gray-50 dark:bg-gray-900"> <main className="flex-1 overflow-y-auto bg-gray-50 dark:bg-gray-900">
<div className="min-h-full py-6 px-4 sm:px-6 lg:px-8"> <div className="min-h-full py-6 px-4 sm:px-6 lg:px-8">
<Suspense fallback={<ContentSkeleton />}>
<Outlet /> <Outlet />
</Suspense>
</div> </div>
</main> </main>
</div> </div>