]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21419 - Encryption page adopts the new UI
authorKevin Silva <kevin.silva@sonarsource.com>
Tue, 16 Jan 2024 17:54:29 +0000 (18:54 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 19 Jan 2024 20:02:55 +0000 (20:02 +0000)
server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.tsx
server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx
server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx

index 1218844d96bd8c8c86e43737ff94a80fdd52473a..b0de72fd28a7730dc1eeb9d29b0bfab44a0f2b06 100644 (file)
@@ -80,6 +80,7 @@ const TEMP_PAGELIST_WITH_NEW_BACKGROUND_WHITE = [
   '/admin/background_tasks',
   '/admin/groups',
   '/admin/users',
+  '/admin/settings/encryption',
 ];
 
 export default function GlobalContainer() {
index bd5f34aa4d2151ddb5ef623519ced5f867e2722a..f18538e6834d2e71cd7795a7f19e33c05e7492e5 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { LargeCenteredLayout, PageContentFontWrapper, Spinner, Title } from 'design-system';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { checkSecretKey, generateSecretKey } from '../../../api/settings';
-import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import EncryptionForm from './EncryptionForm';
 import GenerateSecretKeyForm from './GenerateSecretKeyForm';
@@ -69,20 +69,26 @@ export default class EncryptionApp extends React.PureComponent<{}, State> {
 
   render() {
     const { loading, secretKey, secretKeyAvailable } = this.state;
+
     return (
-      <div className="page page-limited" id="encryption-page">
-        <Helmet defer={false} title={translate('property.category.security.encryption')} />
-        <header className="page-header">
-          <h1 className="page-title">{translate('property.category.security.encryption')}</h1>
-          <Spinner loading={loading} />
-        </header>
+      <LargeCenteredLayout id="encryption-page">
+        <PageContentFontWrapper className="sw-my-8 sw-body-sm">
+          <Helmet defer={false} title={translate('property.category.security.encryption')} />
+          <header>
+            <Title>{translate('property.category.security.encryption')}</Title>
+            <Spinner loading={loading} />
+          </header>
 
-        {!loading && !secretKeyAvailable && (
-          <GenerateSecretKeyForm generateSecretKey={this.generateSecretKey} secretKey={secretKey} />
-        )}
+          {!loading && !secretKeyAvailable && (
+            <GenerateSecretKeyForm
+              generateSecretKey={this.generateSecretKey}
+              secretKey={secretKey}
+            />
+          )}
 
-        {secretKeyAvailable && <EncryptionForm generateSecretKey={this.generateSecretKey} />}
-      </div>
+          {secretKeyAvailable && <EncryptionForm generateSecretKey={this.generateSecretKey} />}
+        </PageContentFontWrapper>
+      </LargeCenteredLayout>
     );
   }
 }
index 5b007bf087bec03c30ea21b06e72eee9883f492d..f1c825781645daa536ca60a07234a25ef46a46ad 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import {
+  ButtonPrimary,
+  ButtonSecondary,
+  ClipboardIconButton,
+  CodeSnippet,
+  InputTextArea,
+  Spinner,
+} from 'design-system';
 import * as React from 'react';
+import { useCallback, useState } from 'react';
 import { FormattedMessage } from 'react-intl';
 import { encryptValue } from '../../../api/settings';
-import DocLink from '../../../components/common/DocLink';
-import { SubmitButton } from '../../../components/controls/buttons';
-import { ClipboardButton } from '../../../components/controls/clipboard';
-import Spinner from '../../../components/ui/Spinner';
+import DocumentationLink from '../../../components/common/DocumentationLink';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
   generateSecretKey: () => Promise<void>;
 }
 
-interface State {
-  encryptedValue?: string;
-  encrypting: boolean;
-  generating: boolean;
-  value: string;
-}
-
-export default class EncryptionForm extends React.PureComponent<Props, State> {
-  mounted = false;
-  state: State = { encrypting: false, generating: false, value: '' };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
-    this.setState({ value: event.currentTarget.value });
-  };
+export default function EncryptionForm({ generateSecretKey }: Readonly<Props>) {
+  const [encrypting, setEncrypting] = useState(false);
+  const [generating, setGenerating] = useState(false);
+  const [encryptedValue, setEncryptedValue] = useState('');
+  const [value, setValue] = useState('');
 
-  handleEncrypt = (event: React.FormEvent) => {
-    event.preventDefault();
-    this.setState({ encrypting: true });
-    encryptValue(this.state.value).then(
-      ({ encryptedValue }) => {
-        if (this.mounted) {
-          this.setState({ encryptedValue, encrypting: false });
-        }
-      },
-      () => {
-        if (this.mounted) {
-          this.setState({ encrypting: false });
-        }
-      },
-    );
-  };
+  const handleChange = useCallback((event: React.ChangeEvent<HTMLTextAreaElement>) => {
+    setValue(event.currentTarget.value);
+  }, []);
 
-  handleGenerateSecretKey = (event: React.FormEvent) => {
-    event.preventDefault();
-    this.setState({ generating: true });
-    this.props.generateSecretKey().then(this.stopGenerating, this.stopGenerating);
-  };
+  const handleEncrypt = useCallback(
+    (event: React.FormEvent) => {
+      event.preventDefault();
+      setEncrypting(true);
+      encryptValue(value).then(
+        ({ encryptedValue }) => {
+          setEncryptedValue(encryptedValue);
+          setEncrypting(false);
+        },
+        () => {
+          setEncrypting(false);
+        },
+      );
+    },
+    [value],
+  );
 
-  stopGenerating = () => {
-    if (this.mounted) {
-      this.setState({ generating: false });
-    }
-  };
+  const handleGenerateSecretKey = useCallback(
+    (event: React.FormEvent) => {
+      event.preventDefault();
+      setGenerating(true);
+      generateSecretKey().then(
+        () => setGenerating(false),
+        () => setGenerating(false),
+      );
+    },
+    [generateSecretKey],
+  );
 
-  render() {
-    const { encryptedValue, encrypting, generating } = this.state;
-    return (
-      <div id="encryption-form-container">
-        <div className="spacer-bottom">{translate('encryption.form_intro')}</div>
-        <form className="big-spacer-bottom" id="encryption-form" onSubmit={this.handleEncrypt}>
-          <textarea
-            autoFocus
-            className="abs-width-600"
-            id="encryption-form-value"
-            onChange={this.handleChange}
-            required
-            rows={5}
-            value={this.state.value}
-          />
-          <div className="spacer-top">
-            <SubmitButton disabled={encrypting || generating}>
-              {translate('encryption.encrypt')}
-            </SubmitButton>
-            <Spinner className="spacer-left" loading={encrypting} />
-          </div>
-        </form>
+  return (
+    <div id="encryption-form-container">
+      <div className="sw-mb-2">{translate('encryption.form_intro')}</div>
+      <form id="encryption-form" onSubmit={handleEncrypt}>
+        <InputTextArea
+          autoFocus
+          size="large"
+          id="encryption-form-value"
+          onChange={handleChange}
+          required
+          rows={5}
+          value={value}
+        />
+        <div>
+          <ButtonSecondary className="sw-my-2" type="submit" disabled={encrypting || generating}>
+            {translate('encryption.encrypt')}
+          </ButtonSecondary>
+          <Spinner loading={encrypting} />
+        </div>
+      </form>
 
-        {encryptedValue && (
-          <div>
-            <label className="little-spacer-right" htmlFor="encrypted-value">
-              {translate('encryption.encrypted_value')}
-            </label>
-            <input
-              className="input-clear input-code input-super-large"
-              id="encrypted-value"
-              readOnly
-              type="text"
-              value={encryptedValue}
+      {encryptedValue && (
+        <div className="sw-my-2">
+          <label>{translate('encryption.encrypted_value')}</label>
+          <div className="sw-flex">
+            <CodeSnippet
+              className="it__encrypted-value sw-max-w-full sw-break-words sw-p-1"
+              isOneLine
+              noCopy
+              snippet={encryptedValue}
+            />
+            <ClipboardIconButton
+              aria-label={translate('copy_to_clipboard')}
+              className="sw-ml-4"
+              copyValue={encryptedValue}
             />
-            <ClipboardButton className="little-spacer-left" copyValue={encryptedValue} />
           </div>
-        )}
+        </div>
+      )}
 
-        <form
-          className="huge-spacer-top bordered-top"
-          id="encryption-new-key-form"
-          onSubmit={this.handleGenerateSecretKey}
-        >
-          <p className="big-spacer-top spacer-bottom">
-            <FormattedMessage
-              defaultMessage={translate('encryption.form_note')}
-              id="encryption.form_note"
-              values={{
-                moreInformationLink: (
-                  <DocLink to="/instance-administration/security/">
-                    {translate('more_information')}
-                  </DocLink>
-                ),
-              }}
-            />
-          </p>
+      <form id="encryption-new-key-form" onSubmit={handleGenerateSecretKey}>
+        <p className="sw-my-2">
+          <FormattedMessage
+            defaultMessage={translate('encryption.form_note')}
+            id="encryption.form_note"
+            values={{
+              moreInformationLink: (
+                <DocumentationLink to="/instance-administration/security/">
+                  {translate('more_information')}
+                </DocumentationLink>
+              ),
+            }}
+          />
+        </p>
 
-          <SubmitButton disabled={generating || encrypting}>
-            {translate('encryption.generate_new_secret_key')}{' '}
-          </SubmitButton>
-          <Spinner className="spacer-left" loading={generating} />
-        </form>
-      </div>
-    );
-  }
+        <ButtonPrimary type="submit" disabled={generating || encrypting}>
+          {translate('encryption.generate_new_secret_key')}{' '}
+        </ButtonPrimary>
+        <Spinner loading={generating} />
+      </form>
+    </div>
+  );
 }
index 0bd7a0d807875b8cff8c8089b85e2e0c95fc1cd6..ba081395b03286e186c51a6a4940cee9794b6ea3 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import {
+  ButtonPrimary,
+  ClipboardIconButton,
+  CodeSnippet,
+  ListItem,
+  SubHeading,
+  UnorderedList,
+} from 'design-system';
 import * as React from 'react';
+import { useCallback, useState } from 'react';
 import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
-import { SubmitButton } from '../../../components/controls/buttons';
-import { ClipboardButton } from '../../../components/controls/clipboard';
+import DocumentationLink from '../../../components/common/DocumentationLink';
 import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 
@@ -30,105 +37,89 @@ interface Props {
   secretKey?: string;
 }
 
-interface State {
-  submitting: boolean;
-}
-
-export default class GenerateSecretKeyForm extends React.PureComponent<Props, State> {
-  mounted = false;
-  state: State = { submitting: false };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
+export default function GenerateSecretKeyForm({ secretKey, generateSecretKey }: Readonly<Props>) {
+  const [submitting, setSubmitting] = useState(false);
 
-  handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
-    event.preventDefault();
-    this.setState({ submitting: true });
-    this.props.generateSecretKey().then(this.stopSubmitting, this.stopSubmitting);
-  };
+  const handleSubmit = useCallback(
+    (event: React.FormEvent<HTMLFormElement>) => {
+      event.preventDefault();
+      setSubmitting(true);
+      generateSecretKey().then(
+        () => setSubmitting(false),
+        () => setSubmitting(false),
+      );
+    },
+    [generateSecretKey],
+  );
 
-  stopSubmitting = () => {
-    if (this.mounted) {
-      this.setState({ submitting: false });
-    }
-  };
-
-  render() {
-    const { secretKey } = this.props;
-    const { submitting } = this.state;
-    return (
-      <div id="generate-secret-key-form-container">
-        {secretKey ? (
-          <>
-            <div className="big-spacer-bottom">
-              <h3 className="spacer-bottom" id="secret-key-title">
-                {translate('encryption.secret_key')}
-              </h3>
-              <input
-                aria-labelledby="secret-key-title"
-                className="input-clear input-code input-large"
-                id="secret-key"
-                readOnly
-                type="text"
-                value={secretKey}
+  return (
+    <div id="generate-secret-key-form-container">
+      {secretKey ? (
+        <>
+          <div className="sw-mb-4">
+            <SubHeading id="secret-key-title">{translate('encryption.secret_key')}</SubHeading>
+            <div className="sw-flex">
+              <CodeSnippet className="it__secret-key sw-p-1" isOneLine noCopy snippet={secretKey} />
+              <ClipboardIconButton
+                aria-label={translate('copy_to_clipboard')}
+                className="sw-ml-2"
+                copyValue={secretKey}
               />
-              <ClipboardButton className="little-spacer-left" copyValue={secretKey} />
-            </div>
-            <h3 className="spacer-bottom">{translate('encryption.how_to_use')}</h3>
-            <div className="markdown">
-              <ul>
-                <li>
-                  <FormattedMessage
-                    defaultMessage={translate('encryption.how_to_use.content1')}
-                    id="encryption.how_to_use.content1"
-                    values={{
-                      secret_file: <code>~/.sonar/sonar-secret.txt</code>,
-                      property: <code>sonar.secretKeyPath</code>,
-                      propreties_file: <code>conf/sonar.properties</code>,
-                    }}
-                  />
-                </li>
-                <li>{translate('encryption.how_to_use.content2')}</li>
-                <li>
-                  <FormattedMessage
-                    defaultMessage={translate('encryption.how_to_use.content3')}
-                    id="encryption.how_to_use.content3"
-                    values={{
-                      property: <code>sonar.secretKeyPath</code>,
-                    }}
-                  />
-                </li>
-                <li>{translate('encryption.how_to_use.content4')}</li>
-              </ul>
             </div>
-          </>
-        ) : (
-          <form id="generate-secret-key-form" onSubmit={this.handleSubmit}>
-            <p className="spacer-bottom">
-              <FormattedMessage
-                defaultMessage={translate('encryption.secret_key_description')}
-                id="encryption.secret_key_description"
-                values={{
-                  moreInformationLink: (
-                    <DocLink to="/instance-administration/security/">
-                      {translate('more_information')}
-                    </DocLink>
-                  ),
-                }}
-              />
-            </p>
-            <SubmitButton disabled={submitting}>
-              {translate('encryption.generate_secret_key')}
-            </SubmitButton>
-            <Spinner className="spacer-left" loading={submitting} />
-          </form>
-        )}
-      </div>
-    );
-  }
+          </div>
+          <SubHeading className="sw-mb-2">{translate('encryption.how_to_use')}</SubHeading>
+          <div>
+            <UnorderedList ticks>
+              <ListItem>
+                <FormattedMessage
+                  defaultMessage={translate('encryption.how_to_use.content1')}
+                  id="encryption.how_to_use.content1"
+                  values={{
+                    secret_file: (
+                      <CodeSnippet isOneLine noCopy snippet="~/.sonar/sonar-secret.txt" />
+                    ),
+                    property: <CodeSnippet isOneLine noCopy snippet="sonar.secretKeyPath" />,
+                    propreties_file: (
+                      <CodeSnippet isOneLine noCopy snippet="conf/sonar.properties" />
+                    ),
+                  }}
+                />
+              </ListItem>
+              <ListItem>{translate('encryption.how_to_use.content2')}</ListItem>
+              <ListItem>
+                <FormattedMessage
+                  defaultMessage={translate('encryption.how_to_use.content3')}
+                  id="encryption.how_to_use.content3"
+                  values={{
+                    property: <CodeSnippet isOneLine noCopy snippet="sonar.secretKeyPath" />,
+                  }}
+                />
+              </ListItem>
+              <ListItem>{translate('encryption.how_to_use.content4')}</ListItem>
+            </UnorderedList>
+          </div>
+        </>
+      ) : (
+        <form id="generate-secret-key-form" onSubmit={handleSubmit}>
+          <p>
+            <FormattedMessage
+              defaultMessage={translate('encryption.secret_key_description')}
+              id="encryption.secret_key_description"
+              values={{
+                moreInformationLink: (
+                  <DocumentationLink to="/instance-administration/security/">
+                    {translate('more_information')}
+                  </DocumentationLink>
+                ),
+              }}
+            />
+          </p>
+          <ButtonPrimary className="sw-mt-4" type="submit" disabled={submitting}>
+            {translate('encryption.generate_secret_key')}
+          </ButtonPrimary>
+          <Spinner loading={submitting} />
+        </form>
+      )}
+    </div>
+  );
 }