Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 12 additions & 38 deletions webnext/src/pages/ClientDownload/ClientDownloadPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import './style.scss';
import { useLoaderData, useNavigate } from '@tanstack/react-router';
import { useQuery } from '@tanstack/react-query';
import { useNavigate } from '@tanstack/react-router';
import { useMemo, useState } from 'react';
import { m } from '../../paraglide/messages';
import { AppleHelpModal } from '../../shared/components/AppleHelpModal/AppleHelpModal';
import { Page } from '../../shared/components/Page/Page';
import { PageNavigation } from '../../shared/components/PageNavigation/PageNavigation';
import { EnrollmentStep } from '../../shared/components/Step/Step';
Expand All @@ -16,19 +18,16 @@ import { ModalControls } from '../../shared/defguard-ui/components/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 { getClientArtifactsQueryOptions } from '../../shared/query/queryOptions';
import { openVirtualLink } from '../../shared/utils/openVirtualLink';
import androidIcon from './assets/android.png';
import apple_video_src from './assets/apple_hardware_help.mp4';
import iosIcon from './assets/ios.png';
import laptopIcon from './assets/laptop.png';
import desktopIcon from './assets/pc-tower.png';

// open link in onClick handler

export const ClientDownloadPage = () => {
const pageData = useLoaderData({
from: '/download',
});
const { data: pageData } = useQuery(getClientArtifactsQueryOptions);

const navigate = useNavigate();

const [confirmModalOpen, setConfirmModalOpen] = useState(false);
Expand Down Expand Up @@ -163,6 +162,12 @@ export const ClientDownloadPage = () => {
icon={iosIcon}
/>
</div>
<AppleHelpModal
isOpen={appleHelpModalOpen}
onClose={() => {
setAppleHelpModalOpen(false);
}}
/>
<Modal
title={m.client_download_modal_title()}
size="small"
Expand All @@ -188,37 +193,6 @@ export const ClientDownloadPage = () => {
}}
/>
</Modal>
<Modal
title={m.client_download_apple_help_title()}
size="small"
isOpen={appleHelpModalOpen}
onClose={() => {
setAppleHelpModalOpen(false);
}}
>
<p>{m.client_download_apple_help_content_1()}</p>
<SizedBox height={ThemeSpacing.Xl} />
<video
controls
playsInline
preload="metadata"
src={apple_video_src}
style={{
width: '100%',
height: 'auto',
}}
/>
<SizedBox height={ThemeSpacing.Xl} />
<p>{m.client_download_apple_help_content_2()}</p>
<ModalControls
submitProps={{
text: m.controls_got_it(),
onClick: () => {
setAppleHelpModalOpen(false);
},
}}
/>
</Modal>
<PageNavigation
backText={m.controls_back()}
onBack={() => {
Expand Down
1 change: 1 addition & 0 deletions webnext/src/pages/ClientDownload/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
& > img {
width: 60px;
height: 60px;
user-select: none;
}

.btn {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import './style.scss';

import { useQuery } from '@tanstack/react-query';
import { useLoaderData } from '@tanstack/react-router';
import { capitalCase } from 'change-case';
import { QRCodeCanvas } from 'qrcode.react';
import { useMemo, useState } from 'react';
import { m } from '../../../paraglide/messages';
import { AppleHelpModal } from '../../../shared/components/AppleHelpModal/AppleHelpModal';
import { ContactFooter } from '../../../shared/components/ContactFooter/ContactFooter';
import { ContainerWithIcon } from '../../../shared/components/ContainerWithIcon/ContainerWithIcon';
import { Page } from '../../../shared/components/Page/Page';
Expand All @@ -19,13 +21,17 @@ import { Icon } from '../../../shared/defguard-ui/components/Icon';
import type { MenuItemsGroup } from '../../../shared/defguard-ui/components/Menu/types';
import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox';
import { ThemeSpacing } from '../../../shared/defguard-ui/types';
import { getClientArtifactsQueryOptions } from '../../../shared/query/queryOptions';
import { openVirtualLink } from '../../../shared/utils/openVirtualLink';

export const ConfigureClientPage = () => {
const pageData = useLoaderData({
from: '/client-setup',
});
const clientLinks = pageData.clientDownload;

const { data: clientLinks } = useQuery(getClientArtifactsQueryOptions);

const [appleHelpModalOpen, setAppleHelpModalOpen] = useState(false);

const clientDownloadMenu = useMemo(
(): MenuItemsGroup[] => [
Expand All @@ -41,6 +47,7 @@ export const ConfigureClientPage = () => {
{
header: {
text: m.client_download_apple_help_header(),
onHelp: () => setAppleHelpModalOpen(true),
},
items: [
{
Expand Down Expand Up @@ -214,6 +221,12 @@ export const ConfigureClientPage = () => {
<p className="finish">{m.client_setup_footer_extra()}</p>
<ContactFooter email={pageData.enrollmentData.admin.email} />
</footer>
<AppleHelpModal
isOpen={appleHelpModalOpen}
onClose={() => {
setAppleHelpModalOpen(false);
}}
/>
</Page>
);
};
7 changes: 4 additions & 3 deletions webnext/src/routes/client-setup.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createFileRoute, redirect } from '@tanstack/react-router';
import z from 'zod';
import { queryClient } from '../app/query';
import { ConfigureClientPage } from '../pages/enrollment/ConfigureClient/ConfigureClientPage';
import { api } from '../shared/api/api';
import type { EnrollmentStartResponse } from '../shared/api/types';
import { updateServiceApi } from '../shared/api/update-service';
import { isPresent } from '../shared/defguard-ui/utils/isPresent';
import { useEnrollmentStore } from '../shared/hooks/useEnrollmentStore';
import { getClientArtifactsQueryOptions } from '../shared/query/queryOptions';

const schema = z.object({
code: z.string().trim().optional(),
Expand Down Expand Up @@ -39,7 +40,8 @@ export const Route = createFileRoute('/client-setup')({
};
},
loader: async ({ context: { openid } }) => {
const clientDownload = await updateServiceApi.getClientArtifacts().catch(() => null);
void queryClient.ensureQueryData(getClientArtifactsQueryOptions);

if (openid) {
try {
const openIdResponse = await api.openId.enrollmentCallback.callbackFn({
Expand Down Expand Up @@ -70,7 +72,6 @@ export const Route = createFileRoute('/client-setup')({
}
const state = useEnrollmentStore.getState();
return {
clientDownload,
token: state.token as string,
enrollmentData: state.enrollmentData as EnrollmentStartResponse,
};
Expand Down
10 changes: 3 additions & 7 deletions webnext/src/routes/download.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { createFileRoute } from '@tanstack/react-router';
import { queryClient } from '../app/query';
import { ClientDownloadPage } from '../pages/ClientDownload/ClientDownloadPage';
import { updateServiceApi } from '../shared/api/update-service';
import { getClientArtifactsQueryOptions } from '../shared/query/queryOptions';

export const Route = createFileRoute('/download')({
component: ClientDownloadPage,
loader: async () => {
const clientVersionData = await updateServiceApi
.getClientArtifacts()
.catch(() => null);
return clientVersionData;
},
loader: () => queryClient.ensureQueryData(getClientArtifactsQueryOptions),
});
29 changes: 28 additions & 1 deletion webnext/src/shared/api/update-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import qs from 'qs';

const baseUrl = import.meta.env.VITE_UPDATE_BASE_URL as string | undefined;

const clientDownloadFallback = 'https://defguard.net/download';

const client = axios.create({
baseURL: baseUrl ?? 'https://update-service-dev.defguard.net/api',
headers: { 'Content-Type': 'application/json' },
Expand Down Expand Up @@ -33,7 +35,32 @@ const updateServiceApi = {
source: 'enrollment',
},
})
.then((response) => response.data),
.then((response) => {
const { data } = response;
const res: ClientVersionCheck = {
deb_amd64: data.deb_amd64 ?? clientDownloadFallback,
deb_arm64: data.deb_arm64 ?? clientDownloadFallback,
macos_amd64: data.macos_amd64 ?? clientDownloadFallback,
macos_arm64: data.macos_arm64 ?? clientDownloadFallback,
rpm_amd64: data.rpm_amd64 ?? clientDownloadFallback,
rpm_arm64: data.rpm_arm64 ?? clientDownloadFallback,
windows_amd64: data.windows_amd64 ?? clientDownloadFallback,
};
return res;
})
.catch((e) => {
console.error(e);
const fallback: ClientVersionCheck = {
deb_amd64: clientDownloadFallback,
deb_arm64: clientDownloadFallback,
macos_amd64: clientDownloadFallback,
macos_arm64: clientDownloadFallback,
rpm_amd64: clientDownloadFallback,
rpm_arm64: clientDownloadFallback,
windows_amd64: clientDownloadFallback,
};
return fallback;
}),
} as const;

export { updateServiceApi };
43 changes: 43 additions & 0 deletions webnext/src/shared/components/AppleHelpModal/AppleHelpModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { m } from '../../../paraglide/messages';
import { Modal } from '../../defguard-ui/components/Modal/Modal';
import { ModalControls } from '../../defguard-ui/components/ModalControls/ModalControls';
import { SizedBox } from '../../defguard-ui/components/SizedBox/SizedBox';
import { ThemeSpacing } from '../../defguard-ui/types';
import apple_video_src from './assets/apple_hardware_help.mp4';

type Props = {
isOpen: boolean;
onClose: () => void;
};

export const AppleHelpModal = ({ isOpen, onClose }: Props) => {
return (
<Modal
title={m.client_download_apple_help_title()}
size="small"
isOpen={isOpen}
onClose={onClose}
>
<p>{m.client_download_apple_help_content_1()}</p>
<SizedBox height={ThemeSpacing.Xl} />
<video
controls
playsInline
preload="metadata"
src={apple_video_src}
style={{
width: '100%',
height: 'auto',
}}
/>
<SizedBox height={ThemeSpacing.Xl} />
<p>{m.client_download_apple_help_content_2()}</p>
<ModalControls
submitProps={{
text: m.controls_got_it(),
onClick: onClose,
}}
/>
</Modal>
);
};
11 changes: 9 additions & 2 deletions webnext/src/shared/defguard-ui/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ import { MenuHeader } from './components/MenuHeader';
import { MenuSpacer } from './components/MenuSpacer';
import type { MenuProps } from './types';

export const Menu = ({ itemGroups, ref, className, onClose, ...props }: MenuProps) => {
export const Menu = ({
itemGroups,
ref,
className,
onClose,
testId,
...props
}: MenuProps) => {
return (
<div className={clsx('menu', className)} ref={ref} {...props}>
<div className={clsx('menu', className)} ref={ref} data-testid={testId} {...props}>
{itemGroups.map((group, groupIndex) => (
<Fragment key={group.header?.text ?? groupIndex}>
{isPresent(group.header) && <MenuHeader {...group.header} onClose={onClose} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { isPresent } from '../../../utils/isPresent';
import { InteractionBox } from '../../InteractionBox/InteractionBox';
import type { MenuHeaderProps } from '../types';

export const MenuHeader = ({ text, onHelp, onClose }: MenuHeaderProps) => {
export const MenuHeader = ({ text, testId, onHelp, onClose }: MenuHeaderProps) => {
return (
<div
className={clsx('menu-header', {
'with-help': isPresent(onHelp),
})}
data-testid={testId}
>
<p className="group-title">{text}</p>
{isPresent(onHelp) && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export const MenuItem = ({
text,
icon,
items,
testId,
variant,
onClick,
onClose,
variant,
}: MenuItemProps) => {
const hasItems = isPresent(items) && items.length > 0;
const hasIcon = isPresent(icon);
Expand All @@ -25,6 +26,7 @@ export const MenuItem = ({
'grid-full': hasIcon && hasItems,
nested: hasItems,
})}
data-testid={testId}
onClick={() => {
if (!disabled) {
onClick?.();
Expand Down
7 changes: 5 additions & 2 deletions webnext/src/shared/defguard-ui/components/Menu/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { IconKindValue } from '../Icon/icon-types';

export interface MenuProps extends HTMLAttributes<HTMLDivElement> {
itemGroups: MenuItemsGroup[];
onClose?: () => void;
ref?: Ref<HTMLDivElement>;
testId?: string;
onClose?: () => void;
}

export interface MenuItemsGroup {
Expand All @@ -15,16 +16,18 @@ export interface MenuItemsGroup {
export interface MenuItemProps {
text: string;
variant?: 'default' | 'danger';
disabled?: boolean;
icon?: IconKindValue;
items?: MenuItemProps[];
disabled?: boolean;
testId?: string;
onClick?: () => void;
onClose?: () => void;
}

export interface MenuHeaderProps {
text: string;
tooltip?: string;
testId?: string;
onClose?: () => void;
onHelp?: () => void;
}
11 changes: 11 additions & 0 deletions webnext/src/shared/query/queryOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { queryOptions } from '@tanstack/react-query';
import { updateServiceApi } from '../api/update-service';

export const getClientArtifactsQueryOptions = queryOptions({
queryFn: updateServiceApi.getClientArtifacts,
queryKey: ['update-service', 'artifacts'],
staleTime: 180 * 1000,
refetchOnWindowFocus: false,
refetchOnMount: true,
refetchOnReconnect: true,
});