settingsIndex.tsx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import {useEffect} from 'react';
  2. import {RouteComponentProps} from 'react-router';
  3. import {css} from '@emotion/react';
  4. import styled from '@emotion/styled';
  5. import ExternalLink from 'sentry/components/links/externalLink';
  6. import Link, {LinkProps} from 'sentry/components/links/link';
  7. import {Panel, PanelBody, PanelHeader} from 'sentry/components/panels';
  8. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  9. import space from 'sentry/styles/space';
  10. import {Organization, User} from 'sentry/types';
  11. import {Theme} from 'sentry/utils/theme';
  12. import {t} from 'sentry/locale';
  13. import UserAvatar from 'sentry/components/avatar/userAvatar';
  14. import {IconDocs, IconLock, IconStack, IconSupport} from 'sentry/icons';
  15. import OrganizationAvatar from 'sentry/components/avatar/organizationAvatar';
  16. import SettingsLayout from './settingsLayout';
  17. const LINKS = {
  18. DOCUMENTATION: 'https://docs.sentry.io/',
  19. DOCUMENTATION_PLATFORMS: 'https://docs.sentry.io/clients/',
  20. DOCUMENTATION_QUICKSTART: 'https://docs.sentry.io/platform-redirect/?next=/',
  21. DOCUMENTATION_CLI: 'https://docs.sentry.io/product/cli/',
  22. DOCUMENTATION_API: 'https://docs.sentry.io/api/',
  23. API: '/settings/account/api/',
  24. MANAGE: '/manage/',
  25. FORUM: 'https://forum.sentry.io/',
  26. GITHUB_ISSUES: 'https://github.com/getsentry/sentry/issues',
  27. SERVICE_STATUS: 'https://status.sentry.io/',
  28. };
  29. const HOME_ICON_SIZE = 56;
  30. type SettingsIndexProps = {
  31. organization: Organization;
  32. user: User;
  33. };
  34. function SettingsIndex({organization, user, ...props}: SettingsIndexProps) {
  35. const organizationSettingsUrl =
  36. (organization && `/settings/${organization.slug}/`) || '';
  37. const isSelfHosted = false;
  38. const supportLinkProps = {
  39. isSelfHosted,
  40. organizationSettingsUrl,
  41. };
  42. const myAccount = (
  43. <GridPanel>
  44. <HomePanelHeader>
  45. <HomeLinkIcon to="/settings/account/">
  46. <UserAvatar user={user} size={HOME_ICON_SIZE} />
  47. {t('My Account')}
  48. </HomeLinkIcon>
  49. </HomePanelHeader>
  50. <HomePanelBody>
  51. <h3>{t('Quick links')}:</h3>
  52. <ul>
  53. <li>
  54. <HomeLink to="/settings/account/security/">
  55. {t('Change my password')}
  56. </HomeLink>
  57. </li>
  58. <li>
  59. <HomeLink to="/settings/account/notifications/">
  60. {t('Notification Preferences')}
  61. </HomeLink>
  62. </li>
  63. <li>
  64. <HomeLink to="/settings/account/">{t('Change my avatar')}</HomeLink>
  65. </li>
  66. </ul>
  67. </HomePanelBody>
  68. </GridPanel>
  69. );
  70. const orgSettings = (
  71. <GridPanel>
  72. {!organization && <LoadingIndicator overlay hideSpinner />}
  73. <HomePanelHeader>
  74. <HomeLinkIcon to={organizationSettingsUrl}>
  75. {organization ? (
  76. <OrganizationAvatar organization={organization} size={HOME_ICON_SIZE} />
  77. ) : (
  78. <HomeIconContainer color="green300">
  79. <IconStack size="lg" />
  80. </HomeIconContainer>
  81. )}
  82. <OrganizationName>
  83. {organization ? organization.slug : t('No Organization')}
  84. </OrganizationName>
  85. </HomeLinkIcon>
  86. </HomePanelHeader>
  87. <HomePanelBody>
  88. <h3>{t('Quick links')}:</h3>
  89. <ul>
  90. <li>
  91. <HomeLink to={`${organizationSettingsUrl}projects/`}>
  92. {t('Projects')}
  93. </HomeLink>
  94. </li>
  95. <li>
  96. <HomeLink to={`${organizationSettingsUrl}teams/`}>{t('Teams')}</HomeLink>
  97. </li>
  98. <li>
  99. <HomeLink to={`${organizationSettingsUrl}members/`}>{t('Members')}</HomeLink>
  100. </li>
  101. </ul>
  102. </HomePanelBody>
  103. </GridPanel>
  104. );
  105. const documentation = (
  106. <GridPanel>
  107. <HomePanelHeader>
  108. <ExternalHomeLinkIcon href={LINKS.DOCUMENTATION}>
  109. <HomeIconContainer color="pink300">
  110. <IconDocs size="lg" />
  111. </HomeIconContainer>
  112. {t('Documentation')}
  113. </ExternalHomeLinkIcon>
  114. </HomePanelHeader>
  115. <HomePanelBody>
  116. <h3>{t('Quick links')}:</h3>
  117. <ul>
  118. <li>
  119. <ExternalHomeLink href={LINKS.DOCUMENTATION_QUICKSTART}>
  120. {t('Quickstart Guide')}
  121. </ExternalHomeLink>
  122. </li>
  123. <li>
  124. <ExternalHomeLink href={LINKS.DOCUMENTATION_PLATFORMS}>
  125. {t('Platforms & Frameworks')}
  126. </ExternalHomeLink>
  127. </li>
  128. <li>
  129. <ExternalHomeLink href={LINKS.DOCUMENTATION_CLI}>
  130. {t('Sentry CLI')}
  131. </ExternalHomeLink>
  132. </li>
  133. </ul>
  134. </HomePanelBody>
  135. </GridPanel>
  136. );
  137. const support = (
  138. <GridPanel>
  139. <HomePanelHeader>
  140. <SupportLink icon {...supportLinkProps}>
  141. <HomeIconContainer color="purple300">
  142. <IconSupport size="lg" />
  143. </HomeIconContainer>
  144. {t('Support')}
  145. </SupportLink>
  146. </HomePanelHeader>
  147. <HomePanelBody>
  148. <h3>{t('Quick links')}:</h3>
  149. <ul>
  150. <li>
  151. <SupportLink {...supportLinkProps}>
  152. {isSelfHosted ? t('Community Forums') : t('Contact Support')}
  153. </SupportLink>
  154. </li>
  155. <li>
  156. <ExternalHomeLink href={LINKS.GITHUB_ISSUES}>
  157. {t('Sentry on GitHub')}
  158. </ExternalHomeLink>
  159. </li>
  160. <li>
  161. <ExternalHomeLink href={LINKS.SERVICE_STATUS}>
  162. {t('Service Status')}
  163. </ExternalHomeLink>
  164. </li>
  165. </ul>
  166. </HomePanelBody>
  167. </GridPanel>
  168. );
  169. const apiKeys = (
  170. <GridPanel>
  171. <HomePanelHeader>
  172. <HomeLinkIcon to={LINKS.API}>
  173. <HomeIconContainer>
  174. <IconLock size="lg" isSolid />
  175. </HomeIconContainer>
  176. {t('API Keys')}
  177. </HomeLinkIcon>
  178. </HomePanelHeader>
  179. <HomePanelBody>
  180. <h3>{t('Quick links')}:</h3>
  181. <ul>
  182. <li>
  183. <HomeLink to={LINKS.API}>{t('Auth Tokens')}</HomeLink>
  184. </li>
  185. <li>
  186. <HomeLink to={`${organizationSettingsUrl}developer-settings/`}>
  187. {t('Your Integrations')}
  188. </HomeLink>
  189. </li>
  190. <li>
  191. <ExternalHomeLink href={LINKS.DOCUMENTATION_API}>
  192. {t('Documentation')}
  193. </ExternalHomeLink>
  194. </li>
  195. </ul>
  196. </HomePanelBody>
  197. </GridPanel>
  198. );
  199. return (
  200. <SentryDocumentTitle
  201. title={organization ? `${organization.slug} Settings` : 'Settings'}
  202. >
  203. <SettingsLayout {...props}>
  204. <GridLayout>
  205. {orgSettings}
  206. {documentation}
  207. {support}
  208. </GridLayout>
  209. </SettingsLayout>
  210. </SentryDocumentTitle>
  211. );
  212. }
  213. export default SettingsIndex;
  214. const GridLayout = styled('div')`
  215. display: grid;
  216. grid-template-columns: 1fr 1fr 1fr;
  217. gap: ${space(2)};
  218. `;
  219. const GridPanel = styled(Panel)`
  220. margin-bottom: 0;
  221. `;
  222. const HomePanelHeader = styled(PanelHeader)`
  223. background: ${p => p.theme.background};
  224. font-size: ${p => p.theme.fontSizeExtraLarge};
  225. align-items: center;
  226. text-transform: unset;
  227. padding: ${space(4)};
  228. `;
  229. const HomePanelBody = styled(PanelBody)`
  230. padding: 30px;
  231. h3 {
  232. font-size: 14px;
  233. }
  234. ul {
  235. margin: 0;
  236. li {
  237. line-height: 1.6;
  238. /* Bullet color */
  239. color: ${p => p.theme.gray200};
  240. }
  241. }
  242. `;
  243. const HomeIconContainer = styled('div')<{color?: string}>`
  244. background: ${p => p.theme[p.color || 'gray300']};
  245. color: ${p => p.theme.white};
  246. width: ${HOME_ICON_SIZE}px;
  247. height: ${HOME_ICON_SIZE}px;
  248. border-radius: ${HOME_ICON_SIZE}px;
  249. display: flex;
  250. justify-content: center;
  251. align-items: center;
  252. `;
  253. const linkCss = ({theme}: {theme: Theme}) => css`
  254. color: ${theme.purple300};
  255. &:hover {
  256. color: ${theme.purple300};
  257. }
  258. `;
  259. const linkIconCss = css`
  260. overflow: hidden;
  261. width: 100%;
  262. display: grid;
  263. grid-template-rows: max-content max-content;
  264. gap: ${space(1.5)};
  265. align-items: center;
  266. justify-items: center;
  267. justify-content: center;
  268. `;
  269. const HomeLink = styled(Link)`
  270. ${linkCss}
  271. `;
  272. const ExternalHomeLink = styled(ExternalLink)`
  273. ${linkCss}
  274. `;
  275. const HomeLinkIcon = styled(HomeLink)`
  276. ${linkIconCss}
  277. `;
  278. const ExternalHomeLinkIcon = styled(ExternalLink)`
  279. ${linkIconCss}
  280. `;
  281. interface SupportLinkProps extends Omit<LinkProps, 'ref' | 'to'> {
  282. isSelfHosted: boolean;
  283. organizationSettingsUrl: string;
  284. icon?: boolean;
  285. }
  286. function SupportLink({
  287. isSelfHosted,
  288. icon,
  289. organizationSettingsUrl,
  290. ...props
  291. }: SupportLinkProps) {
  292. if (isSelfHosted) {
  293. const SelfHostedLink = icon ? ExternalHomeLinkIcon : ExternalHomeLink;
  294. return <SelfHostedLink href={LINKS.FORUM} {...props} />;
  295. }
  296. const SelfHostedLink = icon ? HomeLinkIcon : HomeLink;
  297. return <SelfHostedLink to={`${organizationSettingsUrl}support`} {...props} />;
  298. }
  299. const OrganizationName = styled('div')`
  300. line-height: 1.1em;
  301. ${p => p.theme.overflowEllipsis};
  302. `;