diff --git a/frontend/src/App/Login/LoginByGithub/index.tsx b/frontend/src/App/Login/LoginByGithub/index.tsx index 26438bdc60..5843c2823a 100644 --- a/frontend/src/App/Login/LoginByGithub/index.tsx +++ b/frontend/src/App/Login/LoginByGithub/index.tsx @@ -1,19 +1,127 @@ import React from 'react'; +import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; +import enMessages from '@cloudscape-design/components/i18n/messages/all.en.json'; +import { Mode } from '@cloudscape-design/global-styles'; -import { Box, Button, Link, NavigateLink, SpaceBetween } from 'components'; -import { UnauthorizedLayout } from 'layouts/UnauthorizedLayout'; +import { + AnchorNavigation, + Box, + BreadcrumbGroup, + Button, + Container, + ContentLayout, + ExpandableSection, + Grid, + Header, + I18nProvider, + Icon, + Link, + Popover, + SpaceBetween, + TextContent, + TopNavigation, +} from 'components'; +import { DarkThemeIcon, LightThemeIcon } from 'layouts/AppLayout/themeIcons'; +import { DISCORD_URL } from 'consts'; +import { useAppDispatch, useAppSelector } from 'hooks'; import { goToUrl } from 'libs'; import { ROUTES } from 'routes'; import { useGithubAuthorizeMutation } from 'services/auth'; -import { ReactComponent as GithubIcon } from 'assets/icons/github.svg'; +import { selectSystemMode, setSystemMode } from 'App/slice'; + +import logo from 'assets/images/logo.svg'; import styles from './styles.module.scss'; -export const LoginByGithub: React.FC = () => { - const { t } = useTranslation(); +type PortalProps = { + children: React.ReactNode; +}; + +const i18nStrings = { + overflowMenuTriggerText: '', + overflowMenuTitleText: '', + overflowMenuBackIconAriaLabel: '', + overflowMenuDismissIconAriaLabel: '', +}; + +const THEME_ICON_MAP: Record = { + [Mode.Dark]: DarkThemeIcon, + [Mode.Light]: LightThemeIcon, +}; + +const GitHubIcon: React.FC = () => ( + + + + + +); + +const askAi = () => { + window.document.body.focus(); + window?.Kapa?.open(); +}; + +const HeaderPortal = ({ children }: PortalProps) => { + const domNode = document.querySelector('#header'); + if (domNode) return createPortal(children, domNode); + return null; +}; + +function OnThisPageNavigation({ variant }: { variant: 'mobile' | 'side' }) { + const anchorNavigation = ( + + ); + return variant === 'side' ? ( +
+ + On this page + + {anchorNavigation} +
+ ) : ( + + {anchorNavigation} + + ); +} + +function HeroHeader() { const [githubAuthorize, { isLoading }] = useGithubAuthorizeMutation(); const signInClick = () => { @@ -26,37 +134,338 @@ export const LoginByGithub: React.FC = () => { }; return ( - - -
- {t('auth.sign_in_to_dstack')} - - - -
- - - By clicking you agree to{' '} - - Terms of service - {' '} - and{' '} - - Privacy policy + + +
+ Welcome to dstack Sky + + Enjoy the full power of dstack without the hassle of hosting it yourself or managing + your own infrastructure.
+ Sign up for dstack Sky to use the cheapest GPUs from our marketplace or connect it to + your own cloud accounts. +
+ + By clicking Sign up you agree to the{' '} + + Terms + {' '} + and{' '} + + Privacy policy + + +
+ + + + + + + No credit card required + + + +
+ + + + + + +
+
+
+ ); +} + +function ProductOverview() { + return ( +
+
+ Overview +
+ +
+ + dstack is an open-source container orchestrator that lets ML teams easily manage + clusters, volumes, dev environments, training, and inference. Its container-native interface boosts + productivity, maximizes GPU efficiency, and lowers costs. + + + dstack Sky adds a managed service, letting you use the cheapest GPUs from our + marketplace or connect your own cloud accounts. + +
+ +
+ + Features + + +
+
+
+ + Open-source - - - {t('auth.login_by_token')} - - -
+ +
dstack Sky
+ +
Your cloud accounts
+
+ +
+
+ +
+ +
+ SSH fleets{' '} + + + + + +
+
+ +
+
+ +
+ +
+ GPU marketplace{' '} + + + + + +
+
+
+ +
+ +
+ Gateway{' '} + + + + + +
+
Configure your own domain
+
Pre-configured *.sky.dstack.ai
+ +
Pricing
+
Free
+
Pay only for marketplace GPU
+ +
+
Self-hosted
+
Hosted by dstack
+ +
+ +
+
+ Highlights +
+ +
    +
  • Use your own cloud accounts or access the cheapest GPUs from our marketplace.
  • +
  • Create dev environments, run training tasks, and deploy inference services.
  • +
  • Manage volumes and fleets.
  • +
  • Manage multiple projects and teams.
  • +
+
+
+ +
+
+ Documentation +
+ + + Want to learn more about dstack? Check out the{' '} + + documentation + + + +
+ + + ); +} + +function OtherVersions() { + return ( +
+ + Other versions - +
    +
  • + + + + Open-source + Self-hosted + + Sigle sign-on, advanced governance controls, and dedicated support. + + + +
  • +
  • + + + + dstack Enterprise + Self-hosted + + Sigle sign-on, advanced governance controls, and dedicated support. + + + +
  • +
+
+ ); +} + +export const LoginByGithub: React.FC = () => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const systemMode = useAppSelector(selectSystemMode) ?? ''; + const ThemeIcon = THEME_ICON_MAP[systemMode]; + + const onChangeSystemModeToggle = (event: React.MouseEvent) => { + event.preventDefault(); + switch (systemMode) { + case Mode.Light: + dispatch(setSystemMode(Mode.Dark)); + return; + default: + dispatch(setSystemMode(Mode.Light)); + } + }; + + return ( + <> + +
+ goToUrl('https://dstack.ai/docs/', true), + }, + { + type: 'button', + text: t('common.discord'), + external: true, + onClick: () => goToUrl(DISCORD_URL, true), + }, + { + href: 'theme-button', + type: 'button', + iconSvg: , + onClick: onChangeSystemModeToggle, + }, + { + type: 'button', + iconName: 'gen-ai', + text: t('common.ask_ai'), + title: t('common.ask_ai'), + onClick: askAi, + }, + ]} + /> +
+
+ + + + } + headerVariant="high-contrast" + header={} + defaultPadding={true} + maxContentWidth={1040} + disableOverlap={true} + > +
+
+ +
+ + + +
+ + +
+
+
+
+ ); }; diff --git a/frontend/src/App/Login/LoginByGithub/styles.module.scss b/frontend/src/App/Login/LoginByGithub/styles.module.scss index a2d6ca8353..9904b2bd4d 100644 --- a/frontend/src/App/Login/LoginByGithub/styles.module.scss +++ b/frontend/src/App/Login/LoginByGithub/styles.module.scss @@ -1,33 +1,121 @@ -@use '@cloudscape-design/design-tokens/index' as awsui; - -.signIn { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 30px; - padding-top: 120px; - - button { - //max-width: 270px; - background-color: rgb(0, 0, 0) !important; - border-color: rgb(0, 0, 0) !important; - color: awsui.$color-text-interactive-inverted-hover !important; - - &:hover { - background-color: rgba(0, 0, 0, .85) !important; - border-color: rgba(0, 0, 0, .85) !important; - } - - .loginButtonInner { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - } - } - - .links { - max-width: 270px; - } +@use '~@cloudscape-design/design-tokens' as cs; + +body { + background: cs.$color-background-layout-main; + position: relative; +} + +$viewport-breakpoint-s: 912px; + +body { + // Note: This token will be themed (see the product page index.tsx) + background: cs.$color-background-layout-main; +} + +.productPageContentGrid { + display: grid; + grid-template-columns: 3fr 1fr; + grid-template-rows: 0 auto 0; + margin-block-start: cs.$space-static-xxl; +} + +.onThisPageMobile { + grid-row: 1; + grid-column: 1 / 3; + display: none; + margin-block-end: cs.$space-static-xxl; +} + +.productPageAside { + grid-row: 2; + grid-column: 2 / 3; + padding-inline-start: calc(#{cs.$space-scaled-xxxl} /2); +} + +.productPageContent { + grid-row: 2; + grid-column: 1 / 2; + padding-inline-end: calc(#{cs.$space-scaled-xxxl} /2); + margin-bottom: 20px; +} + + +.productPageAsideSticky { + position: sticky; + inset-block-start: 40px; +} + +@media only screen and (max-width: $viewport-breakpoint-s) { + .productPageContentGrid { + grid-template-columns: 100%; + grid-template-rows: auto auto auto; + } + + .onThisPageMobile { + display: block; + } + + .productPageMobile { + display: block; + } + + .productPageAside { + display: none; + } +} + + +/* High-level sections of the main content area */ +.pageSection { + padding-block-end: cs.$space-static-xxxl; + margin-block-end: cs.$space-static-xxl; + border-bottom: 1px solid cs.$color-border-divider-default; + + &:last-child { + border: none; + margin-block-end: 0; + } +} + +/* Product details list containing keys and values */ +.productDetails { + display: grid; + grid-template-columns: 30% 35% 35%; + margin: 0; + padding: 0; + + dt { + color: cs.$color-text-body-secondary; + font-weight: bold; + } + + dt, + dd { + margin: 0; + padding: 0; + padding-block: cs.$space-scaled-xs; + border-block-end: 1px solid cs.$color-border-divider-default; + } +} + +/* List of product cards */ +.productCardsList { + display: flex; + flex-wrap: wrap; + column-gap: cs.$space-scaled-l; + row-gap: cs.$space-scaled-l; + + list-style-type: none; + margin: 0; + padding: 0; +} + +.productCardsListItem { + flex: 1; + flex-basis: 250px; + max-inline-size: 312px; + + list-style-type: none; + margin: 0; + padding: 0; } diff --git a/frontend/src/components/index.ts b/frontend/src/components/index.ts index f68297f1d2..e24d6bd3ee 100644 --- a/frontend/src/components/index.ts +++ b/frontend/src/components/index.ts @@ -56,6 +56,9 @@ export { default as PropertyFilter } from '@cloudscape-design/components/propert export type { PropertyFilterProps } from '@cloudscape-design/components/property-filter'; export type { LineChartProps } from '@cloudscape-design/components/line-chart/interfaces'; export type { ModalProps } from '@cloudscape-design/components/modal'; +export { default as AnchorNavigation } from '@cloudscape-design/components/anchor-navigation'; +export { default as ExpandableSection } from '@cloudscape-design/components/expandable-section'; +export { I18nProvider } from '@cloudscape-design/components/i18n'; // custom components export { NavigateLink } from './NavigateLink';