diff --git a/apps/settings/css/settings.scss b/apps/settings/css/settings.scss index a80ab6a5d9f..e3d03150fbe 100644 --- a/apps/settings/css/settings.scss +++ b/apps/settings/css/settings.scss @@ -19,27 +19,6 @@ input { clear: both; } -/* icons for sidebar */ -.nav-icon-personal-settings { - @include functions.icon-color('personal', 'settings', variables.$color-black); -} - -.nav-icon-security { - @include functions.icon-color('toggle-filelist', 'settings', variables.$color-black); -} - -.nav-icon-clientsbox { - @include functions.icon-color('change', 'settings', variables.$color-black); -} - -.nav-icon-federated-cloud { - @include functions.icon-color('share', 'settings', variables.$color-black); -} - -.nav-icon-second-factor-backup-codes, .nav-icon-ssl-root-certificate { - @include functions.icon-color('password', 'settings', variables.$color-black); -} - #personal-settings-avatar-container { display: inline-grid; grid-template-columns: 1fr; @@ -325,20 +304,6 @@ table.nostyle { } } -li.active { - .delete, - .rename { - display: block; - } -} - -.app-navigation-entry-utils { - .delete, - .rename { - display: none; - } -} - #usersearchform { position: absolute; top: 2px; @@ -424,26 +389,6 @@ span.usersLastLoginTooltip { white-space: nowrap; } -/* SETTINGS NAVIGATION */ -#app-navigation { - /* Navigation icons */ - img { - margin-bottom: -3px; - margin-inline-end: 6px; - width: 16px; - } - - li span.no-icon { - padding-inline-start: 32px; - } - - ul li.active > span.utils { - .delete, .rename { - display: block; - } - } -} - /* SETTINGS SECTIONS */ // to match with NcSettingsSection component .section { diff --git a/apps/settings/lib/Controller/CommonSettingsTrait.php b/apps/settings/lib/Controller/CommonSettingsTrait.php index 75d2b1f2f9e..c4949339ad5 100644 --- a/apps/settings/lib/Controller/CommonSettingsTrait.php +++ b/apps/settings/lib/Controller/CommonSettingsTrait.php @@ -49,22 +49,10 @@ trait CommonSettingsTrait { /** @var IInitialState */ private $initialState; - /** - * @return array{forms: array{personal: array, admin: array}} - */ - private function getNavigationParameters(string $currentType, string $currentSection): array { - return [ - 'forms' => [ - 'personal' => $this->formatPersonalSections($currentType, $currentSection), - 'admin' => $this->formatAdminSections($currentType, $currentSection), - ], - ]; - } - /** * @param IIconSection[][] $sections * @psalm-param 'admin'|'personal' $type - * @return list + * @return list */ protected function formatSections(array $sections, string $currentSection, string $type, string $currentType): array { $templateParameters = []; @@ -89,8 +77,8 @@ trait CommonSettingsTrait { && $type === $currentType; $templateParameters[] = [ - 'anchor' => $section->getID(), - 'section-name' => $section->getName(), + 'id' => $section->getID(), + 'name' => $section->getName(), 'active' => $active, 'icon' => $icon, ]; @@ -99,11 +87,17 @@ trait CommonSettingsTrait { return $templateParameters; } + /** + * @return list + */ protected function formatPersonalSections(string $currentType, string $currentSection): array { $sections = $this->settingsManager->getPersonalSections(); return $this->formatSections($sections, $currentSection, 'personal', $currentType); } + /** + * @return list + */ protected function formatAdminSections(string $currentType, string $currentSection): array { $sections = $this->settingsManager->getAdminSections(); return $this->formatSections($sections, $currentSection, 'admin', $currentType); @@ -175,9 +169,13 @@ trait CommonSettingsTrait { $this->initialState->provideInitialState('declarative-settings-forms', $declarativeSettings); } + $this->initialState->provideInitialState('sections', [ + 'personal' => $this->formatPersonalSections($type, $section), + 'admin' => $this->formatAdminSections($type, $section), + ]); + $settings = array_merge(...$settings); $templateParams = $this->formatSettings($settings, $declarativeSettings); - $templateParams = array_merge($templateParams, $this->getNavigationParameters($type, $section)); $activeSection = $this->settingsManager->getSection($type, $section); if ($activeSection) { @@ -186,6 +184,7 @@ trait CommonSettingsTrait { $templateParams['activeSectionType'] = $type; } + Util::addScript(Application::APP_ID, 'main', prepend: true); return new TemplateResponse('settings', 'settings/frame', $templateParams); } } diff --git a/apps/settings/src/components/SettingsNavigationItem.vue b/apps/settings/src/components/SettingsNavigationItem.vue new file mode 100644 index 00000000000..867c7278841 --- /dev/null +++ b/apps/settings/src/components/SettingsNavigationItem.vue @@ -0,0 +1,54 @@ + + + + + + + diff --git a/apps/settings/src/main-settings.ts b/apps/settings/src/main-settings.ts new file mode 100644 index 00000000000..b47dc9062c1 --- /dev/null +++ b/apps/settings/src/main-settings.ts @@ -0,0 +1,13 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getCSPNonce } from '@nextcloud/auth' +import Vue from 'vue' +import SettingsNavigation from './views/SettingsNavigation.vue' + +__webpack_nonce__ = getCSPNonce() + +const app = new Vue(SettingsNavigation) +app.$mount('#app-navigation') diff --git a/apps/settings/src/views/SettingsNavigation.vue b/apps/settings/src/views/SettingsNavigation.vue new file mode 100644 index 00000000000..f87ede5e5a3 --- /dev/null +++ b/apps/settings/src/views/SettingsNavigation.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/apps/settings/templates/settings/frame.php b/apps/settings/templates/settings/frame.php index ec21cad5895..4ad142f3c58 100644 --- a/apps/settings/templates/settings/frame.php +++ b/apps/settings/templates/settings/frame.php @@ -7,61 +7,7 @@ style('settings', 'settings'); ?> -
- -
t('Personal')); ?>
- - - - -
t('Administration')); ?>
- - -
+
data-active-section-id="" data-active-section-type="" >
diff --git a/apps/settings/tests/Controller/AdminSettingsControllerTest.php b/apps/settings/tests/Controller/AdminSettingsControllerTest.php index 218be8dbeeb..7ac0b73e37a 100644 --- a/apps/settings/tests/Controller/AdminSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AdminSettingsControllerTest.php @@ -122,12 +122,23 @@ class AdminSettingsControllerTest extends TestCase { ->with($user, 'admin', 'test') ->willReturn([]); - $idx = $this->adminSettingsController->index('test'); + $initialState = []; + $this->initialState->expects(self::atLeastOnce()) + ->method('provideInitialState') + ->willReturnCallback(function () use (&$initialState) { + $initialState[] = func_get_args(); + }); - $expected = new TemplateResponse('settings', 'settings/frame', [ - 'forms' => ['personal' => [], 'admin' => []], - 'content' => '' - ]); - $this->assertEquals($expected, $idx); + $expected = new TemplateResponse( + 'settings', + 'settings/frame', + [ + 'content' => '' + ], + ); + $this->assertEquals($expected, $this->adminSettingsController->index('test')); + $this->assertEquals([ + ['sections', ['admin' => [], 'personal' => []]], + ], $initialState); } } diff --git a/build/frontend-legacy/webpack.modules.cjs b/build/frontend-legacy/webpack.modules.cjs index b5fd95c8174..8bd01fe7920 100644 --- a/build/frontend-legacy/webpack.modules.cjs +++ b/build/frontend-legacy/webpack.modules.cjs @@ -54,6 +54,7 @@ module.exports = { 'public-nickname-handler': path.join(__dirname, 'apps/files_sharing/src', 'public-nickname-handler.ts'), }, settings: { + main: path.join(__dirname, 'apps/settings/src', 'main-settings.ts'), 'vue-settings-admin-overview': path.join(__dirname, 'apps/settings/src', 'main-admin-overview.ts'), 'vue-settings-admin-basic-settings': path.join(__dirname, 'apps/settings/src', 'main-admin-basic-settings.js'), 'vue-settings-admin-ai': path.join(__dirname, 'apps/settings/src', 'main-admin-ai.js'),