Browse Source

ref(ui): Add page titles to organization projects and settings (#14954)

* Add descriptive page titles for:
  * Organization projects dashboard
  * Organization settings:
	  * General Settings
	  * Projects
	  * Teams
  		 * Team Details
	  * Members
	  * Auth Settings
  	  * Audit Log
	  * Repositories
	  * Integrations
	  * Developer Settings

* Add DocumentTitle wrapper component - SentryDocumentTitle
* Rework routeTitleGen to ts
* Used format `<PageTitle> - <orgSlug> - Sentry`
aishchenko 5 years ago
parent
commit
de78c7ca93

+ 19 - 0
src/sentry/static/sentry/app/components/sentryDocumentTitle.tsx

@@ -0,0 +1,19 @@
+import React, {FunctionComponent, ReactChildren} from 'react';
+import DocumentTitle from 'react-document-title';
+
+type DocumentTitleProps = {
+  // Main page title
+  title: string;
+  // Organization or project slug to give title some context
+  objSlug: string;
+  children?: ReactChildren;
+};
+
+const SentryDocumentTitle: FunctionComponent<DocumentTitleProps> = (
+  props: DocumentTitleProps
+) => {
+  const _title = `${props.title} - ${props.objSlug} - Sentry`;
+  return <DocumentTitle title={_title}>{props.children}</DocumentTitle>;
+};
+
+export default SentryDocumentTitle;

+ 10 - 0
src/sentry/static/sentry/app/utils/routeTitle.tsx

@@ -0,0 +1,10 @@
+function routeTitleGen(
+  routeName: string,
+  orgSlug: string,
+  withSentry: boolean = true
+): string {
+  const tmpl = `${routeName} - ${orgSlug}`;
+  return withSentry ? `${tmpl} - Sentry` : tmpl;
+}
+
+export default routeTitleGen;

+ 6 - 1
src/sentry/static/sentry/app/views/organizationIntegrations/index.tsx

@@ -10,6 +10,7 @@ import {sortArray} from 'app/utils';
 import {t} from 'app/locale';
 import AsyncComponent from 'app/components/asyncComponent';
 import LoadingIndicator from 'app/components/loadingIndicator';
+import SentryDocumentTitle from 'app/components/sentryDocumentTitle';
 import MigrationWarnings from 'app/views/organizationIntegrations/migrationWarnings';
 import PermissionAlert from 'app/views/settings/organization/permissionAlert';
 import ProviderRow from 'app/views/organizationIntegrations/providerRow';
@@ -255,6 +256,7 @@ class OrganizationIntegrations extends AsyncComponent<
   };
 
   renderBody() {
+    const {orgId} = this.props.params;
     const {reloading, orgOwnedApps, publishedApps} = this.state;
     const published = publishedApps || [];
     // we dont want the app to render twice if its the org that created
@@ -283,9 +285,12 @@ class OrganizationIntegrations extends AsyncComponent<
       orgOwned.filter(a => a.status === 'internal')
     );
 
