diff --git a/app/Http/Controllers/Company/General/BootstrapController.php b/app/Http/Controllers/Company/General/BootstrapController.php index b21409c5..fa5bf6cb 100644 --- a/app/Http/Controllers/Company/General/BootstrapController.php +++ b/app/Http/Controllers/Company/General/BootstrapController.php @@ -48,6 +48,7 @@ class BootstrapController extends Controller 'admin_page_title', 'copyright_text', 'save_pdf_to_disk', + 'show_sidebar_group_labels', ]); // Super admin mode — return admin-only menu with all companies listed @@ -92,6 +93,20 @@ class BootstrapController extends Controller $main_menu = $this->generateMenu('main_menu', $current_user); $setting_menu = $this->generateMenu('setting_menu', $current_user); + // Merge module-registered menu items into the main menu so they + // participate in the unified group + priority ordering. + foreach (ModuleRegistry::allMenu() as $slug => $item) { + $main_menu[] = [ + 'title' => __($item['title']), + 'link' => $item['link'], + 'icon' => $item['icon'], + 'name' => 'module-'.$slug, + 'group' => $item['group'] ?? 'modules', + 'group_label' => $item['group_label'] ?? 'navigation.modules', + 'priority' => $item['priority'] ?? 100, + ]; + } + $current_company = Company::find($request->header('company')); if ((! $current_company) || ($current_company && ! $current_user->hasCompany($current_company->id))) { @@ -119,7 +134,15 @@ class BootstrapController extends Controller 'main_menu' => $main_menu, 'setting_menu' => $setting_menu, 'modules' => Module::where('enabled', true)->pluck('name'), - 'module_menu' => array_values(ModuleRegistry::allMenu()), + 'user_menu' => collect(ModuleRegistry::allUserMenu()) + ->map(fn (array $item, string $slug) => [ + ...$item, + 'title' => __($item['title']), + 'name' => 'module-'.$slug, + ]) + ->sortBy('priority') + ->values() + ->all(), 'pending_invitations' => CompanyInvitationResource::collection($pendingInvitations), ]); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2b8622e1..531bcb1d 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -125,7 +125,8 @@ class AppServiceProvider extends ServiceProvider ->data('ability', $data['ability']) ->data('model', $data['model']) ->data('group', $data['group']) - ->data('group_label', $data['group_label'] ?? ''); + ->data('group_label', $data['group_label'] ?? '') + ->data('priority', $data['priority'] ?? 100); } public function bootAuth() diff --git a/app/Traits/GeneratesMenuTrait.php b/app/Traits/GeneratesMenuTrait.php index 8c467a65..e2eff819 100644 --- a/app/Traits/GeneratesMenuTrait.php +++ b/app/Traits/GeneratesMenuTrait.php @@ -20,6 +20,7 @@ trait GeneratesMenuTrait 'name' => $data->data['name'], 'group' => $data->data['group'], 'group_label' => $data->data['group_label'] ?? '', + 'priority' => $data->data['priority'] ?? 100, ]; } } diff --git a/config/invoiceshelf.php b/config/invoiceshelf.php index 60182c62..95499423 100644 --- a/config/invoiceshelf.php +++ b/config/invoiceshelf.php @@ -285,7 +285,9 @@ return [ 'main_menu' => [ [ 'title' => 'navigation.dashboard', - 'group' => 1, + 'group' => 'main', + 'group_label' => '', + 'priority' => 10, 'link' => '/admin/dashboard', 'icon' => 'HomeIcon', 'name' => 'Dashboard', @@ -295,7 +297,9 @@ return [ ], [ 'title' => 'navigation.customers', - 'group' => 1, + 'group' => 'main', + 'group_label' => '', + 'priority' => 20, 'link' => '/admin/customers', 'icon' => 'UserIcon', 'name' => 'Customers', @@ -305,7 +309,9 @@ return [ ], [ 'title' => 'navigation.items', - 'group' => 1, + 'group' => 'main', + 'group_label' => '', + 'priority' => 30, 'link' => '/admin/items', 'icon' => 'StarIcon', 'name' => 'Items', @@ -315,7 +321,9 @@ return [ ], [ 'title' => 'navigation.estimates', - 'group' => 2, + 'group' => 'documents', + 'group_label' => 'navigation.documents', + 'priority' => 10, 'link' => '/admin/estimates', 'icon' => 'DocumentIcon', 'name' => 'Estimates', @@ -325,7 +333,9 @@ return [ ], [ 'title' => 'navigation.invoices', - 'group' => 2, + 'group' => 'documents', + 'group_label' => 'navigation.documents', + 'priority' => 20, 'link' => '/admin/invoices', 'icon' => 'DocumentTextIcon', 'name' => 'Invoices', @@ -335,7 +345,9 @@ return [ ], [ 'title' => 'navigation.payments', - 'group' => 2, + 'group' => 'documents', + 'group_label' => 'navigation.documents', + 'priority' => 30, 'link' => '/admin/payments', 'icon' => 'CreditCardIcon', 'name' => 'Payments', @@ -345,7 +357,9 @@ return [ ], [ 'title' => 'navigation.expenses', - 'group' => 2, + 'group' => 'documents', + 'group_label' => 'navigation.documents', + 'priority' => 40, 'link' => '/admin/expenses', 'icon' => 'CalculatorIcon', 'name' => 'Expenses', @@ -355,7 +369,9 @@ return [ ], [ 'title' => 'navigation.modules', - 'group' => 3, + 'group' => 'admin', + 'group_label' => 'navigation.admin', + 'priority' => 10, 'link' => '/admin/modules', 'icon' => 'PuzzlePieceIcon', 'name' => 'Modules', @@ -365,7 +381,9 @@ return [ ], [ 'title' => 'navigation.members', - 'group' => 3, + 'group' => 'admin', + 'group_label' => 'navigation.admin', + 'priority' => 20, 'link' => '/admin/members', 'icon' => 'UsersIcon', 'name' => 'Members', @@ -375,7 +393,9 @@ return [ ], [ 'title' => 'navigation.reports', - 'group' => 3, + 'group' => 'admin', + 'group_label' => 'navigation.admin', + 'priority' => 30, 'link' => '/admin/reports', 'icon' => 'ChartBarIcon', 'name' => 'Reports', @@ -385,7 +405,9 @@ return [ ], [ 'title' => 'navigation.settings', - 'group' => 3, + 'group' => 'admin', + 'group_label' => 'navigation.admin', + 'priority' => 40, 'link' => '/admin/settings', 'icon' => 'CogIcon', 'name' => 'Settings', diff --git a/lang/en.json b/lang/en.json index 88c09fdd..b8db0008 100644 --- a/lang/en.json +++ b/lang/en.json @@ -14,6 +14,8 @@ "users": "Users", "members": "Members", "modules": "Modules", + "documents": "Documents", + "admin": "Administration", "administration": "Administration", "companies": "Companies", "all_users": "Users" @@ -773,6 +775,7 @@ "no_modules_installed": "No Modules Installed Yet!", "disable_warning": "All the settings for this particular will be reverted.", "what_you_get": "What you get", + "screenshots": "Screenshots", "sign_up_and_get_token": "Sign up & Get Token", "index": { "description": "Modules activated by your administrator on this instance. Each company configures its own settings independently.", @@ -911,7 +914,14 @@ "notes": "Record Notes", "exchange_rate": "Exchange Rate", "address_information": "Address Information", - "pdf_generation": "PDF Generation" + "pdf_generation": "PDF Generation", + "appearance": "Appearance" + }, + "appearance": { + "title": "Appearance", + "description": "Customize how the application looks and feels.", + "sidebar_group_labels": "Show sidebar group labels", + "sidebar_group_labels_desc": "Display section headers like Documents, Administration, and Modules in the sidebar navigation." }, "address_information": { "section_description": " You can update Your Address information using form below." diff --git a/resources/scripts/api/services/bootstrap.service.ts b/resources/scripts/api/services/bootstrap.service.ts index 5a22fabc..e11d9268 100644 --- a/resources/scripts/api/services/bootstrap.service.ts +++ b/resources/scripts/api/services/bootstrap.service.ts @@ -12,23 +12,10 @@ export interface MenuItem { icon: string group: string group_label?: string + priority?: number ability?: string } -/** - * Sidebar item registered by an active module via - * \InvoiceShelf\Modules\Registry::registerMenu() in the module's ServiceProvider::boot(). - * - * Distinct shape from MenuItem because module entries are namespaced (i18n - * keys come from the module's lang files) and don't carry group/ability — - * they always render under the dynamic "Modules" sidebar section. - */ -export interface ModuleMenuItem { - title: string - link: string - icon: string -} - export interface BootstrapResponse { current_user: User current_user_settings: Record @@ -42,7 +29,7 @@ export interface BootstrapResponse { config: Record global_settings: Record modules: string[] - module_menu?: ModuleMenuItem[] + user_menu?: Array<{ title: string; link: string; icon: string; priority: number; name: string }> admin_mode?: boolean pending_invitations?: Array<{ token: string diff --git a/resources/scripts/features/admin/routes.ts b/resources/scripts/features/admin/routes.ts index 289c8197..916648b6 100644 --- a/resources/scripts/features/admin/routes.ts +++ b/resources/scripts/features/admin/routes.ts @@ -14,6 +14,7 @@ const AdminBackupView = () => import('./views/settings/AdminBackupView.vue') const AdminFileDiskView = () => import('./views/settings/AdminFileDiskView.vue') const AdminFontView = () => import('./views/settings/AdminFontView.vue') const AdminUpdateAppView = () => import('./views/settings/AdminUpdateAppView.vue') +const AdminAppearanceView = () => import('./views/settings/AdminAppearanceView.vue') export const adminRoutes: RouteRecordRaw[] = [ { @@ -126,6 +127,14 @@ export const adminRoutes: RouteRecordRaw[] = [ }, component: AdminUpdateAppView, }, + { + path: 'appearance', + name: 'admin.settings.appearance', + meta: { + isSuperAdmin: true, + }, + component: AdminAppearanceView, + }, ], }, ], diff --git a/resources/scripts/features/admin/views/AdminSettingsView.vue b/resources/scripts/features/admin/views/AdminSettingsView.vue index a5bc9837..18dd7153 100644 --- a/resources/scripts/features/admin/views/AdminSettingsView.vue +++ b/resources/scripts/features/admin/views/AdminSettingsView.vue @@ -98,6 +98,11 @@ const menuItems = computed(() => [ link: '/admin/administration/settings/update-app', icon: 'ArrowPathIcon', }, + { + title: t('settings.menu_title.appearance'), + link: '/admin/administration/settings/appearance', + icon: 'PaintBrushIcon', + }, ]) watchEffect(() => { diff --git a/resources/scripts/features/admin/views/settings/AdminAppearanceView.vue b/resources/scripts/features/admin/views/settings/AdminAppearanceView.vue new file mode 100644 index 00000000..4fdfdb06 --- /dev/null +++ b/resources/scripts/features/admin/views/settings/AdminAppearanceView.vue @@ -0,0 +1,37 @@ + + + diff --git a/resources/scripts/layouts/CompanyLayout.vue b/resources/scripts/layouts/CompanyLayout.vue index dbbacb80..4658c153 100644 --- a/resources/scripts/layouts/CompanyLayout.vue +++ b/resources/scripts/layouts/CompanyLayout.vue @@ -29,7 +29,7 @@ diff --git a/resources/scripts/layouts/partials/SiteHeader.vue b/resources/scripts/layouts/partials/SiteHeader.vue index 3763c220..7db1add3 100644 --- a/resources/scripts/layouts/partials/SiteHeader.vue +++ b/resources/scripts/layouts/partials/SiteHeader.vue @@ -144,13 +144,30 @@ + + + + + +
+ diff --git a/resources/scripts/layouts/partials/SiteSidebar.vue b/resources/scripts/layouts/partials/SiteSidebar.vue index 8b27d410..1b7e5559 100644 --- a/resources/scripts/layouts/partials/SiteSidebar.vue +++ b/resources/scripts/layouts/partials/SiteSidebar.vue @@ -99,34 +99,6 @@ {{ $t(item.title) }} - - -
@@ -154,16 +126,18 @@ :key="index" class="p-0 m-0 mt-4 list-none" > -
- {{ $t(menu[0].group_label) }} -
-
+
- -
-
- {{ $t('modules.sidebar.section_title') }} -
-
- - - - {{ $t(item.title) }} - - -
-