From fc0147f0462ff71caa9da5242f6f07c46ad6bdaf Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Sat, 25 Apr 2026 08:23:16 +0300 Subject: [PATCH 1/2] done ui enhancement --- package-lock.json | 17 +- src/application/dashboard/Dashboard.tsx | 36 +- src/application/logs/LogsPage.tsx | 17 +- .../admin/components/ModuleEnvironments.tsx | 4 +- .../modules/admin/components/ModuleList.tsx | 23 +- .../admin/components/ModulePermissions.tsx | 6 +- .../components/TenantModuleAssignment.tsx | 2 +- src/application/profile/ProfilePage.tsx | 4 +- src/application/roles/components/AddRoles.tsx | 12 +- src/application/roles/components/AllRoles.tsx | 12 +- src/application/settings/SettingsPage.tsx | 11 +- .../components/AddSubscriptions.tsx | 14 +- .../components/AllSubscriptions.tsx | 14 +- .../tenants/components/AddTenants.tsx | 20 +- .../tenants/components/AllTenants.tsx | 13 +- .../theme/components/AllPalettes.tsx | 17 +- src/application/users/components/AddUsers.tsx | 12 +- src/application/users/components/AllUsers.tsx | 14 +- src/components/custom/CustomTable.tsx | 9 +- src/components/layout/AppHeader.tsx | 116 ++---- src/components/layout/AppLayout.tsx | 13 +- src/components/layout/AppSidebar.tsx | 333 ++++++++++-------- src/components/layout/Backdrop.tsx | 2 +- src/index.css | 22 +- vite.config.ts | 4 + 25 files changed, 355 insertions(+), 392 deletions(-) diff --git a/package-lock.json b/package-lock.json index b18ae12..4ec5556 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,6 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -1794,7 +1793,6 @@ "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1805,7 +1803,6 @@ "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1865,7 +1862,6 @@ "integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", @@ -2113,7 +2109,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2236,7 +2231,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2606,7 +2600,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3118,7 +3111,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -3317,8 +3309,7 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/leaflet-control-geocoder": { "version": "3.3.1", @@ -3865,7 +3856,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3943,7 +3933,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -3953,7 +3942,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -4279,7 +4267,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4374,7 +4361,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -4505,7 +4491,6 @@ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/application/dashboard/Dashboard.tsx b/src/application/dashboard/Dashboard.tsx index 15c1e97..316f1c4 100644 --- a/src/application/dashboard/Dashboard.tsx +++ b/src/application/dashboard/Dashboard.tsx @@ -3,6 +3,7 @@ import { ExternalLink, Box, Search, Rocket } from 'lucide-react'; import { moduleApi, type Module } from '../modules/user/UserModuleApi'; import { useAuth } from '../../context/AuthContext'; import { useTranslation } from 'react-i18next'; +import CustomInput from '../../components/custom/CustomInput'; const Dashboard = () => { const { user } = useAuth(); @@ -83,31 +84,27 @@ const Dashboard = () => { }, [modules, searchTerm, t]); return ( -
+
{/* Hero Section */}
-
-

+
+

{t('dashboard:title')}, {user?.first_name || 'Admin'}! 👋

-

+

{t('dashboard:subtitle')}

-
-
- -
- setSearchTerm(e.target.value)} - className="block w-full pl-10 pr-3 py-2.5 bg-white border border-gray-200 rounded-xl leading-5 text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm hover:shadow-md" - /> -
+
+ setSearchTerm(e.target.value)} + leftIcon={} + /> +

