feat(ui): implement unified design system and financial reporting
Introduce a comprehensive design system across WordPress and Laravel to ensure visual consistency. Implement new financial reporting capabilities and Excel export functionality within the Filament admin panel. - Add `DESIGN_SYSTEM.md` to define brand colors, typography, and spacing - Implement `FinancialReport` page in Filament for transaction and shipment summaries - Add Excel export support for `ShipmentResource` and `WalletTransactionResource` using Laravel Excel - Refactor WordPress plugin and Filament CSS to utilize the new design system variables - Update project status to reflect new reporting and design milestones
This commit is contained in:
parent
cb55f74cb7
commit
8b0814230c
179
01_Documents/DESIGN_SYSTEM.md
Normal file
179
01_Documents/DESIGN_SYSTEM.md
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# IFNEX Design System
|
||||||
|
|
||||||
|
## 🎨 رنگهای برند
|
||||||
|
|
||||||
|
### رنگهای اصلی
|
||||||
|
| نام | HEX | استفاده |
|
||||||
|
|-----|-----|---------|
|
||||||
|
| Primary Amber | `#f59e0b` | دکمههای اصلی، لینکها، آیکونهای فعال |
|
||||||
|
| Primary Dark | `#d97706` | هاور دکمهها |
|
||||||
|
| Primary Light | `#fbbf24` | زمینههای روشن، هایلایت |
|
||||||
|
| Success Green | `#059669` | عملیات موفق، موجودی مثبت |
|
||||||
|
| Danger Red | `#dc2626` | خطاها، موجودی منفی |
|
||||||
|
| Warning Yellow | `#f59e0b` | هشدارها، در حال پردازش |
|
||||||
|
| Info Blue | `#2563eb` | اطلاعات، لینکها |
|
||||||
|
|
||||||
|
### رنگهای خنثی
|
||||||
|
| نام | HEX | استفاده |
|
||||||
|
|-----|-----|---------|
|
||||||
|
| Gray 900 | `#111827` | متنهای اصلی |
|
||||||
|
| Gray 700 | `#374151` | متنهای ثانویه |
|
||||||
|
| Gray 500 | `#6b7280` | توضیحات، placeholder |
|
||||||
|
| Gray 300 | `#d1d5db` | حاشیهها |
|
||||||
|
| Gray 100 | `#f3f4f6` | پسزمینههای روشن |
|
||||||
|
| White | `#ffffff` | پسزمینه کارتها |
|
||||||
|
|
||||||
|
### رنگهای Sidebar
|
||||||
|
| نام | HEX | استفاده |
|
||||||
|
|-----|-----|---------|
|
||||||
|
| Sidebar Dark | `#1a1a2e` | پسزمینه اصلی سایدبار |
|
||||||
|
| Sidebar Blue | `#16213e` | گرادینت سایدبار |
|
||||||
|
| Sidebar Active | `rgba(245, 158, 11, 0.15)` | آیتم فعال |
|
||||||
|
|
||||||
|
## 📐 فاصلهگذاری (Spacing)
|
||||||
|
|
||||||
|
| نام | مقدار | استفاده |
|
||||||
|
|-----|-------|---------|
|
||||||
|
| xs | 0.25rem (4px) | فاصلههای خیلی کوچک |
|
||||||
|
| sm | 0.5rem (8px) | فاصلههای کوچک |
|
||||||
|
| md | 1rem (16px) | فاصلههای استاندارد |
|
||||||
|
| lg | 1.5rem (24px) | فاصلههای بزرگ |
|
||||||
|
| xl | 2rem (32px) | فاصلههای خیلی بزرگ |
|
||||||
|
| 2xl | 3rem (48px) | فاصلههای بخشها |
|
||||||
|
|
||||||
|
## 🔤 تایپوگرافی
|
||||||
|
|
||||||
|
### فونت اصلی
|
||||||
|
- **فامیلی:** Vazirmatn, IranYekan, ui-sans-serif, system-ui, sans-serif
|
||||||
|
- **سایز پایه:** 14px
|
||||||
|
- **خط:** 1.6
|
||||||
|
|
||||||
|
### سایزهای فونت
|
||||||
|
| نام | اندازه | وزن | استفاده |
|
||||||
|
|-----|--------|-----|---------|
|
||||||
|
| xs | 0.75rem (12px) | 400 | توضیحات، watermark |
|
||||||
|
| sm | 0.875rem (14px) | 400 | متنهای استاندارد |
|
||||||
|
| base | 1rem (16px) | 400 | متنهای اصلی |
|
||||||
|
| lg | 1.125rem (18px) | 500 | زیرعنوانها |
|
||||||
|
| xl | 1.25rem (20px) | 600 | عنوانهای صفحه |
|
||||||
|
| 2xl | 1.5rem (24px) | 700 | عنوانهای اصلی |
|
||||||
|
| 3xl | 1.875rem (30px) | 700 | Hero text |
|
||||||
|
|
||||||
|
## 📦 کارتها و کادرها
|
||||||
|
|
||||||
|
### استایل کارت
|
||||||
|
- **Border Radius:** 0.75rem (12px)
|
||||||
|
- **Border:** 1px solid #e5e7eb
|
||||||
|
- **Shadow:** 0 1px 3px rgba(0, 0, 0, 0.05)
|
||||||
|
- **Padding:** 1.25rem
|
||||||
|
- **Background:** #ffffff
|
||||||
|
|
||||||
|
### استایل کارت statistic
|
||||||
|
- **Border Radius:** 0.75rem (12px)
|
||||||
|
- **Padding:** 1.25rem
|
||||||
|
- **Title:** 0.875rem, weight 500, color #6b7280
|
||||||
|
- **Value:** 1.5rem, weight 700, color #111827
|
||||||
|
|
||||||
|
## 🔘 دکمهها
|
||||||
|
|
||||||
|
### Primary Button
|
||||||
|
- **Background:** #f59e0b
|
||||||
|
- **Border:** #f59e0b
|
||||||
|
- **Color:** #ffffff
|
||||||
|
- **Border Radius:** 0.5rem (8px)
|
||||||
|
- **Padding:** 0.625rem 1.25rem
|
||||||
|
- **Font Size:** 0.875rem
|
||||||
|
- **Hover:** #d97706, translateY(-1px), shadow
|
||||||
|
|
||||||
|
### Secondary Button
|
||||||
|
- **Background:** #ffffff
|
||||||
|
- **Border:** #d1d5db
|
||||||
|
- **Color:** #374151
|
||||||
|
- **Hover:** #f9fafb
|
||||||
|
|
||||||
|
## 📊 جداول
|
||||||
|
|
||||||
|
### Header
|
||||||
|
- **Background:** #f9fafb
|
||||||
|
- **Font Size:** 0.875rem
|
||||||
|
- **Font Weight:** 600
|
||||||
|
- **Text Transform:** uppercase
|
||||||
|
- **Letter Spacing:** 0.025em
|
||||||
|
- **Color:** #374151
|
||||||
|
- **Padding:** 0.75rem 1rem
|
||||||
|
|
||||||
|
### Cell
|
||||||
|
- **Padding:** 0.75rem 1rem
|
||||||
|
- **Font Size:** 0.875rem
|
||||||
|
- **Color:** #374151
|
||||||
|
- **Border Bottom:** 1px solid #f3f4f6
|
||||||
|
|
||||||
|
### Hover
|
||||||
|
- **Background:** #fffbeb
|
||||||
|
|
||||||
|
## 📱 رسپانسیو
|
||||||
|
|
||||||
|
### Breakpoints
|
||||||
|
- **Mobile:** < 768px
|
||||||
|
- **Tablet:** 768px - 1024px
|
||||||
|
- **Desktop:** > 1024px
|
||||||
|
|
||||||
|
### Mobile Adjustments
|
||||||
|
- **Font Size:** 13px
|
||||||
|
- **Padding:** 0.75rem
|
||||||
|
- **Grid:** 1 column
|
||||||
|
- **Cards:** Full width
|
||||||
|
|
||||||
|
### Tablet Adjustments
|
||||||
|
- **Grid:** 2 columns
|
||||||
|
- **Padding:** 1rem
|
||||||
|
|
||||||
|
## 🎯 چیدوبندی (Layout)
|
||||||
|
|
||||||
|
### Container
|
||||||
|
- **Max Width:** 1400px
|
||||||
|
- **Padding:** 1.5rem
|
||||||
|
- **Margin:** 0 auto
|
||||||
|
|
||||||
|
### Grid System
|
||||||
|
- **Gap:** 1rem
|
||||||
|
- **Columns:** auto-fit, minmax(240px, 1fr)
|
||||||
|
|
||||||
|
## 🌙 حالت تاریک (Dark Mode)
|
||||||
|
|
||||||
|
### در حال توسعه...
|
||||||
|
- پسزمینه: #0f172a
|
||||||
|
- متن: #f1f5f9
|
||||||
|
- کارت: #1e293b
|
||||||
|
- حاشیه: #334155
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 پیادهسازی در Filament
|
||||||
|
|
||||||
|
```php
|
||||||
|
// AdminPanelProvider.php
|
||||||
|
->colors([
|
||||||
|
'primary' => Color::Amber, // #f59e0b
|
||||||
|
])
|
||||||
|
->sidebarWidth('18rem')
|
||||||
|
->maxContentWidth('xl')
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 پیادهسازی در WordPress
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* استفاده از متغیرهای CSS */
|
||||||
|
:root {
|
||||||
|
--ifnex-primary: #f59e0b;
|
||||||
|
--ifnex-primary-dark: #d97706;
|
||||||
|
--ifnex-success: #059669;
|
||||||
|
--ifnex-danger: #dc2626;
|
||||||
|
--ifnex-gray-900: #111827;
|
||||||
|
--ifnex-gray-700: #374151;
|
||||||
|
--ifnex-gray-500: #6b7280;
|
||||||
|
--ifnex-gray-300: #d1d5db;
|
||||||
|
--ifnex-gray-100: #f3f4f6;
|
||||||
|
--ifnex-white: #ffffff;
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -552,6 +552,11 @@ IFNEX-Logistics/
|
|||||||
- ساخت دستور Artisan `ifnex:sync-wp-users` برای یکسازی کاربران وردپرس ↔ لاراول
|
- ساخت دستور Artisan `ifnex:sync-wp-users` برای یکسازی کاربران وردپرس ↔ لاراول
|
||||||
- تست زنده پلاگین وردپرس — همه APIها با موفقیت تست شدند
|
- تست زنده پلاگین وردپرس — همه APIها با موفقیت تست شدند
|
||||||
- پاکسازی فایلهای تست و اضافی از ریشه پروژه
|
- پاکسازی فایلهای تست و اضافی از ریشه پروژه
|
||||||
|
- افزودن خروجی Excel برای تراکنشها و مرسولات در Filament
|
||||||
|
- ساخت صفحه گزارشگیری مالی در Filament: خلاصه مالی، تراکنشها، مرسولات
|
||||||
|
- ساخت Design System یکپارچه IFNEX برای استفاده در Filament و وردپرس
|
||||||
|
- بهروزرسانی استایلهای Filament مطابق با Design System
|
||||||
|
- بهروزرسانی استایلهای پلاگین وردپرس مطابق با Design System
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,384 +1,450 @@
|
|||||||
/**
|
/**
|
||||||
* IFNEX Bridge Styles
|
* IFNEX Bridge Styles
|
||||||
* استایلهای پلاگین IFNEX Logistics Bridge
|
* استایلهای پلاگین IFNEX Logistics Bridge
|
||||||
|
* بر اساس IFNEX Design System
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Wallet Balance */
|
:root {
|
||||||
|
--ifnex-primary: #f59e0b;
|
||||||
|
--ifnex-primary-dark: #d97706;
|
||||||
|
--ifnex-primary-light: #fbbf24;
|
||||||
|
--ifnex-success: #059669;
|
||||||
|
--ifnex-success-light: #d1fae5;
|
||||||
|
--ifnex-danger: #dc2626;
|
||||||
|
--ifnex-danger-light: #fee2e2;
|
||||||
|
--ifnex-warning: #f59e0b;
|
||||||
|
--ifnex-warning-light: #fef3c7;
|
||||||
|
--ifnex-info: #2563eb;
|
||||||
|
--ifnex-info-light: #dbeafe;
|
||||||
|
--ifnex-gray-900: #111827;
|
||||||
|
--ifnex-gray-700: #374151;
|
||||||
|
--ifnex-gray-600: #4b5563;
|
||||||
|
--ifnex-gray-500: #6b7280;
|
||||||
|
--ifnex-gray-400: #9ca3af;
|
||||||
|
--ifnex-gray-300: #d1d5db;
|
||||||
|
--ifnex-gray-200: #e5e7eb;
|
||||||
|
--ifnex-gray-100: #f3f4f6;
|
||||||
|
--ifnex-gray-50: #f9fafb;
|
||||||
|
--ifnex-white: #ffffff;
|
||||||
|
--ifnex-font-family: 'Vazirmatn', 'IranYekan', ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--ifnex-radius-sm: 0.375rem;
|
||||||
|
--ifnex-radius-md: 0.5rem;
|
||||||
|
--ifnex-radius-lg: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
GLOBAL
|
||||||
|
============================================ */
|
||||||
|
.ifnex-widget,
|
||||||
|
.ifnex-tracking-container,
|
||||||
|
.ifnex-tracking-result,
|
||||||
|
.ifnex-transactions,
|
||||||
.ifnex-wallet-balance {
|
.ifnex-wallet-balance {
|
||||||
background: #f9fafb;
|
font-family: var(--ifnex-font-family) !important;
|
||||||
border: 1px solid #e5e7eb;
|
direction: rtl !important;
|
||||||
border-radius: 12px;
|
text-align: right !important;
|
||||||
padding: 20px;
|
color: var(--ifnex-gray-900) !important;
|
||||||
margin: 15px 0;
|
}
|
||||||
font-family: inherit;
|
|
||||||
direction: rtl;
|
/* ============================================
|
||||||
text-align: right;
|
WALLET BALANCE
|
||||||
|
============================================ */
|
||||||
|
.ifnex-wallet-balance {
|
||||||
|
background: var(--ifnex-white) !important;
|
||||||
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
|
padding: 1.25rem !important;
|
||||||
|
margin: 1rem 0 !important;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-balance-main {
|
.ifnex-balance-main {
|
||||||
display: flex;
|
display: flex !important;
|
||||||
justify-content: space-between;
|
justify-content: space-between !important;
|
||||||
align-items: center;
|
align-items: center !important;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 1rem !important;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 1rem !important;
|
||||||
border-bottom: 1px solid #e5e7eb;
|
border-bottom: 1px solid var(--ifnex-gray-200) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-balance-label {
|
.ifnex-balance-label {
|
||||||
font-size: 14px;
|
font-size: 0.875rem !important;
|
||||||
color: #6b7280;
|
color: var(--ifnex-gray-500) !important;
|
||||||
font-weight: 500;
|
font-weight: 500 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-balance-amount {
|
.ifnex-balance-amount {
|
||||||
font-size: 24px;
|
font-size: 1.5rem !important;
|
||||||
font-weight: 700;
|
font-weight: 700 !important;
|
||||||
color: #059669;
|
color: var(--ifnex-success) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-balance-details {
|
.ifnex-balance-details {
|
||||||
margin-top: 10px;
|
margin-top: 1rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-balance-details p {
|
.ifnex-balance-details p {
|
||||||
margin: 5px 0;
|
margin: 0.5rem 0 !important;
|
||||||
font-size: 14px;
|
font-size: 0.875rem !important;
|
||||||
color: #374151;
|
color: var(--ifnex-gray-700) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-balance-details strong {
|
.ifnex-balance-details strong {
|
||||||
color: #6b7280;
|
color: var(--ifnex-gray-500) !important;
|
||||||
margin-left: 8px;
|
margin-left: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-wallet-frozen {
|
.ifnex-wallet-frozen {
|
||||||
color: #dc2626;
|
color: var(--ifnex-danger) !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
margin-top: 10px;
|
margin-top: 1rem !important;
|
||||||
padding: 8px 12px;
|
padding: 0.75rem 1rem !important;
|
||||||
background: #fee2e2;
|
background: var(--ifnex-danger-light) !important;
|
||||||
border-radius: 6px;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
border-right: 3px solid #dc2626;
|
border-right: 3px solid var(--ifnex-danger) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transactions */
|
/* ============================================
|
||||||
|
TRANSACTIONS
|
||||||
|
============================================ */
|
||||||
.ifnex-transactions {
|
.ifnex-transactions {
|
||||||
margin: 15px 0;
|
margin: 1rem 0 !important;
|
||||||
font-family: inherit;
|
|
||||||
direction: rtl;
|
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transactions h3 {
|
.ifnex-transactions h3 {
|
||||||
font-size: 18px;
|
font-size: 1.125rem !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
color: #111827;
|
color: var(--ifnex-gray-900) !important;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 1rem !important;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 0.5rem !important;
|
||||||
border-bottom: 2px solid #f59e0b;
|
border-bottom: 2px solid var(--ifnex-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transactions-list {
|
.ifnex-transactions-list {
|
||||||
display: flex;
|
display: flex !important;
|
||||||
flex-direction: column;
|
flex-direction: column !important;
|
||||||
gap: 12px;
|
gap: 0.75rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-item {
|
.ifnex-transaction-item {
|
||||||
background: #ffffff;
|
background: var(--ifnex-white) !important;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
border-radius: 10px;
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
padding: 16px;
|
padding: 1rem !important;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-item:hover {
|
.ifnex-transaction-item:hover {
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important;
|
||||||
border-color: #f59e0b;
|
border-color: var(--ifnex-primary-light) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-header {
|
.ifnex-transaction-header {
|
||||||
display: flex;
|
display: flex !important;
|
||||||
justify-content: space-between;
|
justify-content: space-between !important;
|
||||||
align-items: center;
|
align-items: center !important;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-type {
|
.ifnex-transaction-type {
|
||||||
display: inline-block;
|
display: inline-block !important;
|
||||||
padding: 4px 10px;
|
padding: 0.25rem 0.75rem !important;
|
||||||
border-radius: 6px;
|
border-radius: var(--ifnex-radius-sm) !important;
|
||||||
font-size: 12px;
|
font-size: 0.75rem !important;
|
||||||
font-weight: 500;
|
font-weight: 500 !important;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase !important;
|
||||||
background: #dbeafe;
|
background: var(--ifnex-info-light) !important;
|
||||||
color: #1e40af;
|
color: var(--ifnex-info) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-type.deposit {
|
.ifnex-transaction-type.deposit {
|
||||||
background: #d1fae5;
|
background: var(--ifnex-success-light) !important;
|
||||||
color: #065f46;
|
color: var(--ifnex-success) !important;
|
||||||
}
|
|
||||||
|
|
||||||
.ifnex-transaction-type.withdrawal {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ifnex-transaction-type.withdrawal,
|
||||||
.ifnex-transaction-type.order_payment {
|
.ifnex-transaction-type.order_payment {
|
||||||
background: #fef3c7;
|
background: var(--ifnex-danger-light) !important;
|
||||||
color: #92400e;
|
color: var(--ifnex-danger) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-type.refund {
|
.ifnex-transaction-type.refund {
|
||||||
background: #e0e7ff;
|
background: var(--ifnex-info-light) !important;
|
||||||
color: #3730a3;
|
color: var(--ifnex-info) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-amount {
|
.ifnex-transaction-amount {
|
||||||
font-size: 16px;
|
font-size: 1rem !important;
|
||||||
font-weight: 700;
|
font-weight: 700 !important;
|
||||||
font-family: 'Vazirmatn', 'IranYekan', sans-serif;
|
font-family: var(--ifnex-font-family) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-amount.positive {
|
.ifnex-transaction-amount.positive {
|
||||||
color: #059669;
|
color: var(--ifnex-success) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-amount.negative {
|
.ifnex-transaction-amount.negative {
|
||||||
color: #dc2626;
|
color: var(--ifnex-danger) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-details {
|
.ifnex-transaction-details {
|
||||||
margin-top: 8px;
|
margin-top: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-description {
|
.ifnex-transaction-description {
|
||||||
color: #4b5563;
|
color: var(--ifnex-gray-700) !important;
|
||||||
font-size: 14px;
|
font-size: 0.875rem !important;
|
||||||
margin: 0 0 6px 0;
|
margin: 0 0 0.5rem 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-meta {
|
.ifnex-transaction-meta {
|
||||||
display: flex;
|
display: flex !important;
|
||||||
gap: 12px;
|
gap: 0.75rem !important;
|
||||||
font-size: 12px;
|
font-size: 0.75rem !important;
|
||||||
color: #6b7280;
|
color: var(--ifnex-gray-500) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-status {
|
.ifnex-transaction-status {
|
||||||
display: inline-block;
|
display: inline-block !important;
|
||||||
padding: 2px 8px;
|
padding: 0.125rem 0.5rem !important;
|
||||||
border-radius: 4px;
|
border-radius: var(--ifnex-radius-sm) !important;
|
||||||
background: #f3f4f6;
|
background: var(--ifnex-gray-100) !important;
|
||||||
color: #374151;
|
color: var(--ifnex-gray-700) !important;
|
||||||
font-size: 11px;
|
font-size: 0.75rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-status.completed {
|
.ifnex-transaction-status.completed {
|
||||||
background: #d1fae5;
|
background: var(--ifnex-success-light) !important;
|
||||||
color: #065f46;
|
color: var(--ifnex-success) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-status.pending {
|
.ifnex-transaction-status.pending {
|
||||||
background: #fef3c7;
|
background: var(--ifnex-warning-light) !important;
|
||||||
color: #92400e;
|
color: #92400e !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-status.failed {
|
.ifnex-transaction-status.failed {
|
||||||
background: #fee2e2;
|
background: var(--ifnex-danger-light) !important;
|
||||||
color: #991b1b;
|
color: var(--ifnex-danger) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-transaction-ref {
|
.ifnex-transaction-ref {
|
||||||
margin-top: 6px;
|
margin-top: 0.5rem !important;
|
||||||
font-size: 12px;
|
font-size: 0.75rem !important;
|
||||||
color: #6b7280;
|
color: var(--ifnex-gray-500) !important;
|
||||||
font-family: monospace;
|
font-family: monospace !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tracking Status */
|
/* ============================================
|
||||||
|
TRACKING FORM
|
||||||
|
============================================ */
|
||||||
.ifnex-tracking-container {
|
.ifnex-tracking-container {
|
||||||
max-width: 700px;
|
max-width: 700px !important;
|
||||||
margin: 20px auto;
|
margin: 1.5rem auto !important;
|
||||||
padding: 24px;
|
padding: 1.5rem !important;
|
||||||
background: #ffffff;
|
background: var(--ifnex-white) !important;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
border-radius: 12px;
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
font-family: inherit;
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05) !important;
|
||||||
direction: rtl;
|
|
||||||
text-align: right;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-container h2 {
|
.ifnex-tracking-container h2 {
|
||||||
font-size: 20px;
|
font-size: 1.25rem !important;
|
||||||
font-weight: 700;
|
font-weight: 700 !important;
|
||||||
color: #111827;
|
color: var(--ifnex-gray-900) !important;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 1rem !important;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 0.5rem !important;
|
||||||
border-bottom: 2px solid #f59e0b;
|
border-bottom: 2px solid var(--ifnex-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-form {
|
.ifnex-tracking-form {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 1.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-form-group {
|
.ifnex-form-group {
|
||||||
display: flex;
|
display: flex !important;
|
||||||
gap: 10px;
|
gap: 0.75rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-form-group input {
|
.ifnex-form-group input {
|
||||||
flex: 1;
|
flex: 1 !important;
|
||||||
padding: 12px 16px;
|
padding: 0.75rem 1rem !important;
|
||||||
border: 1px solid #d1d5db;
|
border: 1px solid var(--ifnex-gray-300) !important;
|
||||||
border-radius: 8px;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
font-size: 15px;
|
font-size: 0.9375rem !important;
|
||||||
font-family: inherit;
|
font-family: var(--ifnex-font-family) !important;
|
||||||
transition: border-color 0.2s ease;
|
transition: var(--ifnex-transition) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-form-group input:focus {
|
.ifnex-form-group input:focus {
|
||||||
outline: none;
|
outline: none !important;
|
||||||
border-color: #f59e0b;
|
border-color: var(--ifnex-primary) !important;
|
||||||
box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1);
|
box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-form-group button {
|
.ifnex-form-group button {
|
||||||
padding: 12px 24px;
|
padding: 0.75rem 1.5rem !important;
|
||||||
background: #f59e0b;
|
background: var(--ifnex-primary) !important;
|
||||||
color: white;
|
color: var(--ifnex-white) !important;
|
||||||
border: none;
|
border: none !important;
|
||||||
border-radius: 8px;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
cursor: pointer;
|
cursor: pointer !important;
|
||||||
font-size: 15px;
|
font-size: 0.9375rem !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
font-family: inherit;
|
font-family: var(--ifnex-font-family) !important;
|
||||||
transition: all 0.2s ease;
|
transition: var(--ifnex-transition) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-form-group button:hover {
|
.ifnex-form-group button:hover {
|
||||||
background: #d97706;
|
background: var(--ifnex-primary-dark) !important;
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px) !important;
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
box-shadow: var(--ifnex-shadow-md) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-form-group button:disabled {
|
.ifnex-form-group button:disabled {
|
||||||
background: #9ca3af;
|
background: var(--ifnex-gray-400) !important;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed !important;
|
||||||
transform: none;
|
transform: none !important;
|
||||||
box-shadow: none;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-result {
|
.ifnex-tracking-result {
|
||||||
margin-top: 20px;
|
margin-top: 1.5rem !important;
|
||||||
padding: 20px;
|
padding: 1.25rem !important;
|
||||||
background: #f9fafb;
|
background: var(--ifnex-gray-50) !important;
|
||||||
border-radius: 8px;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-error {
|
.ifnex-error {
|
||||||
color: #dc2626;
|
color: var(--ifnex-danger) !important;
|
||||||
font-weight: 500;
|
font-weight: 500 !important;
|
||||||
padding: 10px;
|
padding: 0.75rem 1rem !important;
|
||||||
background: #fee2e2;
|
background: var(--ifnex-danger-light) !important;
|
||||||
border-radius: 6px;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
border-right: 3px solid #dc2626;
|
border-right: 3px solid var(--ifnex-danger) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-status h3 {
|
.ifnex-tracking-status h3 {
|
||||||
margin-top: 0;
|
margin-top: 0 !important;
|
||||||
font-size: 18px;
|
font-size: 1.125rem !important;
|
||||||
font-weight: 700;
|
font-weight: 700 !important;
|
||||||
color: #111827;
|
color: var(--ifnex-gray-900) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-status-badge {
|
.ifnex-status-badge {
|
||||||
display: inline-block;
|
display: inline-block !important;
|
||||||
padding: 6px 14px;
|
padding: 0.375rem 0.875rem !important;
|
||||||
background: #f59e0b;
|
background: var(--ifnex-primary) !important;
|
||||||
color: white;
|
color: var(--ifnex-white) !important;
|
||||||
border-radius: 6px;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
font-size: 14px;
|
font-size: 0.875rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-status-badge.delivered {
|
.ifnex-status-badge.delivered {
|
||||||
background: #059669;
|
background: var(--ifnex-success) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-status-badge.failed {
|
.ifnex-status-badge.failed {
|
||||||
background: #dc2626;
|
background: var(--ifnex-danger) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-status-badge.in_transit {
|
.ifnex-status-badge.in_transit {
|
||||||
background: #2563eb;
|
background: var(--ifnex-info) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-details {
|
.ifnex-tracking-details {
|
||||||
margin-top: 12px;
|
margin-top: 1rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-details p {
|
.ifnex-tracking-details p {
|
||||||
margin: 6px 0;
|
margin: 0.5rem 0 !important;
|
||||||
font-size: 14px;
|
font-size: 0.875rem !important;
|
||||||
color: #4b5563;
|
color: var(--ifnex-gray-700) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-tracking-details strong {
|
.ifnex-tracking-details strong {
|
||||||
color: #374151;
|
color: var(--ifnex-gray-700) !important;
|
||||||
margin-left: 8px;
|
margin-left: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline {
|
.ifnex-timeline {
|
||||||
margin-top: 20px;
|
margin-top: 1.5rem !important;
|
||||||
padding-top: 15px;
|
padding-top: 1rem !important;
|
||||||
border-top: 1px solid #e5e7eb;
|
border-top: 1px solid var(--ifnex-gray-200) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline h4 {
|
.ifnex-timeline h4 {
|
||||||
font-size: 16px;
|
font-size: 1rem !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
color: #111827;
|
color: var(--ifnex-gray-900) !important;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 0.75rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline ul {
|
.ifnex-timeline ul {
|
||||||
list-style: none;
|
list-style: none !important;
|
||||||
padding: 0;
|
padding: 0 !important;
|
||||||
margin: 0;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline li {
|
.ifnex-timeline li {
|
||||||
padding: 10px 12px;
|
padding: 0.75rem !important;
|
||||||
border-bottom: 1px solid #f3f4f6;
|
border-bottom: 1px solid var(--ifnex-gray-100) !important;
|
||||||
display: flex;
|
display: flex !important;
|
||||||
gap: 10px;
|
gap: 0.75rem !important;
|
||||||
align-items: flex-start;
|
align-items: flex-start !important;
|
||||||
font-size: 14px;
|
font-size: 0.875rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline li:last-child {
|
.ifnex-timeline li:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline li strong {
|
.ifnex-timeline li strong {
|
||||||
color: #f59e0b;
|
color: var(--ifnex-primary) !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
white-space: nowrap;
|
white-space: nowrap !important;
|
||||||
min-width: 100px;
|
min-width: 100px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline li span {
|
.ifnex-timeline li span {
|
||||||
color: #4b5563;
|
color: var(--ifnex-gray-700) !important;
|
||||||
flex: 1;
|
flex: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ifnex-timeline li small {
|
.ifnex-timeline li small {
|
||||||
color: #9ca3af;
|
color: var(--ifnex-gray-400) !important;
|
||||||
font-size: 12px;
|
font-size: 0.75rem !important;
|
||||||
white-space: nowrap;
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
RESPONSIVE
|
||||||
|
============================================ */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ifnex-tracking-container {
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding: 1rem !important;
|
||||||
|
margin: 1rem 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnex-form-group {
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnex-form-group button {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnex-balance-amount {
|
||||||
|
font-size: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnex-transaction-header {
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 0.5rem !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"version":2,"defects":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":5,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":7,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":7,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":8,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.052,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.05,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.008,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.005,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.064,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.096,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.05,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.048,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.081,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.059,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.049,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.073,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.062,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.007,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.007,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.002,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.001,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.006}}
|
{"version":2,"defects":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":5,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":7,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":7,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":8,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":7,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.058,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.06,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.012,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.008,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.081,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.109,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.112,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.071,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.053,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.079,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.054,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.053,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.093,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.057,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.005,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.01,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.002,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.001,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.003}}
|
||||||
113
04_Laravel/app/Exports/ShipmentsExport.php
Normal file
113
04_Laravel/app/Exports/ShipmentsExport.php
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Shipment;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||||
|
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
|
class ShipmentsExport implements FromQuery, WithHeadings, WithMapping, WithStyles, ShouldAutoSize
|
||||||
|
{
|
||||||
|
protected $filters;
|
||||||
|
|
||||||
|
public function __construct(array $filters = [])
|
||||||
|
{
|
||||||
|
$this->filters = $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function query()
|
||||||
|
{
|
||||||
|
$query = Shipment::query()
|
||||||
|
->with(['fromCountry', 'toCountry', 'carrierMappings', 'items'])
|
||||||
|
->orderByDesc('created_at');
|
||||||
|
|
||||||
|
if (!empty($this->filters['status'])) {
|
||||||
|
$query->where('status', $this->filters['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['type'])) {
|
||||||
|
$query->where('type', $this->filters['type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['direction'])) {
|
||||||
|
$query->where('direction', $this->filters['direction']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['from_date'])) {
|
||||||
|
$query->where('created_at', '>=', $this->filters['from_date']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['to_date'])) {
|
||||||
|
$query->where('created_at', '<=', $this->filters['to_date']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'AWB No.',
|
||||||
|
'جهت',
|
||||||
|
'نوع',
|
||||||
|
'وضعیت',
|
||||||
|
'کشور مبدا',
|
||||||
|
'کشور مقصد',
|
||||||
|
'وزن واقعی (kg)',
|
||||||
|
'وزن حجمی (kg)',
|
||||||
|
'وزن قابل پرداخت (kg)',
|
||||||
|
'قیمت حمل (درهم)',
|
||||||
|
'خدمات اضافی (درهم)',
|
||||||
|
'هزینه بستهبندی (ریال)',
|
||||||
|
'دریافت داخلی (ریال)',
|
||||||
|
'تحویل داخلی (ریال)',
|
||||||
|
'هزینه انبار (ریال)',
|
||||||
|
'تخفیف (ریال)',
|
||||||
|
'هزینه کل (ریال)',
|
||||||
|
'فرستنده',
|
||||||
|
'تلفن فرستنده',
|
||||||
|
'گیرنده',
|
||||||
|
'تلفن گیرنده',
|
||||||
|
'تاریخ ایجاد',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map($shipment): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$shipment->awb_no,
|
||||||
|
$shipment->direction->label(),
|
||||||
|
$shipment->type->label(),
|
||||||
|
$shipment->status->label(),
|
||||||
|
$shipment->fromCountry->name ?? '-',
|
||||||
|
$shipment->toCountry->name ?? '-',
|
||||||
|
$shipment->weight ?? 0,
|
||||||
|
$shipment->volumetric_weight ?? 0,
|
||||||
|
$shipment->chargeable_weight ?? 0,
|
||||||
|
$shipment->shipping_price ?? 0,
|
||||||
|
$shipment->extra_service ?? 0,
|
||||||
|
$shipment->packing_cost ?? 0,
|
||||||
|
$shipment->domestic_pickup ?? 0,
|
||||||
|
$shipment->domestic_delivery ?? 0,
|
||||||
|
$shipment->warehousing_cost ?? 0,
|
||||||
|
$shipment->discount ?? 0,
|
||||||
|
$shipment->total_fee ?? 0,
|
||||||
|
$shipment->sender_name ?? '-',
|
||||||
|
$shipment->sender_phone ?? '-',
|
||||||
|
$shipment->receiver_name ?? '-',
|
||||||
|
$shipment->receiver_phone ?? '-',
|
||||||
|
$shipment->created_at?->format('Y/m/d H:i'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function styles(Worksheet $sheet)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
1 => ['font' => ['bold' => true, 'size' => 12]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
99
04_Laravel/app/Exports/WalletTransactionsExport.php
Normal file
99
04_Laravel/app/Exports/WalletTransactionsExport.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\WalletTransaction;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||||
|
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class WalletTransactionsExport implements FromQuery, WithHeadings, WithMapping, WithStyles, ShouldAutoSize
|
||||||
|
{
|
||||||
|
protected $filters;
|
||||||
|
|
||||||
|
public function __construct(array $filters = [])
|
||||||
|
{
|
||||||
|
$this->filters = $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function query()
|
||||||
|
{
|
||||||
|
$query = WalletTransaction::query()
|
||||||
|
->with(['wallet.user', 'creator', 'approver'])
|
||||||
|
->orderByDesc('created_at');
|
||||||
|
|
||||||
|
if (!empty($this->filters['type'])) {
|
||||||
|
$query->where('type', $this->filters['type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['status'])) {
|
||||||
|
$query->where('status', $this->filters['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['gateway'])) {
|
||||||
|
$query->where('gateway', $this->filters['gateway']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['from_date'])) {
|
||||||
|
$query->where('created_at', '>=', $this->filters['from_date']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->filters['to_date'])) {
|
||||||
|
$query->where('created_at', '<=', $this->filters['to_date']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'شناسه',
|
||||||
|
'کاربر',
|
||||||
|
'ایمیل',
|
||||||
|
'مبلغ (ریال)',
|
||||||
|
'نوع',
|
||||||
|
'وضعیت',
|
||||||
|
'درگاه',
|
||||||
|
'کد پیگیری',
|
||||||
|
'توضیحات',
|
||||||
|
'یادداشت ادمین',
|
||||||
|
'موجودی قبل',
|
||||||
|
'موجودی بعد',
|
||||||
|
'تاریخ ایجاد',
|
||||||
|
'تایید کننده',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map($transaction): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$transaction->id,
|
||||||
|
$transaction->wallet->user->name ?? 'نامشخص',
|
||||||
|
$transaction->wallet->user->email ?? 'نامشخص',
|
||||||
|
$transaction->amount,
|
||||||
|
$transaction->type->label(),
|
||||||
|
$transaction->status->label(),
|
||||||
|
$transaction->gateway?->label() ?? '-',
|
||||||
|
$transaction->gateway_reference_id ?? '-',
|
||||||
|
$transaction->description ?? '-',
|
||||||
|
$transaction->admin_notes ?? '-',
|
||||||
|
$transaction->balance_before ?? 0,
|
||||||
|
$transaction->balance_after ?? 0,
|
||||||
|
$transaction->created_at?->format('Y/m/d H:i'),
|
||||||
|
$transaction->approver?->name ?? '-',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function styles(Worksheet $sheet)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
// First row as bold text.
|
||||||
|
1 => ['font' => ['bold' => true, 'size' => 12]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
278
04_Laravel/app/Filament/Pages/Reports/FinancialReport.php
Normal file
278
04_Laravel/app/Filament/Pages/Reports/FinancialReport.php
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Pages\Reports;
|
||||||
|
|
||||||
|
use App\Exports\ShipmentsExport;
|
||||||
|
use App\Exports\WalletTransactionsExport;
|
||||||
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Pages\Page;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class FinancialReport extends Page
|
||||||
|
{
|
||||||
|
protected static ?string $navigationIcon = 'heroicon-o-chart-bar';
|
||||||
|
protected static ?string $navigationLabel = 'گزارش مالی';
|
||||||
|
protected static ?string $navigationGroup = 'مالی';
|
||||||
|
protected static ?int $navigationSort = 5;
|
||||||
|
protected static ?string $slug = 'financial-report';
|
||||||
|
protected static string $view = 'filament.pages.reports.financial-report';
|
||||||
|
|
||||||
|
public ?array $data = [];
|
||||||
|
public ?array $reportData = null;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->form->fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Section::make('فیلترهای گزارش')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Select::make('report_type')
|
||||||
|
->label('نوع گزارش')
|
||||||
|
->options([
|
||||||
|
'transactions' => 'تراکنشهای کیف پول',
|
||||||
|
'shipments' => 'مرسولات',
|
||||||
|
'financial_summary' => 'خلاصه مالی',
|
||||||
|
])
|
||||||
|
->required()
|
||||||
|
->reactive()
|
||||||
|
->default('transactions'),
|
||||||
|
|
||||||
|
Forms\Components\DatePicker::make('from_date')
|
||||||
|
->label('از تاریخ')
|
||||||
|
->required()
|
||||||
|
->default(now()->subMonth()),
|
||||||
|
|
||||||
|
Forms\Components\DatePicker::make('to_date')
|
||||||
|
->label('تا تاریخ')
|
||||||
|
->required()
|
||||||
|
->default(now()),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('transaction_type')
|
||||||
|
->label('نوع تراکنش')
|
||||||
|
->options([
|
||||||
|
'deposit' => 'واریزی',
|
||||||
|
'withdrawal' => 'برداشت',
|
||||||
|
'order_payment' => 'پرداخت سفارش',
|
||||||
|
'refund' => 'بازگشت وجه',
|
||||||
|
])
|
||||||
|
->visible(fn ($get) => $get('report_type') === 'transactions')
|
||||||
|
->placeholder('همه'),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('shipment_status')
|
||||||
|
->label('وضعیت مرسوله')
|
||||||
|
->options([
|
||||||
|
'processed' => 'ثبت شده',
|
||||||
|
'picked_up' => 'تحویل به پست',
|
||||||
|
'in_transit' => 'در حال حمل',
|
||||||
|
'out_for_delivery' => 'آماده تحویل',
|
||||||
|
'delivered' => 'تحویل داده شده',
|
||||||
|
'failed' => 'ناموفق',
|
||||||
|
'returned' => 'بازگشته',
|
||||||
|
])
|
||||||
|
->visible(fn ($get) => $get('report_type') === 'shipments')
|
||||||
|
->placeholder('همه'),
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
|
||||||
|
Forms\Components\Section::make('عملیات')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Actions::make([
|
||||||
|
Forms\Components\Actions\Action::make('generate')
|
||||||
|
->label('تولید گزارش')
|
||||||
|
->icon('heroicon-o-eye')
|
||||||
|
->color('primary')
|
||||||
|
->action('generateReport'),
|
||||||
|
|
||||||
|
Forms\Components\Actions\Action::make('export_excel')
|
||||||
|
->label('خروجی Excel')
|
||||||
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
|
->color('success')
|
||||||
|
->action('exportExcel')
|
||||||
|
->visible(fn ($get) => $get('report_type') !== 'financial_summary'),
|
||||||
|
])
|
||||||
|
->fullWidth(),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->statePath('data');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateReport(): void
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$type = $this->data['report_type'];
|
||||||
|
$fromDate = $this->data['from_date'];
|
||||||
|
$toDate = $this->data['to_date'];
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'transactions':
|
||||||
|
$this->generateTransactionsReport($fromDate, $toDate);
|
||||||
|
break;
|
||||||
|
case 'shipments':
|
||||||
|
$this->generateShipmentsReport($fromDate, $toDate);
|
||||||
|
break;
|
||||||
|
case 'financial_summary':
|
||||||
|
$this->generateFinancialSummary($fromDate, $toDate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateTransactionsReport($fromDate, $toDate): void
|
||||||
|
{
|
||||||
|
$query = DB::table('wallet_transactions')
|
||||||
|
->join('wallets', 'wallet_transactions.wallet_id', '=', 'wallets.id')
|
||||||
|
->join('users', 'wallets.user_id', '=', 'users.id')
|
||||||
|
->select(
|
||||||
|
'wallet_transactions.id',
|
||||||
|
'users.name as user_name',
|
||||||
|
'users.email as user_email',
|
||||||
|
'wallet_transactions.amount',
|
||||||
|
'wallet_transactions.type',
|
||||||
|
'wallet_transactions.status',
|
||||||
|
'wallet_transactions.gateway',
|
||||||
|
'wallet_transactions.description',
|
||||||
|
'wallet_transactions.created_at'
|
||||||
|
)
|
||||||
|
->whereBetween('wallet_transactions.created_at', [$fromDate, $toDate . ' 23:59:59'])
|
||||||
|
->orderByDesc('wallet_transactions.created_at');
|
||||||
|
|
||||||
|
if (!empty($this->data['transaction_type'])) {
|
||||||
|
$query->where('wallet_transactions.type', $this->data['transaction_type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->reportData = [
|
||||||
|
'type' => 'transactions',
|
||||||
|
'data' => $query->get(),
|
||||||
|
'summary' => [
|
||||||
|
'total_count' => $query->count(),
|
||||||
|
'total_amount' => $query->sum('amount'),
|
||||||
|
'deposits' => $query->where('type', 'deposit')->sum('amount'),
|
||||||
|
'withdrawals' => $query->whereIn('type', ['withdrawal', 'order_payment'])->sum('amount'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateShipmentsReport($fromDate, $toDate): void
|
||||||
|
{
|
||||||
|
$query = DB::table('shipments')
|
||||||
|
->join('countries as from_country', 'shipments.from_country_id', '=', 'from_country.id')
|
||||||
|
->join('countries as to_country', 'shipments.to_country_id', '=', 'to_country.id')
|
||||||
|
->select(
|
||||||
|
'shipments.awb_no',
|
||||||
|
'shipments.direction',
|
||||||
|
'shipments.type',
|
||||||
|
'shipments.status',
|
||||||
|
'from_country.name as from_country',
|
||||||
|
'to_country.name as to_country',
|
||||||
|
'shipments.weight',
|
||||||
|
'shipments.chargeable_weight',
|
||||||
|
'shipments.shipping_price',
|
||||||
|
'shipments.total_fee',
|
||||||
|
'shipments.sender_name',
|
||||||
|
'shipments.receiver_name',
|
||||||
|
'shipments.created_at'
|
||||||
|
)
|
||||||
|
->whereBetween('shipments.created_at', [$fromDate, $toDate . ' 23:59:59'])
|
||||||
|
->orderByDesc('shipments.created_at');
|
||||||
|
|
||||||
|
if (!empty($this->data['shipment_status'])) {
|
||||||
|
$query->where('shipments.status', $this->data['shipment_status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->reportData = [
|
||||||
|
'type' => 'shipments',
|
||||||
|
'data' => $query->get(),
|
||||||
|
'summary' => [
|
||||||
|
'total_count' => $query->count(),
|
||||||
|
'total_fee' => $query->sum('total_fee'),
|
||||||
|
'total_weight' => $query->sum('chargeable_weight'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateFinancialSummary($fromDate, $toDate): void
|
||||||
|
{
|
||||||
|
// Total deposits
|
||||||
|
$deposits = DB::table('wallet_transactions')
|
||||||
|
->where('type', 'deposit')
|
||||||
|
->where('status', 'completed')
|
||||||
|
->whereBetween('created_at', [$fromDate, $toDate . ' 23:59:59'])
|
||||||
|
->sum('amount');
|
||||||
|
|
||||||
|
// Total withdrawals/order payments
|
||||||
|
$withdrawals = DB::table('wallet_transactions')
|
||||||
|
->whereIn('type', ['withdrawal', 'order_payment'])
|
||||||
|
->where('status', 'completed')
|
||||||
|
->whereBetween('created_at', [$fromDate, $toDate . ' 23:59:59'])
|
||||||
|
->sum('amount');
|
||||||
|
|
||||||
|
// Total shipments revenue
|
||||||
|
$shipmentsRevenue = DB::table('shipments')
|
||||||
|
->whereBetween('created_at', [$fromDate, $toDate . ' 23:59:59'])
|
||||||
|
->sum('total_fee');
|
||||||
|
|
||||||
|
// Total discount codes used
|
||||||
|
$discounts = DB::table('discount_codes')
|
||||||
|
->where('is_active', true)
|
||||||
|
->whereBetween('updated_at', [$fromDate, $toDate . ' 23:59:59'])
|
||||||
|
->sum('used_count');
|
||||||
|
|
||||||
|
// Wallet balances
|
||||||
|
$totalBalance = DB::table('wallets')->sum('balance');
|
||||||
|
$totalDeposited = DB::table('wallets')->sum('total_deposited');
|
||||||
|
$totalWithdrawn = DB::table('wallets')->sum('total_withdrawn');
|
||||||
|
|
||||||
|
$this->reportData = [
|
||||||
|
'type' => 'financial_summary',
|
||||||
|
'summary' => [
|
||||||
|
'deposits' => $deposits,
|
||||||
|
'withdrawals' => abs($withdrawals),
|
||||||
|
'shipments_revenue' => $shipmentsRevenue,
|
||||||
|
'discounts_used' => $discounts,
|
||||||
|
'total_balance' => $totalBalance,
|
||||||
|
'total_deposited' => $totalDeposited,
|
||||||
|
'total_withdrawn' => $totalWithdrawn,
|
||||||
|
'net_profit' => $shipmentsRevenue - $withdrawals,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportExcel(): \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$type = $this->data['report_type'];
|
||||||
|
$filters = [
|
||||||
|
'from_date' => $this->data['from_date'],
|
||||||
|
'to_date' => $this->data['to_date'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($type === 'transactions') {
|
||||||
|
$filters['type'] = $this->data['transaction_type'] ?? null;
|
||||||
|
$export = new WalletTransactionsExport($filters);
|
||||||
|
$fileName = 'transactions_' . now()->format('Y-m-d_H-i') . '.xlsx';
|
||||||
|
} elseif ($type === 'shipments') {
|
||||||
|
$filters['status'] = $this->data['shipment_status'] ?? null;
|
||||||
|
$export = new ShipmentsExport($filters);
|
||||||
|
$fileName = 'shipments_' . now()->format('Y-m-d_H-i') . '.xlsx';
|
||||||
|
} else {
|
||||||
|
Notification::make()
|
||||||
|
->title('خطا')
|
||||||
|
->body('برای خلاصه مالی خروجی Excel موجود نیست.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Excel::download($export, $fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,8 @@ use Filament\Forms\Form;
|
|||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use App\Exports\ShipmentsExport;
|
||||||
|
use Filament\Tables\Actions\ExportAction;
|
||||||
|
|
||||||
class ShipmentResource extends Resource
|
class ShipmentResource extends Resource
|
||||||
{
|
{
|
||||||
@ -196,6 +198,18 @@ class ShipmentResource extends Resource
|
|||||||
Tables\Actions\ViewAction::make(),
|
Tables\Actions\ViewAction::make(),
|
||||||
Tables\Actions\EditAction::make(),
|
Tables\Actions\EditAction::make(),
|
||||||
])
|
])
|
||||||
|
->headerActions([
|
||||||
|
ExportAction::make()
|
||||||
|
->label('خروجی Excel')
|
||||||
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
|
->color('success')
|
||||||
|
->export(fn ($query) => new ShipmentsExport([
|
||||||
|
'status' => request()->input('filters.status'),
|
||||||
|
'type' => request()->input('filters.type'),
|
||||||
|
'direction' => request()->input('filters.direction'),
|
||||||
|
]))
|
||||||
|
->fileName('shipments_' . now()->format('Y-m-d_H-i')),
|
||||||
|
])
|
||||||
->bulkActions([
|
->bulkActions([
|
||||||
Tables\Actions\BulkActionGroup::make([
|
Tables\Actions\BulkActionGroup::make([
|
||||||
Tables\Actions\DeleteBulkAction::make(),
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
|||||||
@ -12,6 +12,9 @@ use Filament\Forms\Form;
|
|||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use App\Exports\WalletTransactionsExport;
|
||||||
|
use Filament\Tables\Actions\ExportAction;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
|
||||||
class WalletTransactionResource extends Resource
|
class WalletTransactionResource extends Resource
|
||||||
{
|
{
|
||||||
@ -133,6 +136,18 @@ class WalletTransactionResource extends Resource
|
|||||||
->actions([
|
->actions([
|
||||||
Tables\Actions\ViewAction::make(),
|
Tables\Actions\ViewAction::make(),
|
||||||
])
|
])
|
||||||
|
->headerActions([
|
||||||
|
ExportAction::make()
|
||||||
|
->label('خروجی Excel')
|
||||||
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
|
->color('success')
|
||||||
|
->export(fn ($query) => new WalletTransactionsExport([
|
||||||
|
'type' => request()->input('filters.type'),
|
||||||
|
'status' => request()->input('filters.status'),
|
||||||
|
'gateway' => request()->input('filters.gateway'),
|
||||||
|
]))
|
||||||
|
->fileName('transactions_' . now()->format('Y-m-d_H-i')),
|
||||||
|
])
|
||||||
->bulkActions([
|
->bulkActions([
|
||||||
Tables\Actions\BulkActionGroup::make([
|
Tables\Actions\BulkActionGroup::make([
|
||||||
Tables\Actions\DeleteBulkAction::make(),
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
|||||||
@ -29,6 +29,7 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||||
->pages([
|
->pages([
|
||||||
Dashboard::class,
|
Dashboard::class,
|
||||||
|
\App\Filament\Pages\Reports\FinancialReport::class,
|
||||||
])
|
])
|
||||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||||
->widgets([
|
->widgets([
|
||||||
|
|||||||
@ -1,8 +1,71 @@
|
|||||||
@import 'tailwindcss';
|
:root {
|
||||||
|
/* IFNEX Brand Colors */
|
||||||
|
--ifnex-primary: #f59e0b;
|
||||||
|
--ifnex-primary-dark: #d97706;
|
||||||
|
--ifnex-primary-light: #fbbf24;
|
||||||
|
--ifnex-success: #059669;
|
||||||
|
--ifnex-success-light: #d1fae5;
|
||||||
|
--ifnex-danger: #dc2626;
|
||||||
|
--ifnex-danger-light: #fee2e2;
|
||||||
|
--ifnex-warning: #f59e0b;
|
||||||
|
--ifnex-warning-light: #fef3c7;
|
||||||
|
--ifnex-info: #2563eb;
|
||||||
|
--ifnex-info-light: #dbeafe;
|
||||||
|
|
||||||
/* Custom IFNEX Filament Theme */
|
/* Neutral Colors */
|
||||||
|
--ifnex-gray-900: #111827;
|
||||||
|
--ifnex-gray-700: #374151;
|
||||||
|
--ifnex-gray-600: #4b5563;
|
||||||
|
--ifnex-gray-500: #6b7280;
|
||||||
|
--ifnex-gray-400: #9ca3af;
|
||||||
|
--ifnex-gray-300: #d1d5db;
|
||||||
|
--ifnex-gray-200: #e5e7eb;
|
||||||
|
--ifnex-gray-100: #f3f4f6;
|
||||||
|
--ifnex-gray-50: #f9fafb;
|
||||||
|
--ifnex-white: #ffffff;
|
||||||
|
|
||||||
/* Sidebar Styling */
|
/* Typography */
|
||||||
|
--ifnex-font-family: 'Vazirmatn', 'IranYekan', ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--ifnex-font-size-base: 14px;
|
||||||
|
--ifnex-line-height: 1.6;
|
||||||
|
|
||||||
|
/* Spacing */
|
||||||
|
--ifnex-spacing-xs: 0.25rem;
|
||||||
|
--ifnex-spacing-sm: 0.5rem;
|
||||||
|
--ifnex-spacing-md: 1rem;
|
||||||
|
--ifnex-spacing-lg: 1.5rem;
|
||||||
|
--ifnex-spacing-xl: 2rem;
|
||||||
|
--ifnex-spacing-2xl: 3rem;
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
--ifnex-radius-sm: 0.375rem;
|
||||||
|
--ifnex-radius-md: 0.5rem;
|
||||||
|
--ifnex-radius-lg: 0.75rem;
|
||||||
|
|
||||||
|
/* Shadows */
|
||||||
|
--ifnex-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
--ifnex-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||||
|
--ifnex-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
/* Transitions */
|
||||||
|
--ifnex-transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Reset */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--ifnex-font-family) !important;
|
||||||
|
font-size: var(--ifnex-font-size-base) !important;
|
||||||
|
line-height: var(--ifnex-line-height) !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
SIDEBAR
|
||||||
|
============================================ */
|
||||||
.fi-sidebar {
|
.fi-sidebar {
|
||||||
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%) !important;
|
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%) !important;
|
||||||
border-left: 1px solid rgba(245, 158, 11, 0.2) !important;
|
border-left: 1px solid rgba(245, 158, 11, 0.2) !important;
|
||||||
@ -18,39 +81,32 @@
|
|||||||
padding: 0.75rem !important;
|
padding: 0.75rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navigation Groups */
|
|
||||||
.fi-sidebar-nav-group {
|
.fi-sidebar-nav-group {
|
||||||
margin-bottom: 0.5rem !important;
|
margin-bottom: 0.5rem !important;
|
||||||
border-radius: 0.5rem !important;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-sidebar-nav-group-header {
|
.fi-sidebar-nav-group-header {
|
||||||
background: rgba(245, 158, 11, 0.08) !important;
|
background: rgba(245, 158, 11, 0.08) !important;
|
||||||
border-bottom: 1px solid rgba(245, 158, 11, 0.1) !important;
|
border-bottom: 1px solid rgba(245, 158, 11, 0.1) !important;
|
||||||
transition: all 0.2s ease !important;
|
padding: 0.5rem 0.75rem !important;
|
||||||
|
color: rgba(255, 255, 255, 0.7) !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
letter-spacing: 0.05em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-sidebar-nav-group-header:hover {
|
.fi-sidebar-nav-group-header:hover {
|
||||||
background: rgba(245, 158, 11, 0.15) !important;
|
background: rgba(245, 158, 11, 0.15) !important;
|
||||||
|
color: var(--ifnex-primary-light) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-sidebar-nav-group-item {
|
|
||||||
padding: 0.5rem 0.75rem !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
color: rgba(255, 255, 255, 0.7) !important;
|
|
||||||
transition: all 0.2s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fi-sidebar-nav-group-item:hover {
|
|
||||||
color: #fbbf24 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navigation Items */
|
|
||||||
.fi-sidebar-nav-item {
|
.fi-sidebar-nav-item {
|
||||||
margin: 0.15rem 0 !important;
|
margin: 0.15rem 0 !important;
|
||||||
border-radius: 0.5rem !important;
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
transition: all 0.2s ease !important;
|
transition: var(--ifnex-transition) !important;
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
@ -63,7 +119,7 @@
|
|||||||
transform: translateY(-50%) !important;
|
transform: translateY(-50%) !important;
|
||||||
width: 3px !important;
|
width: 3px !important;
|
||||||
height: 0 !important;
|
height: 0 !important;
|
||||||
background: #fbbf24 !important;
|
background: var(--ifnex-primary) !important;
|
||||||
border-radius: 3px 0 0 3px !important;
|
border-radius: 3px 0 0 3px !important;
|
||||||
transition: height 0.2s ease !important;
|
transition: height 0.2s ease !important;
|
||||||
}
|
}
|
||||||
@ -83,11 +139,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fi-sidebar-nav-item.active .fi-sidebar-nav-item-label {
|
.fi-sidebar-nav-item.active .fi-sidebar-nav-item-label {
|
||||||
color: #fbbf24 !important;
|
color: var(--ifnex-primary-light) !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Icons */
|
|
||||||
.fi-sidebar-nav-item-icon {
|
.fi-sidebar-nav-item-icon {
|
||||||
color: rgba(255, 255, 255, 0.6) !important;
|
color: rgba(255, 255, 255, 0.6) !important;
|
||||||
transition: color 0.2s ease !important;
|
transition: color 0.2s ease !important;
|
||||||
@ -95,193 +150,417 @@
|
|||||||
|
|
||||||
.fi-sidebar-nav-item:hover .fi-sidebar-nav-item-icon,
|
.fi-sidebar-nav-item:hover .fi-sidebar-nav-item-icon,
|
||||||
.fi-sidebar-nav-item.active .fi-sidebar-nav-item-icon {
|
.fi-sidebar-nav-item.active .fi-sidebar-nav-item-icon {
|
||||||
color: #fbbf24 !important;
|
color: var(--ifnex-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Labels */
|
|
||||||
.fi-sidebar-nav-item-label {
|
.fi-sidebar-nav-item-label {
|
||||||
color: rgba(255, 255, 255, 0.85) !important;
|
color: rgba(255, 255, 255, 0.85) !important;
|
||||||
font-size: 0.875rem !important;
|
font-size: 0.875rem !important;
|
||||||
transition: color 0.2s ease !important;
|
transition: color 0.2s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-sidebar-nav-group-item {
|
/* ============================================
|
||||||
color: rgba(255, 255, 255, 0.5) !important;
|
MAIN CONTENT
|
||||||
font-size: 0.75rem !important;
|
============================================ */
|
||||||
text-transform: uppercase !important;
|
.fi-main {
|
||||||
letter-spacing: 0.05em !important;
|
max-width: 1400px !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
padding: var(--ifnex-spacing-lg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Badges */
|
@media (max-width: 1024px) {
|
||||||
.fi-badge {
|
.fi-main {
|
||||||
font-weight: 600 !important;
|
padding: var(--ifnex-spacing-md) !important;
|
||||||
border-radius: 9999px !important;
|
max-width: 100% !important;
|
||||||
font-size: 0.75rem !important;
|
}
|
||||||
padding: 0.125rem 0.5rem !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cards and Panels */
|
@media (max-width: 768px) {
|
||||||
|
.fi-main {
|
||||||
|
padding: var(--ifnex-spacing-sm) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
PAGE HEADER
|
||||||
|
============================================ */
|
||||||
|
.fi-page-header {
|
||||||
|
margin-bottom: var(--ifnex-spacing-lg) !important;
|
||||||
|
padding-bottom: var(--ifnex-spacing-md) !important;
|
||||||
|
border-bottom: 2px solid var(--ifnex-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-page-header h1 {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.fi-page-header h1 {
|
||||||
|
font-size: 1.25rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
CARDS
|
||||||
|
============================================ */
|
||||||
.fi-card {
|
.fi-card {
|
||||||
border-radius: 0.75rem !important;
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
border: 1px solid #e5e7eb !important;
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05) !important;
|
box-shadow: var(--ifnex-shadow-sm) !important;
|
||||||
transition: all 0.2s ease !important;
|
margin-bottom: var(--ifnex-spacing-md) !important;
|
||||||
}
|
|
||||||
|
|
||||||
.fi-card:hover {
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
.fi-btn {
|
|
||||||
border-radius: 0.5rem !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
transition: all 0.2s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fi-btn-primary {
|
|
||||||
background: #f59e0b !important;
|
|
||||||
border-color: #f59e0b !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fi-btn-primary:hover {
|
|
||||||
background: #d97706 !important;
|
|
||||||
border-color: #d97706 !important;
|
|
||||||
transform: translateY(-1px) !important;
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(245, 158, 11, 0.3) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tables */
|
|
||||||
.fi-ta-table {
|
|
||||||
border-radius: 0.75rem !important;
|
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
|
background: var(--ifnex-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-card-header {
|
||||||
|
background: var(--ifnex-gray-50) !important;
|
||||||
|
padding: 1rem 1.25rem !important;
|
||||||
|
border-bottom: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-card-header h2 {
|
||||||
|
font-size: 1.125rem !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-card-body {
|
||||||
|
padding: 1.25rem !important;
|
||||||
|
background: var(--ifnex-white) !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
STATS WIDGET
|
||||||
|
============================================ */
|
||||||
|
.fi-wi-stats-overview {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)) !important;
|
||||||
|
gap: var(--ifnex-spacing-md) !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-wi-stats-overview-card {
|
||||||
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
padding: 1.25rem !important;
|
||||||
|
transition: var(--ifnex-transition) !important;
|
||||||
|
background: var(--ifnex-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-wi-stats-overview-card:hover {
|
||||||
|
transform: translateY(-2px) !important;
|
||||||
|
box-shadow: var(--ifnex-shadow-md) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-wi-stats-overview-card h3 {
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
color: var(--ifnex-gray-500) !important;
|
||||||
|
margin: 0 0 0.5rem 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-wi-stats-overview-card p {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.fi-wi-stats-overview {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-wi-stats-overview-card p {
|
||||||
|
font-size: 1.25rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
TABLES
|
||||||
|
============================================ */
|
||||||
|
.fi-ta-table {
|
||||||
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-ta-header-cell {
|
.fi-ta-header-cell {
|
||||||
background: #f9fafb !important;
|
background: var(--ifnex-gray-50) !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
color: #374151 !important;
|
color: var(--ifnex-gray-700) !important;
|
||||||
font-size: 0.75rem !important;
|
font-size: 0.875rem !important;
|
||||||
text-transform: uppercase !important;
|
text-transform: uppercase !important;
|
||||||
letter-spacing: 0.05em !important;
|
letter-spacing: 0.025em !important;
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-ta-row {
|
.fi-ta-cell {
|
||||||
transition: background-color 0.15s ease !important;
|
padding: 0.75rem 1rem !important;
|
||||||
|
color: var(--ifnex-gray-700) !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
border-bottom: 1px solid var(--ifnex-gray-100) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-ta-row:hover {
|
.fi-ta-row:hover {
|
||||||
background-color: #fffbeb !important;
|
background-color: #fffbeb !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forms */
|
/* ============================================
|
||||||
.fi-input {
|
FORMS
|
||||||
border-radius: 0.5rem !important;
|
============================================ */
|
||||||
border-color: #d1d5db !important;
|
.fi-input,
|
||||||
transition: all 0.2s ease !important;
|
.fi-select,
|
||||||
|
.fi-textarea {
|
||||||
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
|
border-color: var(--ifnex-gray-300) !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
padding: 0.625rem 0.875rem !important;
|
||||||
|
transition: var(--ifnex-transition) !important;
|
||||||
|
width: 100% !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-input:focus {
|
.fi-input:focus,
|
||||||
border-color: #fbbf24 !important;
|
.fi-select:focus,
|
||||||
|
.fi-textarea:focus {
|
||||||
|
border-color: var(--ifnex-primary) !important;
|
||||||
box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1) !important;
|
box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1) !important;
|
||||||
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-select {
|
.fi-label {
|
||||||
border-radius: 0.5rem !important;
|
font-weight: 500 !important;
|
||||||
|
color: var(--ifnex-gray-700) !important;
|
||||||
|
margin-bottom: 0.375rem !important;
|
||||||
|
display: block !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Widgets */
|
/* ============================================
|
||||||
.fi-wi-stats-overview {
|
BUTTONS
|
||||||
gap: 1rem !important;
|
============================================ */
|
||||||
|
.fi-btn {
|
||||||
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
padding: 0.625rem 1.25rem !important;
|
||||||
|
transition: var(--ifnex-transition) !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
gap: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-wi-stats-overview-card {
|
.fi-btn-primary {
|
||||||
border-radius: 0.75rem !important;
|
background: var(--ifnex-primary) !important;
|
||||||
border: 1px solid #e5e7eb !important;
|
border-color: var(--ifnex-primary) !important;
|
||||||
transition: all 0.2s ease !important;
|
color: var(--ifnex-white) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-wi-stats-overview-card:hover {
|
.fi-btn-primary:hover {
|
||||||
transform: translateY(-2px) !important;
|
background: var(--ifnex-primary-dark) !important;
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
|
border-color: var(--ifnex-primary-dark) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
box-shadow: var(--ifnex-shadow-md) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dashboard */
|
/* ============================================
|
||||||
.fi-page-header {
|
REPORTS PAGE
|
||||||
border-bottom: 2px solid #fbbf24 !important;
|
============================================ */
|
||||||
margin-bottom: 1.5rem !important;
|
.ifnex-report-page {
|
||||||
padding-bottom: 1rem !important;
|
max-width: 1200px !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
padding: var(--ifnex-spacing-lg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Top Navigation */
|
.ifnex-report-container {
|
||||||
.fi-topbar {
|
background: var(--ifnex-white) !important;
|
||||||
border-bottom: 1px solid #f3f4f6 !important;
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
background: #ffffff !important;
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
padding: var(--ifnex-spacing-lg) !important;
|
||||||
|
box-shadow: var(--ifnex-shadow-sm) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Notifications */
|
.section-title {
|
||||||
.fi-notification {
|
font-size: 1.125rem !important;
|
||||||
border-radius: 0.75rem !important;
|
|
||||||
border-right: 4px solid #fbbf24 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modals */
|
|
||||||
.fi-modal {
|
|
||||||
border-radius: 0.75rem !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dropdowns */
|
|
||||||
.fi-dropdown {
|
|
||||||
border-radius: 0.5rem !important;
|
|
||||||
border: 1px solid #e5e7eb !important;
|
|
||||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tabs */
|
|
||||||
.fi-tabs {
|
|
||||||
border-bottom: 2px solid #f3f4f6 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fi-tab {
|
|
||||||
border-bottom: 2px solid transparent !important;
|
|
||||||
margin-bottom: -2px !important;
|
|
||||||
transition: all 0.2s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fi-tab:hover {
|
|
||||||
border-bottom-color: #fbbf24 !important;
|
|
||||||
color: #d97706 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fi-tab.active {
|
|
||||||
border-bottom-color: #fbbf24 !important;
|
|
||||||
color: #d97706 !important;
|
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
margin: var(--ifnex-spacing-lg) 0 var(--ifnex-spacing-md) 0 !important;
|
||||||
|
padding-bottom: var(--ifnex-spacing-sm) !important;
|
||||||
|
border-bottom: 2px solid var(--ifnex-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollbar */
|
.report-summary-grid {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important;
|
||||||
|
gap: var(--ifnex-spacing-md) !important;
|
||||||
|
margin: var(--ifnex-spacing-md) 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-card {
|
||||||
|
background: var(--ifnex-white) !important;
|
||||||
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
|
padding: 1.25rem !important;
|
||||||
|
text-align: center !important;
|
||||||
|
transition: var(--ifnex-transition) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-card:hover {
|
||||||
|
box-shadow: var(--ifnex-shadow-sm) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-card-title {
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
color: var(--ifnex-gray-500) !important;
|
||||||
|
margin-bottom: var(--ifnex-spacing-sm) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-card-value {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
color: var(--ifnex-gray-900) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.report-summary-grid {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-card-value {
|
||||||
|
font-size: 1.25rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
REPORT TABLES
|
||||||
|
============================================ */
|
||||||
|
.report-table-container {
|
||||||
|
overflow-x: auto !important;
|
||||||
|
margin-top: var(--ifnex-spacing-lg) !important;
|
||||||
|
border-radius: var(--ifnex-radius-lg) !important;
|
||||||
|
border: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-table {
|
||||||
|
width: 100% !important;
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-table thead {
|
||||||
|
background: var(--ifnex-gray-50) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-table th {
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
|
text-align: right !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
color: var(--ifnex-gray-700) !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
letter-spacing: 0.025em !important;
|
||||||
|
border-bottom: 1px solid var(--ifnex-gray-200) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-table td {
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
|
color: var(--ifnex-gray-700) !important;
|
||||||
|
border-bottom: 1px solid var(--ifnex-gray-100) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-table tbody tr:hover {
|
||||||
|
background-color: #fffbeb !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.report-table {
|
||||||
|
font-size: 0.8125rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-table th,
|
||||||
|
.report-table td {
|
||||||
|
padding: 0.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
ALERTS
|
||||||
|
============================================ */
|
||||||
|
.fi-alert {
|
||||||
|
border-radius: var(--ifnex-radius-md) !important;
|
||||||
|
padding: var(--ifnex-spacing-md) var(--ifnex-spacing-lg) !important;
|
||||||
|
margin-bottom: var(--ifnex-spacing-md) !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-alert-success {
|
||||||
|
background: var(--ifnex-success-light) !important;
|
||||||
|
color: var(--ifnex-success) !important;
|
||||||
|
border-right: 4px solid var(--ifnex-success) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi-alert-danger {
|
||||||
|
background: var(--ifnex-danger-light) !important;
|
||||||
|
color: var(--ifnex-danger) !important;
|
||||||
|
border-right: 4px solid var(--ifnex-danger) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
SCROLLBAR
|
||||||
|
============================================ */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px !important;
|
width: 8px !important;
|
||||||
height: 8px !important;
|
height: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: #f1f5f9 !important;
|
background: var(--ifnex-gray-100) !important;
|
||||||
|
border-radius: var(--ifnex-radius-sm) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: #cbd5e1 !important;
|
background: var(--ifnex-gray-300) !important;
|
||||||
border-radius: 4px !important;
|
border-radius: var(--ifnex-radius-sm) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: #94a3b8 !important;
|
background: var(--ifnex-gray-400) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode adjustments */
|
/* ============================================
|
||||||
@media (prefers-color-scheme: dark) {
|
TEXT COLORS
|
||||||
.fi-sidebar {
|
============================================ */
|
||||||
background: linear-gradient(180deg, #0f172a 0%, #1e293b 100%) !important;
|
.text-gray-900 { color: var(--ifnex-gray-900) !important; }
|
||||||
}
|
.text-gray-700 { color: var(--ifnex-gray-700) !important; }
|
||||||
}
|
.text-gray-600 { color: var(--ifnex-gray-600) !important; }
|
||||||
|
.text-gray-500 { color: var(--ifnex-gray-500) !important; }
|
||||||
|
.text-white { color: var(--ifnex-white) !important; }
|
||||||
|
.text-green-700 { color: var(--ifnex-success) !important; }
|
||||||
|
.text-red-700 { color: var(--ifnex-danger) !important; }
|
||||||
|
.text-blue-700 { color: var(--ifnex-info) !important; }
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
BACKGROUNDS
|
||||||
|
============================================ */
|
||||||
|
.bg-white { background-color: var(--ifnex-white) !important; }
|
||||||
|
.bg-gray-50 { background-color: var(--ifnex-gray-50) !important; }
|
||||||
|
.bg-gray-100 { background-color: var(--ifnex-gray-100) !important; }
|
||||||
|
.bg-green-50 { background-color: var(--ifnex-success-light) !important; }
|
||||||
|
.bg-red-50 { background-color: var(--ifnex-danger-light) !important; }
|
||||||
|
.bg-yellow-50 { background-color: var(--ifnex-warning-light) !important; }
|
||||||
|
.bg-blue-50 { background-color: var(--ifnex-info-light) !important; }
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
BORDERS
|
||||||
|
============================================ */
|
||||||
|
.border-gray-200 { border-color: var(--ifnex-gray-200) !important; }
|
||||||
|
.border-gray-300 { border-color: var(--ifnex-gray-300) !important; }
|
||||||
|
|||||||
@ -0,0 +1,164 @@
|
|||||||
|
<x-filament-panels::page>
|
||||||
|
<div class="ifnex-report-page">
|
||||||
|
{{ $this->form }}
|
||||||
|
|
||||||
|
@if ($reportData)
|
||||||
|
<div class="ifnex-report-container">
|
||||||
|
<h2 class="section-title">نتیجه گزارش</h2>
|
||||||
|
|
||||||
|
@if ($reportData['type'] === 'financial_summary')
|
||||||
|
<div class="report-summary-grid">
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">کل واریزیها</p>
|
||||||
|
<p class="report-card-value text-green-700">
|
||||||
|
{{ number_format($reportData['summary']['deposits']) }} ریال
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">کل برداشتها</p>
|
||||||
|
<p class="report-card-value text-red-700">
|
||||||
|
{{ number_format($reportData['summary']['withdrawals']) }} ریال
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">درآمد از مرسولات</p>
|
||||||
|
<p class="report-card-value text-blue-700">
|
||||||
|
{{ number_format($reportData['summary']['shipments_revenue']) }} ریال
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">سود خالص</p>
|
||||||
|
<p class="report-card-value text-purple-700">
|
||||||
|
{{ number_format($reportData['summary']['net_profit']) }} ریال
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="report-summary-grid">
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">موجودی کل کیف پولها</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_balance']) }} ریال</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">کل واریزیهای ثبت شده</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_deposited']) }} ریال</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">کل برداشتهای ثبت شده</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_withdrawn']) }} ریال</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($reportData['summary']['discounts_used'] > 0)
|
||||||
|
<div class="report-card" style="background: var(--ifnex-warning-light); border-color: var(--ifnex-primary-light);">
|
||||||
|
<p class="report-card-title">تخفیفهای استفاده شده</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['discounts_used']) }} بار</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($reportData['type'] === 'transactions' && $reportData['data']->count() > 0)
|
||||||
|
<div class="report-summary-grid">
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">تعداد تراکنشها</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_count']) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">مجموع واریزیها</p>
|
||||||
|
<p class="report-card-value text-green-700">
|
||||||
|
{{ number_format($reportData['summary']['deposits']) }} ریال
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">مجموع برداشتها</p>
|
||||||
|
<p class="report-card-value text-red-700">
|
||||||
|
{{ number_format($reportData['summary']['withdrawals']) }} ریال
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="report-table-container">
|
||||||
|
<table class="report-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>شناسه</th>
|
||||||
|
<th>کاربر</th>
|
||||||
|
<th>مبلغ</th>
|
||||||
|
<th>نوع</th>
|
||||||
|
<th>وضعیت</th>
|
||||||
|
<th>تاریخ</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($reportData['data'] as $item)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $item->id }}</td>
|
||||||
|
<td>{{ $item->user_name }}</td>
|
||||||
|
<td class="{{ $item->amount >= 0 ? 'text-green-700' : 'text-red-700' }}" style="font-weight: 600;">
|
||||||
|
{{ number_format($item->amount) }} ریال
|
||||||
|
</td>
|
||||||
|
<td>{{ $item->type }}</td>
|
||||||
|
<td>{{ $item->status }}</td>
|
||||||
|
<td>{{ \Morilog\Jalali\Jalalian::fromCarbon(\Carbon\Carbon::parse($item->created_at))->format('Y/m/d H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($reportData['type'] === 'shipments' && $reportData['data']->count() > 0)
|
||||||
|
<div class="report-summary-grid">
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">تعداد مرسولات</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_count']) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">هزینه کل مرسولات</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_fee']) }} ریال</p>
|
||||||
|
</div>
|
||||||
|
<div class="report-card">
|
||||||
|
<p class="report-card-title">وزن کل (kg)</p>
|
||||||
|
<p class="report-card-value">{{ number_format($reportData['summary']['total_weight'], 2) }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="report-table-container">
|
||||||
|
<table class="report-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>AWB</th>
|
||||||
|
<th>جهت</th>
|
||||||
|
<th>نوع</th>
|
||||||
|
<th>وضعیت</th>
|
||||||
|
<th>مبدا</th>
|
||||||
|
<th>مقصد</th>
|
||||||
|
<th>هزینه کل</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($reportData['data'] as $item)
|
||||||
|
<tr>
|
||||||
|
<td style="font-family: monospace;">{{ $item->awb_no }}</td>
|
||||||
|
<td>{{ $item->direction }}</td>
|
||||||
|
<td>{{ $item->type }}</td>
|
||||||
|
<td>{{ $item->status }}</td>
|
||||||
|
<td>{{ $item->from_country }}</td>
|
||||||
|
<td>{{ $item->to_country }}</td>
|
||||||
|
<td style="font-weight: 600;">{{ number_format($item->total_fee) }} ریال</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($reportData['data']->count() === 0)
|
||||||
|
<div class="report-card" style="text-align: center; padding: 3rem;">
|
||||||
|
<p style="color: var(--ifnex-gray-500); font-size: 1rem;">هیچ دادهای برای این بازه زمانی یافت نشد.</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</x-filament-panels::page>
|
||||||
Loading…
Reference in New Issue
Block a user