diff --git a/app/assets/css/app.css b/app/assets/css/app.css index 057cf34f7..82c0e21f7 100644 --- a/app/assets/css/app.css +++ b/app/assets/css/app.css @@ -17,6 +17,7 @@ html { font-size: 16px; overflow-y: scroll; + scroll-behavior: smooth; } html[theme='dark'], diff --git a/app/portainer/react/components/settings.ts b/app/portainer/react/components/settings.ts index 2f9f3a4ee..1e9811e33 100644 --- a/app/portainer/react/components/settings.ts +++ b/app/portainer/react/components/settings.ts @@ -25,7 +25,7 @@ export const settingsModule = angular ) .component( 'applicationSettingsPanel', - r2a(withReactQuery(ApplicationSettingsPanel), ['onSuccess']) + r2a(withReactQuery(ApplicationSettingsPanel), ['onSuccess', 'settings']) ) .component( 'sslSettingsPanel', @@ -38,5 +38,5 @@ export const settingsModule = angular ) .component( 'kubeSettingsPanel', - r2a(withUIRouter(withReactQuery(KubeSettingsPanel)), []) + r2a(withUIRouter(withReactQuery(KubeSettingsPanel)), ['settings']) ).name; diff --git a/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatable.tsx b/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatable.tsx index 7602982d3..82d162d28 100644 --- a/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatable.tsx +++ b/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatable.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useEffect } from 'react'; import { useCurrentUser } from '@/react/hooks/useUser'; import helm from '@/assets/ico/vendor/helm.svg?c'; @@ -41,6 +41,19 @@ export function HelmRepositoryDatatable() { helmReposQuery.data?.UserRepositories, ]); + useEffect(() => { + // window.location.hash will get everything after the hashbang + // the regex will match the the content after each hash + const timeout = setTimeout(() => { + const regEx = /#!.*#(.*)/; + const match = window.location.hash.match(regEx); + if (match && match[1]) { + document.getElementById(match[1])?.scrollIntoView(); + } + }, 1000); + return () => clearTimeout(timeout); + }, []); + return ( String(row.Id)} @@ -48,8 +61,9 @@ export function HelmRepositoryDatatable() { description={} settingsManager={tableState} columns={columns} - title="Helm Repositories" + title="Helm repositories" titleIcon={helm} + titleId="helm-repositories" renderTableActions={(selectedRows) => ( )} diff --git a/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx b/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx index 26b579c48..47a1451b8 100644 --- a/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx +++ b/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx @@ -36,7 +36,7 @@ export function HelmRepositoryDatatableActions({ selectedItems }: Props) { data-cy="credentials-addButton" icon={Plus} > - Add Helm Repository + Add Helm repository ); diff --git a/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/ApplicationSettingsPanel.tsx b/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/ApplicationSettingsPanel.tsx index dc688e5f7..641b7c672 100644 --- a/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/ApplicationSettingsPanel.tsx +++ b/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/ApplicationSettingsPanel.tsx @@ -2,10 +2,7 @@ import { Settings as SettingsIcon } from 'lucide-react'; import { Field, Form, Formik, useFormikContext } from 'formik'; import { EdgeCheckinIntervalField } from '@/react/edge/components/EdgeCheckInIntervalField'; -import { - useSettings, - useUpdateSettingsMutation, -} from '@/react/portainer/settings/queries'; +import { useUpdateSettingsMutation } from '@/react/portainer/settings/queries'; import { notifySuccess } from '@/portainer/services/notifications'; import { Widget } from '@@/Widget'; @@ -24,17 +21,13 @@ import { EnableTelemetryField } from './EnableTelemetryField'; export function ApplicationSettingsPanel({ onSuccess, + settings, }: { onSuccess(settings: Settings): void; + settings: Settings; }) { - const settingsQuery = useSettings(); const mutation = useUpdateSettingsMutation(); - if (!settingsQuery.data) { - return null; - } - - const settings = settingsQuery.data; const initialValues: Values = { edgeAgentCheckinInterval: settings.EdgeAgentCheckinInterval, enableTelemetry: settings.EnableTelemetry, diff --git a/app/react/portainer/settings/SettingsView/KubeSettingsPanel/KubeSettingsPanel.tsx b/app/react/portainer/settings/SettingsView/KubeSettingsPanel/KubeSettingsPanel.tsx index 66190b88c..6af5f1680 100644 --- a/app/react/portainer/settings/SettingsView/KubeSettingsPanel/KubeSettingsPanel.tsx +++ b/app/react/portainer/settings/SettingsView/KubeSettingsPanel/KubeSettingsPanel.tsx @@ -8,7 +8,8 @@ import { useEnvironmentId } from '@/react/hooks/useEnvironmentId'; import { LoadingButton } from '@@/buttons'; import { Widget } from '@@/Widget'; -import { useSettings, useUpdateSettingsMutation } from '../../queries'; +import { useUpdateSettingsMutation } from '../../queries'; +import { Settings } from '../../types'; import { HelmSection } from './HelmSection'; import { KubeConfigSection } from './KubeConfigSection'; @@ -16,19 +17,14 @@ import { FormValues } from './types'; import { DeploymentOptionsSection } from './DeploymentOptionsSection'; import { validation } from './validation'; -export function KubeSettingsPanel() { - const settingsQuery = useSettings(); +export function KubeSettingsPanel({ settings }: { settings: Settings }) { const queryClient = useQueryClient(); const environmentId = useEnvironmentId(false); const mutation = useUpdateSettingsMutation(); - if (!settingsQuery.data) { - return null; - } - const initialValues: FormValues = { - helmRepositoryUrl: settingsQuery.data.HelmRepositoryURL || '', - kubeconfigExpiry: settingsQuery.data.KubeconfigExpiry || '0', + helmRepositoryUrl: settings.HelmRepositoryURL || '', + kubeconfigExpiry: settings.KubeconfigExpiry || '0', globalDeploymentOptions: { ...{ requireNoteOnApplications: false, @@ -39,7 +35,7 @@ export function KubeSettingsPanel() { perEnvOverride: false, hideStacksFunctionality: false, }, - ...settingsQuery.data.GlobalDeploymentOptions, + ...settings.GlobalDeploymentOptions, }, }; diff --git a/app/react/portainer/settings/SettingsView/SettingsView.tsx b/app/react/portainer/settings/SettingsView/SettingsView.tsx index 22ce1b5a1..adb07bc46 100644 --- a/app/react/portainer/settings/SettingsView/SettingsView.tsx +++ b/app/react/portainer/settings/SettingsView/SettingsView.tsx @@ -1,9 +1,11 @@ +import { useEffect } from 'react'; import angular from 'angular'; import { StateManager } from '@/portainer/services/types'; import { PageHeader } from '@@/PageHeader'; +import { useSettings } from '../queries'; import { Settings } from '../types'; import { isBE } from '../../feature-flags/feature-flags.service'; @@ -16,14 +18,33 @@ import { SSLSettingsPanelWrapper } from './SSLSettingsPanel/SSLSettingsPanel'; import { ExperimentalFeatures } from './ExperimentalFeatures'; export function SettingsView() { + const settingsQuery = useSettings(); + + useEffect(() => { + if (settingsQuery.data) { + const regEx = /#!.*#(.*)/; + const match = window.location.hash.match(regEx); + if (match && match[1]) { + document.getElementById(match[1])?.scrollIntoView(); + } + } + }, [settingsQuery.data]); + return ( <>
- + {settingsQuery.data && ( + <> + - + + + )}