diff --git a/app/docker/views/dashboard/dashboard.html b/app/docker/views/dashboard/dashboard.html index f567b1e3e..73442d156 100644 --- a/app/docker/views/dashboard/dashboard.html +++ b/app/docker/views/dashboard/dashboard.html @@ -18,7 +18,7 @@ <p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'"> <pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon> Portainer is connected to a node that is part of a Swarm cluster. Some resources located on other nodes in the cluster might not be available for management, have a look at - <a href="https://docs.portainer.io/admin/environments/add/swarm/agent" target="_blank">our agent setup</a> for more details. + <help-link doc-link="'/admin/environments/add/swarm/agent'" target="'_blank'" children="'our agent setup'"></help-link> for more details. </p> <p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'WORKER'"> <pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon> diff --git a/app/kubernetes/views/deploy/deploy.html b/app/kubernetes/views/deploy/deploy.html index 480b1c195..27ec85ee2 100644 --- a/app/kubernetes/views/deploy/deploy.html +++ b/app/kubernetes/views/deploy/deploy.html @@ -106,7 +106,7 @@ deploy-method="{{ ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE ? 'compose' : 'manifest' }}" base-webhook-url="{{ ctrl.state.baseWebhookUrl }}" webhook-id="{{ ctrl.state.webhookId }}" - webhooks-docs="https://docs.portainer.io/user/kubernetes/applications/webhooks" + webhooks-docs="/user/kubernetes/applications/webhooks" ></git-form> <!-- !repository --> diff --git a/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html index de32a26cc..feed7e157 100644 --- a/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html +++ b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html @@ -12,7 +12,7 @@ is-force-pull-visible="false" base-webhook-url="{{ $ctrl.state.baseWebhookUrl }}" webhook-id="{{ $ctrl.state.webhookId }}" - webhooks-docs="https://docs.portainer.io/user/kubernetes/applications/webhooks" + webhooks-docs="/user/kubernetes/applications/webhooks" ></git-form-auto-update-fieldset> <time-window-display></time-window-display> diff --git a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html index 5b8124435..170b7dd05 100644 --- a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html +++ b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html @@ -16,7 +16,7 @@ is-force-pull-visible="$ctrl.stack.Type !== 3" base-webhook-url="{{ $ctrl.state.baseWebhookUrl }}" webhook-id="{{ $ctrl.state.webhookId }}" - webhooks-docs="https://docs.portainer.io/user/docker/stacks/webhooks" + webhooks-docs="/user/docker/stacks/webhooks" ></git-form-auto-update-fieldset> <div class="form-group"> diff --git a/app/portainer/react/components/index.ts b/app/portainer/react/components/index.ts index a35edbfb5..e9b2ba556 100644 --- a/app/portainer/react/components/index.ts +++ b/app/portainer/react/components/index.ts @@ -36,6 +36,7 @@ import { Slider } from '@@/form-components/Slider'; import { TagButton } from '@@/TagButton'; import { BETeaserButton } from '@@/BETeaserButton'; import { CodeEditor } from '@@/CodeEditor'; +import { HelpLink } from '@@/PageHeader/HelpLink'; import { fileUploadField } from './file-upload-field'; import { switchField } from './switch-field'; @@ -126,6 +127,14 @@ export const ngModule = angular 'reactQueryDevTools', r2a(withReactQuery(ReactQueryDevtoolsWrapper), []) ) + .component( + 'helpLink', + r2a(withUIRouter(withReactQuery(HelpLink)), [ + 'docLink', + 'target', + 'children', + ]) + ) .component( 'dashboardItem', r2a(DashboardItem, [ diff --git a/app/portainer/views/stacks/create/createstack.html b/app/portainer/views/stacks/create/createstack.html index d925bf5b9..ffd860c44 100644 --- a/app/portainer/views/stacks/create/createstack.html +++ b/app/portainer/views/stacks/create/createstack.html @@ -87,7 +87,7 @@ is-force-pull-visible="true" base-webhook-url="{{ state.baseWebhookUrl }}" webhook-id="{{ state.webhookId }}" - webhooks-docs="https://docs.portainer.io/user/docker/stacks/webhooks" + webhooks-docs="/user/docker/stacks/webhooks" ></git-form> <div ng-show="state.Method === 'template'"> diff --git a/app/react/components/PageHeader/ContextHelp/ContextHelp.tsx b/app/react/components/PageHeader/ContextHelp/ContextHelp.tsx index f763536e9..26b44fcde 100644 --- a/app/react/components/PageHeader/ContextHelp/ContextHelp.tsx +++ b/app/react/components/PageHeader/ContextHelp/ContextHelp.tsx @@ -32,11 +32,11 @@ export function ContextHelp() { ); } -function useDocsUrl(): string { +export function useDocsUrl(doc?: string): string { const { state } = useCurrentStateAndParams(); const versionQuery = useSystemVersion(); - if (!state) { + if (!doc && !state) { return ''; } @@ -54,6 +54,10 @@ function useDocsUrl(): string { } } + if (doc) { + return url + doc; + } + const { data } = state; if ( data && diff --git a/app/react/components/PageHeader/HelpLink/HelpLink.tsx b/app/react/components/PageHeader/HelpLink/HelpLink.tsx new file mode 100644 index 000000000..1c116c56a --- /dev/null +++ b/app/react/components/PageHeader/HelpLink/HelpLink.tsx @@ -0,0 +1,21 @@ +import { useDocsUrl } from '../ContextHelp/ContextHelp'; + +type HelpLinkProps = { + docLink?: string; + target?: string; + children?: React.ReactNode; +}; + +export function HelpLink({ + docLink, + target = '_blank', + children, +}: HelpLinkProps) { + const docsUrl = useDocsUrl(docLink); + + return ( + <a href={docsUrl} target={target} rel="noreferrer"> + {children} + </a> + ); +} diff --git a/app/react/components/PageHeader/HelpLink/index.ts b/app/react/components/PageHeader/HelpLink/index.ts new file mode 100644 index 000000000..c3473f158 --- /dev/null +++ b/app/react/components/PageHeader/HelpLink/index.ts @@ -0,0 +1 @@ +export { HelpLink } from './HelpLink'; diff --git a/app/react/docker/containers/CreateView/CreateView.tsx b/app/react/docker/containers/CreateView/CreateView.tsx index eca277ce0..6ba44dc63 100644 --- a/app/react/docker/containers/CreateView/CreateView.tsx +++ b/app/react/docker/containers/CreateView/CreateView.tsx @@ -17,6 +17,7 @@ import { confirmDestructive } from '@@/modals/confirm'; import { buildConfirmButton } from '@@/modals/utils'; import { InformationPanel } from '@@/InformationPanel'; import { TextTip } from '@@/Tip/TextTip'; +import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp'; import { useContainers } from '../queries/containers'; import { useSystemLimits } from '../../proxy/queries/useInfo'; @@ -77,6 +78,8 @@ function CreateForm() { isDockerhubRateLimited, }); + const createContDocUrl = useDocsUrl('/docker/containers/create'); + if (!envQuery.data || !initialValuesQuery) { return null; } @@ -101,11 +104,7 @@ function CreateForm() { The new container may fail to start if the image is changed, and settings from the previous container aren't compatible. Common causes include entrypoint, cmd or - <a - href="https://docs.portainer.io/user/docker/containers/advanced" - target="_blank" - rel="noreferrer" - > + <a href={createContDocUrl} target="_blank" rel="noreferrer"> other settings </a>{' '} set by an image. diff --git a/app/react/portainer/environments/wizard/EnvironmentsCreationView/WizardKubernetes/KubeConfigTeaserForm.tsx b/app/react/portainer/environments/wizard/EnvironmentsCreationView/WizardKubernetes/KubeConfigTeaserForm.tsx index 0a9b5bd02..f79f1ce97 100644 --- a/app/react/portainer/environments/wizard/EnvironmentsCreationView/WizardKubernetes/KubeConfigTeaserForm.tsx +++ b/app/react/portainer/environments/wizard/EnvironmentsCreationView/WizardKubernetes/KubeConfigTeaserForm.tsx @@ -7,6 +7,7 @@ import { FormSectionTitle } from '@@/form-components/FormSectionTitle'; import { Input } from '@@/form-components/Input'; import { Button } from '@@/buttons'; import { TextTip } from '@@/Tip/TextTip'; +import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp'; const initialValues = { kubeConfig: '', @@ -18,6 +19,10 @@ const initialValues = { }; export function KubeConfigTeaserForm() { + const kubeConfigImportDocUrl = useDocsUrl( + 'admin/environments/add/kubernetes/import' + ); + return ( <Formik initialValues={initialValues} onSubmit={() => {}} validateOnMount> {() => ( @@ -28,7 +33,7 @@ export function KubeConfigTeaserForm() { <TextTip color="blue"> <span className="text-muted"> <a - href="https://docs.portainer.io/admin/environments/add/kubernetes/import" + href={kubeConfigImportDocUrl} target="_blank" rel="noreferrer" > diff --git a/app/react/portainer/gitops/GitForm.tsx b/app/react/portainer/gitops/GitForm.tsx index 7fe6c2f8f..3c8e42f06 100644 --- a/app/react/portainer/gitops/GitForm.tsx +++ b/app/react/portainer/gitops/GitForm.tsx @@ -11,6 +11,7 @@ import { TimeWindowDisplay } from '@/react/portainer/gitops/TimeWindowDisplay'; import { FormSection } from '@@/form-components/FormSection'; import { validateForm } from '@@/form-components/validate-form'; import { SwitchField } from '@@/form-components/SwitchField'; +import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp'; import { GitCredential } from '../account/git-credentials/types'; @@ -50,6 +51,8 @@ export function GitForm({ webhooksDocs, }: Props) { const [value, setValue] = useState(initialValue); // TODO: remove this state when form is not inside angularjs + const webhooksDocsUrl = useDocsUrl(webhooksDocs); + return ( <FormSection title="Git repository"> <AuthFieldset @@ -105,7 +108,7 @@ export function GitForm({ onChange={(value) => handleChange({ AutoUpdate: value })} isForcePullVisible={isForcePullVisible} errors={errors.AutoUpdate as FormikErrors<GitFormModel['AutoUpdate']>} - webhooksDocs={webhooksDocs} + webhooksDocs={webhooksDocsUrl} /> )} diff --git a/app/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset.tsx b/app/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset.tsx index 1e1b3b566..516b6d4bc 100644 --- a/app/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset.tsx +++ b/app/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset.tsx @@ -10,6 +10,7 @@ import { SwitchField } from '@@/form-components/SwitchField'; import { TextTip } from '@@/Tip/TextTip'; import { FormControl } from '@@/form-components/FormControl'; import { Input, Select } from '@@/form-components/Input'; +import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp'; import { RelativePathModel, getPerDevConfigsFilterType } from './types'; @@ -37,6 +38,10 @@ export function RelativePathFieldset({ const { enableFsPath0, enableFsPath1, toggleFsPath } = useEnableFsPath(value); + const gitoptsEdgeConfigDocUrl = useDocsUrl( + '/user/edge/stacks/add#gitops-edge-configurations' + ); + const pathTip0 = 'For relative path volumes use with Docker Swarm, you must have a network filesystem which all of your nodes can access.'; const pathTip1 = @@ -279,10 +284,7 @@ export function RelativePathFieldset({ ./config/${PORTAINER_EDGE_GROUP}:/myapp/groupconfig </code> . More documentation can be found{' '} - <a href="https://docs.portainer.io/user/edge/stacks/add#gitops-edge-configurations"> - here - </a> - . + <a href={gitoptsEdgeConfigDocUrl}>here</a>. </div> </TextTip> </div> diff --git a/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/TemplatesUrlSection.tsx b/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/TemplatesUrlSection.tsx index b25d25fff..949cd786a 100644 --- a/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/TemplatesUrlSection.tsx +++ b/app/react/portainer/settings/SettingsView/ApplicationSettingsPanel/TemplatesUrlSection.tsx @@ -3,6 +3,7 @@ import { useField, Field } from 'formik'; import { FormControl } from '@@/form-components/FormControl'; import { FormSection } from '@@/form-components/FormSection'; import { Input } from '@@/form-components/Input'; +import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp'; // this value is taken from https://github.com/portainer/portainer/blob/develop/api/portainer.go#L1628 const DEFAULT_URL = @@ -11,6 +12,8 @@ const DEFAULT_URL = export function TemplatesUrlSection() { const [{ name }, { error }] = useField<string>('templatesUrl'); + const buildTemplateDocUrl = useDocsUrl('/advanced/app-templates/build'); + return ( <FormSection title="App Templates"> <div className="form-group"> @@ -18,11 +21,7 @@ export function TemplatesUrlSection() { <p> You can specify the URL to your own template definitions file here. See{' '} - <a - href="https://docs.portainer.io/advanced/app-templates/build" - target="_blank" - rel="noreferrer" - > + <a href={buildTemplateDocUrl} target="_blank" rel="noreferrer"> Portainer documentation </a>{' '} for more details.