diff --git a/webnext/messages/en.json b/webnext/messages/en.json index 54f0163a..28f2511d 100644 --- a/webnext/messages/en.json +++ b/webnext/messages/en.json @@ -11,6 +11,7 @@ "form_error_min_len": "Minimum length of {length}", "form_error_email": "Enter valid email", "form_error_required": "Field is required", + "form_error_token": "Token is not valid", "form_label_token": "Token", "form_label_email": "email", "form_label_url": "URL", diff --git a/webnext/src/pages/ClientDownload/ClientDownloadPage.tsx b/webnext/src/pages/ClientDownload/ClientDownloadPage.tsx index d0647645..5bc93153 100644 --- a/webnext/src/pages/ClientDownload/ClientDownloadPage.tsx +++ b/webnext/src/pages/ClientDownload/ClientDownloadPage.tsx @@ -1,26 +1,95 @@ import './style.scss'; import { useNavigate } from '@tanstack/react-router'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { m } from '../../paraglide/messages'; import { Page } from '../../shared/components/Page/Page'; import { PageNavigation } from '../../shared/components/PageNavigation/PageNavigation'; import { EnrollmentStep } from '../../shared/components/Step/Step'; +import { externalLink } from '../../shared/consts'; import { Button } from '../../shared/defguard-ui/components/Button/Button'; +import { ButtonMenu } from '../../shared/defguard-ui/components/ButtonMenu/MenuButton'; import { Icon } from '../../shared/defguard-ui/components/Icon'; import type { IconKindValue } from '../../shared/defguard-ui/components/Icon/icon-types'; +import type { MenuItemsGroup } from '../../shared/defguard-ui/components/Menu/types'; import { Modal } from '../../shared/defguard-ui/components/Modal/Modal'; import { ModalControls } from '../../shared/defguard-ui/components/ModalControls/ModalControls'; import { SizedBox } from '../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../shared/defguard-ui/types'; +import { isPresent } from '../../shared/defguard-ui/utils/isPresent'; import androidIcon from './assets/android.png'; import iosIcon from './assets/ios.png'; import laptopIcon from './assets/laptop.png'; import desktopIcon from './assets/pc-tower.png'; +// open link in onClick handler +const openLink = (value: string): void => { + const anchorElement = document.createElement('a'); + anchorElement.style.display = 'none'; + anchorElement.href = value; + anchorElement.target = '_blank'; + anchorElement.rel = 'noopener noreferrer'; + document.body.appendChild(anchorElement); + anchorElement.click(); + anchorElement.remove(); +}; + +const linuxMenu: MenuItemsGroup[] = [ + { + items: [ + { + text: 'Deb X86', + onClick: () => openLink(externalLink.client.desktop.linux.deb.amd), + }, + { + text: 'Deb ARM', + onClick: () => openLink(externalLink.client.desktop.linux.deb.arm), + }, + { + text: 'RPM X86', + onClick: () => openLink(externalLink.client.desktop.linux.rpm.amd), + }, + { + text: 'RPM ARM', + onClick: () => openLink(externalLink.client.desktop.linux.rpm.arm), + }, + { + text: 'ArchLinux', + onClick: () => openLink(externalLink.client.desktop.linux.arch), + }, + ], + }, +]; + export const ClientDownloadPage = () => { - const [modalOpen, setModalOpen] = useState(false); const navigate = useNavigate(); + const [confirmModalOpen, setConfirmModalOpen] = useState(false); + const [_appleHelpModalOpen, setAppleHelpModalOpen] = useState(false); + + const appleMenu = useMemo( + (): MenuItemsGroup[] => [ + { + header: { + text: 'Apple', + onHelp: () => { + setAppleHelpModalOpen(true); + }, + }, + items: [ + { + text: 'Intel', + onClick: () => openLink(externalLink.client.desktop.macos.intel), + }, + { + text: 'ARM', + onClick: () => openLink(externalLink.client.desktop.macos.arm), + }, + ], + }, + ], + [], + ); + return ( @@ -42,6 +111,7 @@ export const ClientDownloadPage = () => { })} buttonText={m.client_download_for({ platform: 'Windows' })} buttonIconKind="windows" + directLink={externalLink.client.desktop.windows} icon={desktopIcon} /> { })} buttonText={m.client_download_for({ platform: 'Linux' })} buttonIconKind="linux" + menuItems={linuxMenu} icon={desktopIcon} /> { })} buttonText={m.client_download_for({ platform: 'Mac' })} buttonIconKind="app-store" + menuItems={appleMenu} icon={laptopIcon} /> @@ -78,6 +150,7 @@ export const ClientDownloadPage = () => { })} buttonText={m.client_download_for({ platform: 'Android' })} buttonIconKind="android" + directLink={externalLink.client.mobile.google} icon={androidIcon} /> { })} buttonText={m.client_download_for({ platform: 'iOS' })} buttonIconKind="apple" + directLink={externalLink.client.mobile.apple} icon={iosIcon} /> { - setModalOpen(false); + setConfirmModalOpen(false); }} >

{m.client_download_modal_content()}

setModalOpen(false), + onClick: () => setConfirmModalOpen(false), }} submitProps={{ text: m.controls_continue(), @@ -126,7 +200,7 @@ export const ClientDownloadPage = () => { }} nextText={m.controls_continue()} onNext={() => { - setModalOpen(true); + setConfirmModalOpen(true); }} />
@@ -140,6 +214,8 @@ const Platform = ({ subtitle, title, testId, + menuItems, + directLink, }: { icon: string; title: string; @@ -147,6 +223,8 @@ const Platform = ({ buttonText: string; buttonIconKind: IconKindValue; testId: string; + directLink?: string; + menuItems?: MenuItemsGroup[]; }) => { return (
@@ -155,15 +233,19 @@ const Platform = ({

{title}

{subtitle}

-