+
+
+
-
تعداد کاربران
-
3 عدد
+
گردش انبارداری
+
+
+ {{ formatNumber(overview.totalWarehouseTransactions) }} تراکنش
+
-
+
+
+
+ {{ formatNumber(warehouseFlow.inCount) }} ورود
+
+
+
+ {{ formatNumber(warehouseFlow.outCount) }} خروج
+
+
+
+
+
+
+
+
+
+
+
+
کاربران سیستم
+
+
+ {{ formatNumber(overview.totalUsers) }} کاربر
+
+
+
+
+
+
+
+
+ تعریف شده در {{ formatNumber(overview.totalRoles) }} نقش سازمانی
+
diff --git a/src/components/dashboard/WarehouseActivityWidget.vue b/src/components/dashboard/WarehouseActivityWidget.vue
new file mode 100644
index 0000000..d07b1fd
--- /dev/null
+++ b/src/components/dashboard/WarehouseActivityWidget.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
آخرین گردشهای انبار
+
گزارش لحظهای ثبت ورود و خروج کالا در انبارها
+
+
+
+
+
+
+
+
+
+
+
+
+ مجموع ورودی
+
+ {{ formatNumber(warehouseFlow.inQuantity) }} واحد
+
+
+
+
+ {{ formatNumber(warehouseFlow.inCount) }} تراکنش
+
+
+
+
+
+
+
+
+
+ مجموع خروجی
+
+ {{ formatNumber(warehouseFlow.outQuantity) }} واحد
+
+
+
+
+ {{ formatNumber(warehouseFlow.outCount) }} تراکنش
+
+
+
+
+
+
+
+
+
+
+
+
+ تراکنش انباری ثبت نشده است
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ {{ tx.itemName }}
+
+
+ {{ tx.type === 'IN' ? 'ورود' : 'خروج' }}
+
+
+
+ انبار: {{ tx.warehouseName }}
+ •
+ توسط: {{ tx.actorName }}
+
+
+
+
+
+
+ {{ tx.type === 'IN' ? '+' : '-' }}{{ formatNumber(tx.quantity) }} {{ tx.unit }}
+
+
+ {{ formatJalaliDate(tx.date) }}
+
+
+
+
+
+
+
diff --git a/src/layout/AppTopbar.vue b/src/layout/AppTopbar.vue
index 4cbbef3..d42b697 100644
--- a/src/layout/AppTopbar.vue
+++ b/src/layout/AppTopbar.vue
@@ -2,12 +2,54 @@
import { useLayout } from '@/layout/composables/layout';
import AppConfigurator from './AppConfigurator.vue';
import Cookies from 'js-cookie';
+import axios from 'axios';
import { useRouter } from 'vue-router';
-const router = useRouter()
-const signOut = () => {
- Cookies.set('bm-token', '')
- router.push('/auth/login')
-}
+
+const router = useRouter();
+
+const signOut = async () => {
+ try {
+ await axios.post('/api/users/logout', {}, { withCredentials: true });
+ } catch (e) {
+ console.warn('Logout API error:', e?.message || e);
+ }
+
+ // Comprehensive cookie cleaning
+ Cookies.remove('bm-token');
+ Cookies.remove('bm-token', { domain: '.mbmanager.ir', path: '/' });
+ Cookies.remove('bm-token', { path: '/' });
+ Cookies.remove('belonging_token');
+ Cookies.remove('belonging_token', { domain: '.mbmanager.ir', path: '/' });
+ Cookies.remove('belonging_token', { path: '/' });
+ Cookies.remove('jwt');
+ Cookies.remove('jwt', { domain: '.mbmanager.ir', path: '/' });
+ Cookies.remove('jwt', { path: '/' });
+
+ // Force expire raw cookies in document.cookie
+ if (typeof document !== 'undefined') {
+ const cookiesToClear = ['bm-token', 'belonging_token', 'jwt'];
+ const domains = ['', '; domain=.mbmanager.ir', '; domain=' + window.location.hostname];
+ cookiesToClear.forEach((name) => {
+ domains.forEach((dom) => {
+ document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/${dom}`;
+ });
+ });
+ }
+
+ // Clear local & session storage
+ if (typeof localStorage !== 'undefined') {
+ localStorage.removeItem('bm-token');
+ localStorage.removeItem('belonging_token');
+ localStorage.removeItem('jwt');
+ }
+ if (typeof sessionStorage !== 'undefined') {
+ sessionStorage.clear();
+ }
+
+ delete axios.defaults.headers.common['Authorization'];
+
+ await router.replace('/auth/login');
+};
const { toggleMenu, toggleDarkMode, isDarkTheme } = useLayout();
@@ -15,7 +57,7 @@ const { toggleMenu, toggleDarkMode, isDarkTheme } = useLayout();