@@ -116,11 +113,12 @@ const Dashboard = () => {
{loading ? (
- {[1, 2, 3, 4].map((i) => ( -
+ {[1, 2, 3, 4, 5, 6, 7, 8].map((i) => ( +
-
+
+
))}
diff --git a/src/application/logs/LogsPage.tsx b/src/application/logs/LogsPage.tsx index 3ace440..d7af77f 100644 --- a/src/application/logs/LogsPage.tsx +++ b/src/application/logs/LogsPage.tsx @@ -1,7 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { History, RefreshCcw } from "lucide-react"; import { useTranslation } from "react-i18next"; -import { CustomButton, CustomColumnFilter } from "../../components/custom"; +import { CustomColumnFilter, CustomButton } from "../../components/custom"; import { DataTable } from "../../components/custom/CustomTable"; import type { ColumnDef } from "../../components/custom/CustomTable"; import { @@ -17,6 +16,7 @@ import { SHARED_ADMIN_TABLE_PAGE_SIZE_STORAGE_KEY, resolveStoredTablePageSize, } from "../../lib/tablePageSize"; +import { RefreshCcw } from "lucide-react"; interface AuditLog extends Record { id: string; @@ -267,11 +267,14 @@ const LogsPage = () => { ); return ( -
-
-

- {t("title")} -

+
+
+
+

+ {t("title")} +

+

Track and audit all system activities and user actions.

+
{ const fetchData = async () => { if (!moduleId) return; - + try { const [moduleData, envsData] = await Promise.all([ adminModuleApi.getModule(moduleId), @@ -39,7 +39,7 @@ const ModuleEnvironments = () => { const handleSetDefault = async (envId: string) => { if (!moduleId) return; - + try { await adminModuleApi.setDefaultEnvironment(moduleId, envId); fetchData(); diff --git a/src/application/modules/admin/components/ModuleList.tsx b/src/application/modules/admin/components/ModuleList.tsx index b9ca4db..8d395b2 100644 --- a/src/application/modules/admin/components/ModuleList.tsx +++ b/src/application/modules/admin/components/ModuleList.tsx @@ -12,7 +12,7 @@ import CustomButton from '../../../../components/custom/CustomButton'; const ModuleList = () => { const navigate = useNavigate(); const { t } = useTranslation(['modules', 'common']); - const { listModules, deleteModule, loading } = useModuleApi(); + const { listModules, deleteModule, loading } = useModuleApi(); const [modules, setModules] = useState([]); const [showForm, setShowForm] = useState(false); const [selectedModule, setSelectedModule] = useState(null); @@ -63,7 +63,7 @@ const ModuleList = () => { if (loading && modules.length === 0) { return ( -
+
@@ -73,11 +73,11 @@ const ModuleList = () => { } return ( -
-
-
-

{t('registry.title')}

-

{t('registry.subtitle')}

+
+
+
+

{t('registry.title')}

+

{t('registry.subtitle')}

{
{ if (togglingId) return; diff --git a/src/application/modules/admin/components/ModulePermissions.tsx b/src/application/modules/admin/components/ModulePermissions.tsx index d9b760b..da49b09 100644 --- a/src/application/modules/admin/components/ModulePermissions.tsx +++ b/src/application/modules/admin/components/ModulePermissions.tsx @@ -11,14 +11,14 @@ const ModulePermissions = () => { const { moduleId } = useParams<{ moduleId: string }>(); const navigate = useNavigate(); const { getModule, getPermissions, syncPermissions, loading } = useModuleApi(); - + const [module, setModule] = useState(null); const [permissions, setPermissions] = useState([]); const [syncing, setSyncing] = useState(false); const fetchData = async () => { if (!moduleId) return; - + const [moduleData, permsData] = await Promise.all([ getModule(moduleId), getPermissions(moduleId) @@ -34,7 +34,7 @@ const ModulePermissions = () => { const handleSync = async () => { if (!moduleId) return; - + setSyncing(true); const result = await syncPermissions(moduleId); if (result) { diff --git a/src/application/modules/admin/components/TenantModuleAssignment.tsx b/src/application/modules/admin/components/TenantModuleAssignment.tsx index ee5ec1e..353a4b5 100644 --- a/src/application/modules/admin/components/TenantModuleAssignment.tsx +++ b/src/application/modules/admin/components/TenantModuleAssignment.tsx @@ -10,7 +10,7 @@ const TenantModuleAssignment = () => { const [availableModules, setAvailableModules] = useState([]); const [loading, setLoading] = useState(true); const [showAssignDialog, setShowAssignDialog] = useState(false); - const [selectedModule, setSelectedModule] = useState(''); + const [selectedModule, setSelectedModule] = useState(''); const [selectedEnv, setSelectedEnv] = useState('prod'); const fetchData = async () => { diff --git a/src/application/profile/ProfilePage.tsx b/src/application/profile/ProfilePage.tsx index d0346ea..ec86485 100644 --- a/src/application/profile/ProfilePage.tsx +++ b/src/application/profile/ProfilePage.tsx @@ -239,8 +239,8 @@ const ProfilePage: React.FC = () => { return (
{/* Header */} -
-

{t('title')}

+
+

{t('title')}

{t('subTitle')}

diff --git a/src/application/roles/components/AddRoles.tsx b/src/application/roles/components/AddRoles.tsx index 5263a33..7c98779 100644 --- a/src/application/roles/components/AddRoles.tsx +++ b/src/application/roles/components/AddRoles.tsx @@ -196,12 +196,12 @@ const AddRoles = () => {
-
-

{t('add')}

-

- {t('subTitle')} -

-
+
+

{t('add')}

+

+ {t('subTitle')} +

+
diff --git a/src/application/roles/components/AllRoles.tsx b/src/application/roles/components/AllRoles.tsx index 8d8119b..5474adc 100644 --- a/src/application/roles/components/AllRoles.tsx +++ b/src/application/roles/components/AllRoles.tsx @@ -464,8 +464,9 @@ const AllRoles = () => { return (
-
-

{t('title')}

+
+

{t('title')}

+

Define and manage user roles and their associated permissions.

@@ -476,16 +477,13 @@ const AllRoles = () => { - {isLoading ? ( -
- -
- ) : errorMessage ? ( + {errorMessage ? (
{errorMessage}
) : ( { return (
{/* Page Header */} -
-
-

- {t('settings.title')} -

-
+
+

+ {t('settings.title')} +

+

Manage your account settings and application preferences.

{/* Language Settings Section */} diff --git a/src/application/subscriptions/components/AddSubscriptions.tsx b/src/application/subscriptions/components/AddSubscriptions.tsx index 23db135..d581200 100644 --- a/src/application/subscriptions/components/AddSubscriptions.tsx +++ b/src/application/subscriptions/components/AddSubscriptions.tsx @@ -123,12 +123,14 @@ const AddSubscriptions = () => {
-

- Add Subscription Plan -

-

- Create a new plan with pricing and access permissions. -

+
+

+ Add Subscription Plan +

+

+ Create a new plan with pricing and access permissions. +

+
diff --git a/src/application/subscriptions/components/AllSubscriptions.tsx b/src/application/subscriptions/components/AllSubscriptions.tsx index 5f72b29..333941b 100644 --- a/src/application/subscriptions/components/AllSubscriptions.tsx +++ b/src/application/subscriptions/components/AllSubscriptions.tsx @@ -529,10 +529,9 @@ const AllSubscriptions = () => { return (
-
-

- Subscription Plans -

+
+

Subscription Plans

+

Create and manage different subscription tiers and their permissions.

@@ -541,16 +540,13 @@ const AllSubscriptions = () => {
- {isLoading ? ( -
- -
- ) : errorMessage ? ( + {errorMessage ? (
{errorMessage}
) : ( {
-
-

- {canCreateTenant ? "Add Tenant" : "Tenant Details"} -

-

- {canCreateTenant - ? "Create a new tenant with domain and subscription plan." - : "Your account is assigned to this tenant."} -

-
+
+

+ {canCreateTenant ? "Add Tenant" : "Tenant Details"} +

+

+ {canCreateTenant + ? "Create a new tenant with domain and subscription plan." + : "Your account is assigned to this tenant."} +

+
diff --git a/src/application/tenants/components/AllTenants.tsx b/src/application/tenants/components/AllTenants.tsx index 754d016..0249f06 100644 --- a/src/application/tenants/components/AllTenants.tsx +++ b/src/application/tenants/components/AllTenants.tsx @@ -12,7 +12,6 @@ import { CustomModal, CustomTable, CustomStatus, - CustomLoader, CustomActionMenu, CustomActionItem, } from "../../../components/custom"; @@ -643,8 +642,9 @@ const AllTenants = () => { return (
-
-

Tenants

+
+

Tenants

+

Manage and monitor all tenant accounts in the system.

@@ -653,16 +653,13 @@ const AllTenants = () => {
- {isLoading ? ( -
- -
- ) : errorMessage ? ( + {errorMessage ? (
{errorMessage}
) : ( { <>
-
-

{t('theme:title')}

- {/*

+

+

{t('theme:title')}

+

Manage system color themes and default palettes. -

*/} +

}> {t('theme:actions.create')}
- {isLoading ? ( -
- -
- ) : errorMessage ? ( + {errorMessage ? (
{errorMessage}
) : ( {
-
-

{t('add')}

-

- {t('subTitle')} -

-
+
+

{t('add')}

+

+ {t('subTitle')} +

+
diff --git a/src/application/users/components/AllUsers.tsx b/src/application/users/components/AllUsers.tsx index 1fd191c..1329f63 100644 --- a/src/application/users/components/AllUsers.tsx +++ b/src/application/users/components/AllUsers.tsx @@ -11,7 +11,7 @@ import { CustomInput, CustomModal, CustomTable, - CustomLoader, + CustomActionMenu, CustomActionItem, CustomStatus, @@ -587,8 +587,9 @@ const AllUsers = () => { return (
-
-

{t('title')}

+
+

{t('title')}

+

Manage system users, their roles, and account status.

@@ -599,16 +600,13 @@ const AllUsers = () => { - {isLoading ? ( -
- -
- ) : errorMessage ? ( + {errorMessage ? (
{errorMessage}
) : ( ) { return parts.filter(Boolean).join(" "); @@ -701,12 +702,8 @@ export function DataTable>( colSpan={visibleColumns.length} className="px-6 py-24 text-center text-[var(--text-secondary)]" > -
- - - - -

{t('common.loading')}

+
+
diff --git a/src/components/layout/AppHeader.tsx b/src/components/layout/AppHeader.tsx index d8bb111..20546e9 100644 --- a/src/components/layout/AppHeader.tsx +++ b/src/components/layout/AppHeader.tsx @@ -1,21 +1,23 @@ import { Menu, ChevronDown, + LogOut, } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useSidebar } from "../../context/SidebarContext"; import { useAuth } from "../../context/AuthContext"; +import { useTranslation } from "react-i18next"; import { authApi } from "../../application/authentication/AuthApi"; import { clearAuthCookies } from "../../lib/authCookies"; -import { useTranslation } from "react-i18next"; + const AppHeader: React.FC = () => { const { t } = useTranslation(['profile', 'common']); - const navigate = useNavigate(); - const { user, } = useAuth(); + const { user } = useAuth(); const { toggleMobileSidebar } = useSidebar(); + const navigate = useNavigate(); const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); const userMenuRef = useRef(null); @@ -50,12 +52,12 @@ const AppHeader: React.FC = () => { : "U"; return ( -
+
{/* LEFT: Mobile Toggle & Brand (Mobile only) */}
- SaaS + SaaS
- {/* Logistics Network Pattern - Multi-layer Design */} -
- {/* ... existing SVG content ... */} - - {/* Background grid pattern */} - - - - - - {/* Multiple route paths */} - - - - - {/* Hub nodes with glow effect */} - - - - - - - - - - - - - {/* Package icons */} - - - - - - - - - - - - - - - - - - {/* Connecting dots */} - - - - - - -
- {/* RIGHT: Actions */} -
+
{/* User Menu */}
@@ -132,11 +79,10 @@ const AppHeader: React.FC = () => { onClick={() => setIsUserMenuOpen((prev) => !prev)} className=" flex items-center gap-2 - pl-2 pr-1 py-1.5 - rounded-lg - hover:bg-(--header-border) - transition-colors - border border-transparent hover:border-(--header-border) + p-1 pr-3 + rounded-full border border-gray-200 + hover:border-gray-300 hover:bg-gray-50 + transition-all bg-white/50 " aria-expanded={isUserMenuOpen} > @@ -144,12 +90,12 @@ const AppHeader: React.FC = () => { {initials}
-
-

{fullName}

+
+

{fullName}

@@ -159,33 +105,45 @@ const AppHeader: React.FC = () => { {isUserMenuOpen && (
-
-

+

+

{user?.email}

+

+ {user?.role?.role_name || "User"} +

setIsUserMenuOpen(false)} - className="flex w-full items-center gap-2 px-4 py-2 text-sm text-(--text-primary) hover:bg-(--background-secondary) transition-colors" + className="flex w-full items-center gap-3 px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50 transition-colors" > +
+ +
{t('profile:header.profileSettings')} + +
diff --git a/src/components/layout/AppLayout.tsx b/src/components/layout/AppLayout.tsx index db83e84..f5083a2 100644 --- a/src/components/layout/AppLayout.tsx +++ b/src/components/layout/AppLayout.tsx @@ -2,15 +2,15 @@ import { SidebarProvider, useSidebar } from "../../context/SidebarContext"; import { Outlet } from "react-router-dom"; import AppHeader from "./AppHeader"; import AppSidebar from "./AppSidebar"; +import Backdrop from "./Backdrop"; const LayoutContent: React.FC = () => { const { isExpanded } = useSidebar(); - - return ( -
+
+
{ > -
- {/* Outlet Container - Ensures content is constrained but responsive */} - +
+
+ +
diff --git a/src/components/layout/AppSidebar.tsx b/src/components/layout/AppSidebar.tsx index 3f3d0e7..22d8752 100644 --- a/src/components/layout/AppSidebar.tsx +++ b/src/components/layout/AppSidebar.tsx @@ -15,6 +15,7 @@ import { ChevronDown, ChevronRight, CreditCard, + FileText, } from "lucide-react"; import { useSidebar } from "../../context/SidebarContext"; import { usePermission } from "../../lib/usePermission"; @@ -82,7 +83,7 @@ const navItems: NavItem[] = [ access: "modules.view", }, { - icon: , + icon: , name: "Logs", path: "/logs", access: "admin.logs.read", @@ -124,6 +125,7 @@ const AppSidebar: React.FC = () => { 'Settings': 'settings', 'Modules': 'modules', 'Logs': 'logs', + 'Subscriptions': 'subscriptions', }; const key = keyMap[name]; @@ -131,7 +133,6 @@ const AppSidebar: React.FC = () => { return t(`nav.${key}`, name); } - // Try other namespaces if not in nav map return t([ `modules:${name}`, `dashboard:${name}`, @@ -140,22 +141,6 @@ const AppSidebar: React.FC = () => { ]); }; - - const isActive = useCallback( - (path: string) => location.pathname === path || location.pathname.startsWith(path + "/"), - [location.pathname] - ); - - const isSubmenuRouteActive = useCallback( - (submenu?: SubMenuItem[]) => - submenu?.some( - (subItem) => - location.pathname === subItem.path || - location.pathname.startsWith(subItem.path + "/") - ) ?? false, - [location.pathname] - ); - const visibleNavItems = useMemo( () => navItems.filter((item) => { if (!item.access) return true; @@ -169,11 +154,55 @@ const AppSidebar: React.FC = () => { [can] ); + const isActive = useCallback( + (path: string) => location.pathname === path || location.pathname.startsWith(path + "/"), + [location.pathname] + ); + + const groupedNavItems = useMemo(() => { + const groups: { label: string; items: NavItem[] }[] = []; + const addedPaths = new Set(); + + const getItems = (names: string[]) => { + const filtered = visibleNavItems.filter(item => + names.includes(item.name) && !addedPaths.has(item.path) + ); + filtered.forEach(item => addedPaths.add(item.path)); + return filtered; + }; + + // 1. Overview + const dashboardItems = getItems(["Dashboard"]); + if (dashboardItems.length > 0) { + groups.push({ label: t('nav.groups.overview', 'Overview'), items: dashboardItems }); + } + + // 2. Management + const managementItems = getItems(["Tenants", "Subscriptions", "Roles", "Users"]); + if (managementItems.length > 0) { + groups.push({ label: t('nav.groups.management', 'Management'), items: managementItems }); + } + + // 3. System + const systemItems = getItems(["Themes", "Modules", "Logs", "Settings"]); + if (systemItems.length > 0) { + groups.push({ label: t('nav.groups.system', 'System'), items: systemItems }); + } + + // Catch-all + const remaining = visibleNavItems.filter(item => !addedPaths.has(item.path)); + if (remaining.length > 0) { + groups.push({ label: t('nav.groups.other', 'Other'), items: remaining }); + } + + return groups; + }, [visibleNavItems, t]); + const [openSubmenus, setOpenSubmenus] = useState>({}); const toggleSubmenu = (name: string) => { if (!isExpanded && !isMobile) { - toggleSidebar(); // Auto expand when clicking submenu in collapsed state + toggleSidebar(); } setOpenSubmenus(prev => ({ ...prev, [name]: !prev[name] })); }; @@ -192,25 +221,15 @@ const AppSidebar: React.FC = () => { return null; } - // Sidebar width logic const sidebarWidth = isExpanded ? "w-[260px]" : "w-[80px]"; const mobileClasses = isMobileOpen ? "translate-x-0" : "-translate-x-full"; return ( <> - {/* Mobile Backdrop */} - {isMobileOpen && ( - {/* Footer */} -
- {/* Logout */} +
diff --git a/src/components/layout/Backdrop.tsx b/src/components/layout/Backdrop.tsx index 5dca0c0..bb3104c 100644 --- a/src/components/layout/Backdrop.tsx +++ b/src/components/layout/Backdrop.tsx @@ -7,7 +7,7 @@ const Backdrop: React.FC = () => { return (
); diff --git a/src/index.css b/src/index.css index 6d2efa5..95666fa 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,4 @@ -@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,100..900;1,100..900&display=swap"); @import "tailwindcss"; @keyframes fadeIn { @@ -15,7 +15,7 @@ /* Default Palette (Navy) - fallback */ --primary: #3B82F6; --primary-hover: #2563EB; - + --sidebar-bg: #1D2A4B; --sidebar-text: #F1F5F9; --sidebar-text-muted: #94A3B8; @@ -23,14 +23,14 @@ --sidebar-active-text: #FFFFFF; --sidebar-hover-bg: rgba(255,255,255,0.2); --sidebar-border: #334155; - + --header-bg: #FFFFFF; --header-text: #1F2937; --header-border: #E5E7EB; - + --background: #F9FAFB; --background-secondary: #FFFFFF; - + --table-header-bg: #F8FAFC; --table-row-hover: #F1F5F9; --table-border: #E2E8F0; @@ -40,16 +40,26 @@ --text-secondary: #64748B; --card-bg: #FFFFFF; --card-border: #E2E8F0; + + /* Optional: smoother focus ring for any inputs inside your custom components */ + --ui-ring: rgba(17, 24, 39, 0.12); /* gray-900/12 */ } html, body { - font-family: "Outfit", sans-serif; + font-family: "Archivo", sans-serif; @apply text-base antialiased; } } @layer utilities { + /* Optional: smoother focus ring for any inputs inside your custom components */ + .ui-focus :is(input, textarea, select, button):focus-visible { + outline: none; + box-shadow: 0 0 0 3px var(--ui-ring); + border-radius: 12px; + } + /* Hide number input arrows/spinners */ .no-spinner::-webkit-inner-spin-button, .no-spinner::-webkit-outer-spin-button { diff --git a/vite.config.ts b/vite.config.ts index 37fadb7..c21ca36 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,6 +7,10 @@ export default defineConfig(({ mode }) => { console.log(`\n 🔌 Backend API URL: \x1b[36m${env.VITE_API_BASE_URL}\x1b[0m\n`); return { + server: { + port: 5174, + + }, plugins: [react(), tailwindcss()], } }) From 3961978bf47ba4db50082d72e1c9171af8b3af53 Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Sat, 25 Apr 2026 08:34:57 +0300 Subject: [PATCH 2/2] done loader changes --- .../admin/components/ModuleEnvironments.tsx | 45 +++++++++++-------- .../admin/components/ModulePermissions.tsx | 37 ++++++++------- .../components/TenantModuleAssignment.tsx | 23 +++++++--- 3 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/application/modules/admin/components/ModuleEnvironments.tsx b/src/application/modules/admin/components/ModuleEnvironments.tsx index 4ca873f..aa385cc 100644 --- a/src/application/modules/admin/components/ModuleEnvironments.tsx +++ b/src/application/modules/admin/components/ModuleEnvironments.tsx @@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom'; import { ArrowLeft, Plus, Edit, Trash2, Check, X } from 'lucide-react'; import { adminModuleApi } from '../AdminModuleApi'; import type { ModuleEnvironment, Module } from '../AdminModuleTypes'; +import Loader from '../../../../components/custom/CustomLoader'; import EnvironmentForm from './EnvironmentForm'; import { CustomConfirmationModal } from '../../../../components/custom'; @@ -63,12 +64,10 @@ const ModuleEnvironments = () => { } }; - if (loading) { - return
Loading...
; - } + return ( -
+
-
-

- {module?.module_name} - Environments +
+

+ {module?.module_name || 'Loading...'} - Environments

-

Configure environment-specific settings

+

Configure environment-specific settings

+
+
+
-

-
- {environments.map((env) => ( + {loading ? ( +
+ +
+ ) : ( +
+ {environments.map((env) => (
@@ -159,9 +165,10 @@ const ModuleEnvironments = () => {
))} -
+
+ )} - {environments.length === 0 && ( + {environments.length === 0 && !loading && (

No environments configured

-
-

- {module?.module_name} - Permissions +
+

+ {module?.module_name || 'Loading...'} - Permissions

-

View and sync permissions from module

+

View and sync permissions from module

+
+
+ } + > + {syncing ? 'Syncing...' : 'Sync Permissions'} +
- } - > - {syncing ? 'Syncing...' : 'Sync Permissions'} -

- {permissions.length > 0 ? ( + {loading ? ( +
+ +
+ ) : permissions.length > 0 ? (
diff --git a/src/application/modules/admin/components/TenantModuleAssignment.tsx b/src/application/modules/admin/components/TenantModuleAssignment.tsx index 353a4b5..7dbe806 100644 --- a/src/application/modules/admin/components/TenantModuleAssignment.tsx +++ b/src/application/modules/admin/components/TenantModuleAssignment.tsx @@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom'; import { adminModuleApi } from '../AdminModuleApi'; import type { TenantModule, Module } from '../AdminModuleTypes'; import { Plus, Trash2 } from 'lucide-react'; +import Loader from '../../../../components/custom/CustomLoader'; const TenantModuleAssignment = () => { const { tenantId } = useParams<{ tenantId: string }>(); @@ -85,12 +86,15 @@ const TenantModuleAssignment = () => { } }; - if (loading) return
Loading...
; + return ( -
+
-

Tenant Modules

+
+

Tenant Modules

+

Manage module assignments for this tenant

+
-
- {assignedModules.map((tm) => ( + {loading ? ( +
+ +

Loading assignments...

+
+ ) : ( +
+ {assignedModules.map((tm) => (
@@ -140,7 +150,8 @@ const TenantModuleAssignment = () => {
))} -
+
+ )} {showAssignDialog && (