+    const title = t('Integrations');
+
     return (
       <React.Fragment>
-        {!this.props.hideHeader && <SettingsPageHeader title={t('Integrations')} />}
+        <SentryDocumentTitle title={title} objSlug={orgId} />
+        {!this.props.hideHeader && <SettingsPageHeader title={title} />}
         <PermissionAlert access={['org:integrations']} />
 
         <MigrationWarnings

+ 5 - 1
src/sentry/static/sentry/app/views/projectsDashboard/index.jsx

@@ -12,6 +12,7 @@ import ConfigStore from 'app/stores/configStore';
 import IdBadge from 'app/components/idBadge';
 import NoProjectMessage from 'app/components/noProjectMessage';
 import PageHeading from 'app/components/pageHeading';
+import SentryDocumentTitle from 'app/components/sentryDocumentTitle';
 import ProjectsStatsStore from 'app/stores/projectsStatsStore';
 import SentryTypes from 'app/sentryTypes';
 import getRouteStringFromRoutes from 'app/utils/getRouteStringFromRoutes';
@@ -64,9 +65,12 @@ class Dashboard extends React.Component {
     if (showEmptyMessage) {
       return <NoProjectMessage organization={organization}>{null}</NoProjectMessage>;
     }
-
     return (
       <React.Fragment>
+        <SentryDocumentTitle
+          title={t('Projects Dashboard')}
+          objSlug={organization.slug}
+        />
         {projects.length > 0 && (
           <ProjectsHeader>
             <PageHeading>Projects</PageHeading>

+ 3 - 2
src/sentry/static/sentry/app/views/settings/organizationAuditLog/index.jsx

@@ -4,6 +4,8 @@ import React from 'react';
 
 import AsyncView from 'app/views/asyncView';
 import SentryTypes from 'app/sentryTypes';
+import {t} from 'app/locale';
+import routeTitleGen from 'app/utils/routeTitle';
 
 import AuditLogList from './auditLogList';
 
@@ -82,8 +84,7 @@ class OrganizationAuditLog extends AsyncView {
   }
 
   getTitle() {
-    const org = this.context.organization;
-    return `${org.name} Audit Log`;
+    return routeTitleGen(t('Audit Log'), this.context.organization.slug, false);
   }
 
   handleEventSelect = value => {

+ 2 - 2
src/sentry/static/sentry/app/views/settings/organizationAuth/index.jsx

@@ -4,6 +4,7 @@ import {t} from 'app/locale';
 import AsyncView from 'app/views/asyncView';
 import IndicatorStore from 'app/stores/indicatorStore';
 import SentryTypes from 'app/sentryTypes';
+import routeTitleGen from 'app/utils/routeTitle';
 
 import OrganizationAuthList from './organizationAuthList';
 
@@ -30,8 +31,7 @@ class OrganizationAuth extends AsyncView {
   }
 
   getTitle() {
-    const org = this.context.organization;
-    return `${org.name} - Auth Settings`;
+    return routeTitleGen(t('Auth Settings'), this.context.organization.slug, false);
   }
 
   handleSendReminders = provider => {

+ 6 - 0
src/sentry/static/sentry/app/views/settings/organizationDeveloperSettings/index.jsx

@@ -11,12 +11,18 @@ import SettingsPageHeader from 'app/views/settings/components/settingsPageHeader
 import SentryApplicationRow from 'app/views/settings/organizationDeveloperSettings/sentryApplicationRow';
 import withOrganization from 'app/utils/withOrganization';
 import {t} from 'app/locale';
+import routeTitleGen from 'app/utils/routeTitle';
 
 class OrganizationDeveloperSettings extends AsyncView {
   static propTypes = {
     organization: SentryTypes.Organization.isRequired,
   };
 
+  getTitle() {
+    const {orgId} = this.props.params;
+    return routeTitleGen(t('Developer Settings'), orgId, false);
+  }
+
   getEndpoints() {
     const {orgId} = this.props.params;
 

+ 3 - 1
src/sentry/static/sentry/app/views/settings/organizationDeveloperSettings/sentryApplicationDetails.tsx

@@ -20,6 +20,7 @@ import {
   internalIntegrationForms,
 } from 'app/data/forms/sentryApplication';
 import getDynamicText from 'app/utils/getDynamicText';
+import routeTitleGen from 'app/utils/routeTitle';
 
 import DateTime from 'app/components/dateTime';
 import Button from 'app/components/button';
@@ -92,7 +93,8 @@ export default class SentryApplicationDetails extends AsyncView<Props, State> {
   }
 
   getTitle() {
-    return t('Sentry Integration Details');
+    const {orgId} = this.props.params;
+    return routeTitleGen(t('Sentry Integration Details'), orgId, false);
   }
 
   // Events may come from the API as "issue.created" when we just want "issue" here.

+ 2 - 0
src/sentry/static/sentry/app/views/settings/organizationGeneralSettings/index.jsx

@@ -4,6 +4,7 @@ import React from 'react';
 import createReactClass from 'create-react-class';
 
 import {Panel, PanelHeader} from 'app/components/panels';
+import SentryDocumentTitle from 'app/components/sentryDocumentTitle';
 import {addLoadingMessage} from 'app/actionCreators/indicator';
 import {
   changeOrganizationSlug,
@@ -97,6 +98,7 @@ const OrganizationGeneralSettings = createReactClass({
 
     return (
       <div>
+        <SentryDocumentTitle title={t('General Settings')} objSlug={orgId} />
         {error && <LoadingError />}
         {loading && !error && <LoadingIndicator />}
 

+ 3 - 2
src/sentry/static/sentry/app/views/settings/organizationMembers/index.jsx

@@ -12,6 +12,7 @@ import ConfigStore from 'app/stores/configStore';
 import Pagination from 'app/components/pagination';
 import SentryTypes from 'app/sentryTypes';
 import SettingsPageHeader from 'app/views/settings/components/settingsPageHeader';
+import routeTitleGen from 'app/utils/routeTitle';
 import {redirectToRemainingOrganization} from 'app/actionCreators/organizations';
 import {openInviteMembersModal} from 'app/actionCreators/modal';
 
@@ -77,8 +78,8 @@ class OrganizationMembersView extends AsyncView {
   }
 
   getTitle() {
-    const org = this.context.organization;
-    return `${org.name} Members`;
+    const orgId = this.context.organization.slug;
+    return routeTitleGen(t('Members'), orgId, false);
   }
 
   removeMember = id => {

Some files were not shown because too many files changed in this diff