From aa16ccc8c6f968c156ada7ad16717a4ed7018ee5 Mon Sep 17 00:00:00 2001 From: peterschmidt85 Date: Sat, 6 Sep 2025 00:05:06 +0200 Subject: [PATCH 1/4] [UI] Reworked dstack Sky sign-up page --- .../src/App/Login/LoginByGithub/index.tsx | 479 ++++++++++++++++-- .../Login/LoginByGithub/styles.module.scss | 33 -- .../src/App/Login/LoginByGithub/styles.scss | 215 ++++++++ 3 files changed, 658 insertions(+), 69 deletions(-) delete mode 100644 frontend/src/App/Login/LoginByGithub/styles.module.scss create mode 100644 frontend/src/App/Login/LoginByGithub/styles.scss diff --git a/frontend/src/App/Login/LoginByGithub/index.tsx b/frontend/src/App/Login/LoginByGithub/index.tsx index 26438bdc60..b3cfff1cdf 100644 --- a/frontend/src/App/Login/LoginByGithub/index.tsx +++ b/frontend/src/App/Login/LoginByGithub/index.tsx @@ -1,19 +1,125 @@ import React from 'react'; +import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; +import { Container } from '@cloudscape-design/components'; +import AnchorNavigation from '@cloudscape-design/components/anchor-navigation'; +import Box from '@cloudscape-design/components/box'; +import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group'; +import Button from '@cloudscape-design/components/button'; +import ContentLayout from '@cloudscape-design/components/content-layout'; +import ExpandableSection from '@cloudscape-design/components/expandable-section'; +import Grid from '@cloudscape-design/components/grid'; +import Header from '@cloudscape-design/components/header'; +import { I18nProvider } from '@cloudscape-design/components/i18n'; +import enMessages from '@cloudscape-design/components/i18n/messages/all.en.json'; +import Icon from '@cloudscape-design/components/icon'; +import Link from '@cloudscape-design/components/link'; +import Popover from '@cloudscape-design/components/popover'; +import SpaceBetween from '@cloudscape-design/components/space-between'; +import TextContent from '@cloudscape-design/components/text-content'; +import TopNavigation from '@cloudscape-design/components/top-navigation'; +import { Mode } from '@cloudscape-design/global-styles'; -import { Box, Button, Link, NavigateLink, SpaceBetween } from 'components'; -import { UnauthorizedLayout } from 'layouts/UnauthorizedLayout'; - +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 styles from './styles.module.scss'; +import { selectSystemMode, setSystemMode } from 'App/slice'; -export const LoginByGithub: React.FC = () => { - const { t } = useTranslation(); +import { DarkThemeIcon, LightThemeIcon } from '../../../layouts/AppLayout/themeIcons'; + +import logo from '../../../assets/images/logo.svg'; +import './styles.scss'; + +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 +132,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 deleted file mode 100644 index a2d6ca8353..0000000000 --- a/frontend/src/App/Login/LoginByGithub/styles.module.scss +++ /dev/null @@ -1,33 +0,0 @@ -@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; - } -} diff --git a/frontend/src/App/Login/LoginByGithub/styles.scss b/frontend/src/App/Login/LoginByGithub/styles.scss new file mode 100644 index 0000000000..ffddd635f8 --- /dev/null +++ b/frontend/src/App/Login/LoginByGithub/styles.scss @@ -0,0 +1,215 @@ +@use '~@cloudscape-design/design-tokens' as cs; + +body { + background: cs.$color-background-layout-main; + position: relative; +} + +.custom-main-header { + display: block; + position: sticky; + top: 0; + left: 0; + right: 0; + z-index: 1000; + margin: 0; + background-color: #0f1b2a; + font-family: cs.$font-family-base; + border-bottom: solid 1px #414d5c; +} + +ul.menu-list { + display: flex; + align-items: center; + height: 39px; + margin: 0; + padding: 0 40px; + list-style: none; + font-size: 14px; + + &>li { + padding: 0; + margin: 0; + margin-right: 8px; + + >a { + padding: 0 6px; + } + + a, + div, + button, + input, + label { + float: left; + color: cs.$color-text-interactive-default; + line-height: 16px; + } + + #visual-refresh-toggle { + margin-right: 5px; + margin-top: 1px; + } + + a, + a:hover { + cursor: pointer; + text-decoration: none; + } + + &.title { + font-weight: bold; + } + } + + @media only screen and (max-width: 493px) { + padding: 4px 20px; + flex-wrap: wrap; + height: fit-content; + + .title { + flex: 1 1 100%; + margin-bottom: 8px; + } + + li { + width: min-content; + + button, + a { + text-align: left; + } + + a { + padding: 0; + } + } + } +} + +$viewport-breakpoint-s: 912px; + +body { + // Note: This token will be themed (see the product page index.tsx) + background: cs.$color-background-layout-main; +} + +.product-page-content-grid { + display: grid; + grid-template-columns: 3fr 1fr; + grid-template-rows: 0 auto 0; + margin-block-start: cs.$space-static-xxl; +} + +.on-this-page--mobile { + grid-row: 1; + grid-column: 1 / 3; + display: none; + margin-block-end: cs.$space-static-xxl; +} + +.product-page-aside { + grid-row: 2; + grid-column: 2 / 3; + padding-inline-start: calc(#{cs.$space-scaled-xxxl} /2); +} + +.product-page-content { + grid-row: 2; + grid-column: 1 / 2; + padding-inline-end: calc(#{cs.$space-scaled-xxxl} /2); + margin-bottom: 20px; +} + +.product-page-mobile { + grid-row: 3; + grid-column: 1 / 3; + display: none; +} + +.product-page-aside-sticky { + position: sticky; + inset-block-start: 40px; +} + +@media only screen and (max-width: $viewport-breakpoint-s) { + .product-page-content-grid { + grid-template-columns: 100%; + grid-template-rows: auto auto auto; + } + + .on-this-page--mobile { + display: block; + } + + .product-page-mobile { + display: block; + } + + .product-page-aside { + display: none; + } +} + +/* Simple separator */ +hr { + inline-size: 100%; + block-size: 0; + border: none; + border-block-start: 1px solid cs.$color-border-divider-default; +} + +/* High-level sections of the main content area */ +.page-section { + 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 */ +.product-details { + 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 */ +.product-cards-list { + 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; +} + +.product-cards-list-item { + flex: 1; + flex-basis: 250px; + max-inline-size: 312px; + + list-style-type: none; + margin: 0; + padding: 0; +} \ No newline at end of file From 94feb8b9e3a51830a6c45af7e6a11f3a834c7c6a Mon Sep 17 00:00:00 2001 From: peterschmidt85 Date: Sat, 6 Sep 2025 00:10:14 +0200 Subject: [PATCH 2/4] [UI] Reworked dstack Sky sign-up page linter --- frontend/src/App/Login/LoginByGithub/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/App/Login/LoginByGithub/styles.scss b/frontend/src/App/Login/LoginByGithub/styles.scss index ffddd635f8..4d53333a76 100644 --- a/frontend/src/App/Login/LoginByGithub/styles.scss +++ b/frontend/src/App/Login/LoginByGithub/styles.scss @@ -212,4 +212,4 @@ hr { list-style-type: none; margin: 0; padding: 0; -} \ No newline at end of file +} From a96792e62cec6b7db6861373da990ba672aa464c Mon Sep 17 00:00:00 2001 From: Oleg Vavilov Date: Sun, 7 Sep 2025 22:16:07 +0300 Subject: [PATCH 3/4] Refactoring after review --- .../src/App/Login/LoginByGithub/index.tsx | 70 +++++----- .../{styles.scss => styles.module.scss} | 120 ++---------------- frontend/src/components/index.ts | 3 + 3 files changed, 52 insertions(+), 141 deletions(-) rename frontend/src/App/Login/LoginByGithub/{styles.scss => styles.module.scss} (53%) diff --git a/frontend/src/App/Login/LoginByGithub/index.tsx b/frontend/src/App/Login/LoginByGithub/index.tsx index b3cfff1cdf..75e73813d9 100644 --- a/frontend/src/App/Login/LoginByGithub/index.tsx +++ b/frontend/src/App/Login/LoginByGithub/index.tsx @@ -1,25 +1,29 @@ import React from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; -import { Container } from '@cloudscape-design/components'; -import AnchorNavigation from '@cloudscape-design/components/anchor-navigation'; -import Box from '@cloudscape-design/components/box'; -import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group'; -import Button from '@cloudscape-design/components/button'; -import ContentLayout from '@cloudscape-design/components/content-layout'; -import ExpandableSection from '@cloudscape-design/components/expandable-section'; -import Grid from '@cloudscape-design/components/grid'; -import Header from '@cloudscape-design/components/header'; -import { I18nProvider } from '@cloudscape-design/components/i18n'; import enMessages from '@cloudscape-design/components/i18n/messages/all.en.json'; -import Icon from '@cloudscape-design/components/icon'; -import Link from '@cloudscape-design/components/link'; -import Popover from '@cloudscape-design/components/popover'; -import SpaceBetween from '@cloudscape-design/components/space-between'; -import TextContent from '@cloudscape-design/components/text-content'; -import TopNavigation from '@cloudscape-design/components/top-navigation'; import { Mode } from '@cloudscape-design/global-styles'; +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'; @@ -28,10 +32,8 @@ import { useGithubAuthorizeMutation } from 'services/auth'; import { selectSystemMode, setSystemMode } from 'App/slice'; -import { DarkThemeIcon, LightThemeIcon } from '../../../layouts/AppLayout/themeIcons'; - -import logo from '../../../assets/images/logo.svg'; -import './styles.scss'; +import logo from 'assets/images/logo.svg'; +import styles from './styles.module.scss'; type PortalProps = { children: React.ReactNode; @@ -106,7 +108,7 @@ function OnThisPageNavigation({ variant }: { variant: 'mobile' | 'side' }) { ); return variant === 'side' ? ( -
+
On this page @@ -190,7 +192,7 @@ function HeroHeader() { function ProductOverview() { return ( -
+
Overview
@@ -212,7 +214,7 @@ function ProductOverview() { Features -
+
+
Other versions -
    -
  • +
      +
    • @@ -347,7 +349,7 @@ function OtherVersions() {
    • -
    • +
    • @@ -423,6 +425,7 @@ export const LoginByGithub: React.FC = () => { />
+ { maxContentWidth={1040} disableOverlap={true} > -
-
+
+
-